1/*
2 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
3 * Copyright (c) 2012 Hewlett-Packard Development Company, L.P.
4 * Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this program; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24#include "qquickwebview_p.h"
25
26#include "CoordinatedLayerTreeHostProxy.h"
27#include "DownloadProxy.h"
28#include "DrawingAreaProxyImpl.h"
29#include "PageViewportControllerClientQt.h"
30#include "QtDialogRunner.h"
31#include "QtDownloadManager.h"
32#include "QtWebContext.h"
33#include "QtWebError.h"
34#include "QtWebIconDatabaseClient.h"
35#include "QtWebPageEventHandler.h"
36#include "QtWebPagePolicyClient.h"
37#include "WebBackForwardList.h"
38#include "WebContext.h"
39#include "WebFindOptions.h"
40#if ENABLE(INSPECTOR_SERVER)
41#include "WebInspectorProxy.h"
42#include "WebInspectorServer.h"
43#endif
44#if ENABLE(FULLSCREEN_API)
45#include "WebFullScreenManagerProxy.h"
46#endif
47#include "WebPageGroup.h"
48#include "WebPreferences.h"
49#include "qglobal.h"
50#include "qquicknetworkreply_p.h"
51#include "qquicknetworkrequest_p.h"
52#include "qquickwebpage_p_p.h"
53#include "qquickwebview_p_p.h"
54#include "qwebdownloaditem_p_p.h"
55#include "qwebiconimageprovider_p.h"
56#include "qwebkittest_p.h"
57#include "qwebloadrequest_p.h"
58#include "qwebnavigationhistory_p.h"
59#include "qwebnavigationhistory_p_p.h"
60#include "qwebpreferences_p.h"
61#include "qwebpreferences_p_p.h"
62#include <JavaScriptCore/InitializeThreading.h>
63#include <JavaScriptCore/JSBase.h>
64#include <JavaScriptCore/JSRetainPtr.h>
65#include <QDateTime>
66#include <QMap>
67#include <QtCore/QFile>
68#include <QtQml/QJSValue>
69#include <QtQuick/QQuickView>
70#include <WKNumber.h>
71#include <WKOpenPanelResultListener.h>
72#include <WKPageGroup.h>
73#include <WKPreferences.h>
74#include <WKSerializedScriptValue.h>
75#include <WKString.h>
76#include <WKStringQt.h>
77#include <WKURLQt.h>
78#include <WebCore/CoordinatedGraphicsScene.h>
79#include <WebCore/IntPoint.h>
80#include <WebCore/IntRect.h>
81#include <limits>
82#include <wtf/Assertions.h>
83#include <wtf/MainThread.h>
84#include <wtf/Vector.h>
85#include <wtf/text/WTFString.h>
86
87using namespace WebCore;
88using namespace WebKit;
89
90static bool s_flickableViewportEnabled = true;
91static const int kAxisLockSampleCount = 5;
92static const qreal kAxisLockVelocityThreshold = 300;
93static const qreal kAxisLockVelocityDirectionThreshold = 50;
94
95typedef QMap<WKPageRef, QQuickWebViewPrivate*> PageToViewMap;
96Q_GLOBAL_STATIC(PageToViewMap, pageToView)
97
98static inline QQuickWebViewPrivate* toQQuickWebViewPrivate(const void* clientInfo)
99{
100    ASSERT(clientInfo);
101    return reinterpret_cast<QQuickWebViewPrivate*>(const_cast<void*>(clientInfo));
102}
103
104struct JSCallbackClosure {
105    QPointer<QObject> receiver;
106    QByteArray method;
107    QJSValue value;
108};
109
110static inline QString toQString(JSStringRef string)
111{
112    return QString(reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(string)), JSStringGetLength(string));
113}
114
115static inline QJSValue toQJSValue(JSStringRef string)
116{
117    return QJSValue(toQString(string));
118}
119
120static QJSValue buildQJSValue(QJSEngine* engine, JSGlobalContextRef context, JSValueRef value, int depth)
121{
122    QJSValue var;
123    JSValueRef exception = 0;
124
125    if (depth > 10)
126        return var;
127
128    switch (JSValueGetType(context, value)) {
129    case kJSTypeBoolean:
130        var = QJSValue(JSValueToBoolean(context, value));
131        break;
132    case kJSTypeNumber:
133        {
134            double number = JSValueToNumber(context, value, &exception);
135            if (!exception)
136                var = QJSValue(number);
137        }
138        break;
139    case kJSTypeString:
140        {
141            JSRetainPtr<JSStringRef> string = JSValueToStringCopy(context, value, &exception);
142            if (!exception)
143                var = toQJSValue(string.get());
144        }
145        break;
146    case kJSTypeObject:
147        {
148            JSObjectRef obj = JSValueToObject(context, value, &exception);
149
150            JSPropertyNameArrayRef names = JSObjectCopyPropertyNames(context, obj);
151            size_t length = JSPropertyNameArrayGetCount(names);
152
153            var = engine->newObject();
154
155            for (size_t i = 0; i < length; ++i) {
156                JSRetainPtr<JSStringRef> name = JSPropertyNameArrayGetNameAtIndex(names, i);
157                JSValueRef property = JSObjectGetProperty(context, obj, name.get(), &exception);
158
159                if (!exception) {
160                    QJSValue value = buildQJSValue(engine, context, property, depth + 1);
161                    var.setProperty(toQString(name.get()), value);
162                }
163            }
164        }
165        break;
166    }
167    return var;
168}
169
170static void javaScriptCallback(WKSerializedScriptValueRef valueRef, WKErrorRef, void* data)
171{
172    JSCallbackClosure* closure = reinterpret_cast<JSCallbackClosure*>(data);
173
174    if (closure->method.size())
175        QMetaObject::invokeMethod(closure->receiver, closure->method);
176    else {
177        QJSValue function = closure->value;
178
179        // If a callable function is supplied, we build a JavaScript value accessible
180        // in the QML engine, and calls the function with that.
181        if (function.isCallable()) {
182            QJSValue var;
183            if (valueRef) {
184                // FIXME: Slow but OK for now.
185                JSGlobalContextRef context = JSGlobalContextCreate(0);
186
187                JSValueRef exception = 0;
188                JSValueRef value = WKSerializedScriptValueDeserialize(valueRef, context, &exception);
189                var = buildQJSValue(function.engine(), context, value, /* depth */ 0);
190
191                JSGlobalContextRelease(context);
192            }
193
194            QList<QJSValue> args;
195            args.append(var);
196            function.call(args);
197        }
198    }
199
200    delete closure;
201}
202
203static QQuickWebViewPrivate* createPrivateObject(QQuickWebView* publicObject)
204{
205    if (s_flickableViewportEnabled)
206        return new QQuickWebViewFlickablePrivate(publicObject);
207    return new QQuickWebViewLegacyPrivate(publicObject);
208}
209
210QQuickWebViewPrivate* QQuickWebViewPrivate::get(WKPageRef page)
211{
212    return pageToView()->value(page);
213}
214
215QQuickWebViewPrivate::FlickableAxisLocker::FlickableAxisLocker()
216    : m_allowedDirection(QQuickFlickable::AutoFlickDirection)
217    , m_time(0), m_sampleCount(0)
218{
219}
220
221QVector2D QQuickWebViewPrivate::FlickableAxisLocker::touchVelocity(const QTouchEvent* event)
222{
223    static bool touchVelocityAvailable = event->device()->capabilities().testFlag(QTouchDevice::Velocity);
224    const QTouchEvent::TouchPoint& touchPoint = event->touchPoints().first();
225
226    if (touchVelocityAvailable)
227        return touchPoint.velocity();
228
229    const QLineF movementLine(touchPoint.pos(), m_initialPosition);
230    const ulong elapsed = event->timestamp() - m_time;
231
232    if (!elapsed)
233        return QVector2D(0, 0);
234
235    // Calculate an approximate velocity vector in the unit of pixel / second.
236    return QVector2D(1000 * movementLine.dx() / elapsed, 1000 * movementLine.dy() / elapsed);
237}
238
239void QQuickWebViewPrivate::FlickableAxisLocker::update(const QTouchEvent* event)
240{
241    ASSERT(event->touchPoints().size() == 1);
242    const QTouchEvent::TouchPoint& touchPoint = event->touchPoints().first();
243
244    ++m_sampleCount;
245
246    if (m_sampleCount == 1) {
247        m_initialPosition = touchPoint.pos();
248        m_time = event->timestamp();
249        return;
250    }
251
252    if (m_sampleCount > kAxisLockSampleCount
253            || m_allowedDirection == QQuickFlickable::HorizontalFlick
254            || m_allowedDirection == QQuickFlickable::VerticalFlick)
255        return;
256
257    QVector2D velocity = touchVelocity(event);
258
259    qreal directionIndicator = qAbs(velocity.x()) - qAbs(velocity.y());
260
261    if (velocity.length() > kAxisLockVelocityThreshold && qAbs(directionIndicator) > kAxisLockVelocityDirectionThreshold)
262        m_allowedDirection = (directionIndicator > 0) ? QQuickFlickable::HorizontalFlick : QQuickFlickable::VerticalFlick;
263}
264
265void QQuickWebViewPrivate::FlickableAxisLocker::setReferencePosition(const QPointF& position)
266{
267    m_lockReferencePosition = position;
268}
269
270void QQuickWebViewPrivate::FlickableAxisLocker::reset()
271{
272    m_allowedDirection = QQuickFlickable::AutoFlickDirection;
273    m_sampleCount = 0;
274}
275
276QPointF QQuickWebViewPrivate::FlickableAxisLocker::adjust(const QPointF& position)
277{
278    if (m_allowedDirection == QQuickFlickable::HorizontalFlick)
279        return QPointF(position.x(), m_lockReferencePosition.y());
280
281    if (m_allowedDirection == QQuickFlickable::VerticalFlick)
282        return QPointF(m_lockReferencePosition.x(), position.y());
283
284    return position;
285}
286
287QQuickWebViewPrivate::QQuickWebViewPrivate(QQuickWebView* viewport)
288    : q_ptr(viewport)
289    , experimental(new QQuickWebViewExperimental(viewport, this))
290    , context(0)
291    , alertDialog(0)
292    , confirmDialog(0)
293    , promptDialog(0)
294    , authenticationDialog(0)
295    , certificateVerificationDialog(0)
296    , itemSelector(0)
297    , proxyAuthenticationDialog(0)
298    , filePicker(0)
299    , databaseQuotaDialog(0)
300    , colorChooser(0)
301    , m_betweenLoadCommitAndFirstFrame(false)
302    , m_useDefaultContentItemSize(true)
303    , m_navigatorQtObjectEnabled(false)
304    , m_renderToOffscreenBuffer(false)
305    , m_allowAnyHTTPSCertificateForLocalHost(false)
306    , m_loadProgress(0)
307{
308    viewport->setClip(true);
309    viewport->setPixelAligned(true);
310    QObject::connect(viewport, SIGNAL(visibleChanged()), viewport, SLOT(_q_onVisibleChanged()));
311    QObject::connect(viewport, SIGNAL(urlChanged()), viewport, SLOT(_q_onUrlChanged()));
312    pageView.reset(new QQuickWebPage(viewport));
313}
314
315QQuickWebViewPrivate::~QQuickWebViewPrivate()
316{
317    webPageProxy->close();
318    pageToView()->remove(webPage.get());
319}
320
321// Note: we delay this initialization to make sure that QQuickWebView has its d-ptr in-place.
322void QQuickWebViewPrivate::initialize(WKContextRef contextRef, WKPageGroupRef pageGroupRef)
323{
324    pageGroup = pageGroupRef;
325    if (!pageGroup)
326        pageGroup = adoptWK(WKPageGroupCreateWithIdentifier(0));
327
328    context = contextRef ? QtWebContext::create(contextRef) : QtWebContext::defaultContext();
329    webPageProxy = toImpl(context->context())->createWebPage(&pageClient, toImpl(pageGroup.get()));
330    webPage = toAPI(webPageProxy.get());
331    pageToView()->insert(webPage.get(), this);
332
333    webPageProxy->setUseFixedLayout(s_flickableViewportEnabled);
334#if ENABLE(FULLSCREEN_API)
335    webPageProxy->fullScreenManager()->setWebView(q_ptr);
336#endif
337
338    pageEventHandler.reset(new QtWebPageEventHandler(webPage.get(), pageView.data(), q_ptr));
339
340    {
341        WKPageFindClient findClient;
342        memset(&findClient, 0, sizeof(WKPageFindClient));
343        findClient.version = kWKPageFindClientCurrentVersion;
344        findClient.clientInfo = this;
345        findClient.didFindString = didFindString;
346        findClient.didFailToFindString = didFailToFindString;
347        WKPageSetPageFindClient(webPage.get(), &findClient);
348    }
349
350    {
351        WKPageLoaderClient loadClient;
352        memset(&loadClient, 0, sizeof(WKPageLoaderClient));
353        loadClient.version = kWKPageLoaderClientCurrentVersion;
354        loadClient.clientInfo = this;
355        loadClient.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
356        loadClient.didReceiveServerRedirectForProvisionalLoadForFrame = didReceiveServerRedirectForProvisionalLoadForFrame;
357        loadClient.didFailProvisionalLoadWithErrorForFrame = didFailLoad;
358        loadClient.didCommitLoadForFrame = didCommitLoadForFrame;
359        loadClient.didFinishLoadForFrame = didFinishLoadForFrame;
360        loadClient.didFailLoadWithErrorForFrame = didFailLoad;
361        loadClient.didSameDocumentNavigationForFrame = didSameDocumentNavigationForFrame;
362        loadClient.didReceiveTitleForFrame = didReceiveTitleForFrame;
363        loadClient.didStartProgress = didStartProgress;
364        loadClient.didChangeProgress = didChangeProgress;
365        loadClient.didFinishProgress = didFinishProgress;
366        loadClient.didChangeBackForwardList = didChangeBackForwardList;
367        WKPageSetPageLoaderClient(webPage.get(), &loadClient);
368    }
369
370    pagePolicyClient.reset(new QtWebPagePolicyClient(webPage.get(), q_ptr));
371    pageUIClient.reset(new QtWebPageUIClient(webPage.get(), q_ptr));
372    navigationHistory = adoptPtr(QWebNavigationHistoryPrivate::createHistory(webPage.get()));
373
374    QtWebIconDatabaseClient* iconDatabase = context->iconDatabase();
375    QObject::connect(iconDatabase, SIGNAL(iconChangedForPageURL(QString)), q_ptr, SLOT(_q_onIconChangedForPageURL(QString)));
376
377    // Any page setting should preferrable be set before creating the page.
378    WKPreferencesRef preferencesRef = WKPageGroupGetPreferences(pageGroup.get());
379    WKPreferencesSetAcceleratedCompositingEnabled(preferencesRef, true);
380    bool showDebugVisuals = qgetenv("WEBKIT_SHOW_COMPOSITING_DEBUG_VISUALS") == "1";
381    WKPreferencesSetCompositingBordersVisible(preferencesRef, showDebugVisuals);
382    WKPreferencesSetCompositingRepaintCountersVisible(preferencesRef, showDebugVisuals);
383    WKPreferencesSetFrameFlatteningEnabled(preferencesRef, true);
384    WKPreferencesSetWebGLEnabled(preferencesRef, true);
385    webPageProxy->pageGroup()->preferences()->setForceCompositingMode(true);
386
387    pageClient.initialize(q_ptr, pageEventHandler.data(), &undoController);
388    webPageProxy->initializeWebPage();
389    webPageProxy->registerApplicationScheme(ASCIILiteral("qrc"));
390
391    q_ptr->setAcceptedMouseButtons(Qt::MouseButtonMask);
392    q_ptr->setAcceptHoverEvents(true);
393    q_ptr->setFlag(QQuickItem::ItemAcceptsDrops, true);
394}
395
396void QQuickWebViewPrivate::didStartProvisionalLoadForFrame(WKPageRef, WKFrameRef frame, WKTypeRef, const void* clientInfo)
397{
398    if (!WKFrameIsMainFrame(frame))
399        return;
400
401    WKRetainPtr<WKURLRef> url  = adoptWK(WKFrameCopyProvisionalURL(frame));
402
403    QQuickWebView* const q = toQQuickWebViewPrivate(clientInfo)->q_func();
404
405    q->emitUrlChangeIfNeeded();
406    QWebLoadRequest loadRequest(WKURLCopyQUrl(url.get()), QQuickWebView::LoadStartedStatus);
407    emit q->loadingChanged(&loadRequest);
408}
409
410void QQuickWebViewPrivate::didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef, WKFrameRef frame, WKTypeRef, const void* clientInfo)
411{
412    if (!WKFrameIsMainFrame(frame))
413        return;
414
415    toQQuickWebViewPrivate(clientInfo)->q_func()->emitUrlChangeIfNeeded();
416}
417
418void QQuickWebViewPrivate::didFailLoad(WKPageRef, WKFrameRef frame, WKErrorRef errorRef, WKTypeRef, const void* clientInfo)
419{
420    if (!WKFrameIsMainFrame(frame))
421        return;
422
423    QQuickWebView* const q = toQQuickWebViewPrivate(clientInfo)->q_func();
424    ASSERT(!q->loading());
425
426    QtWebError error(errorRef);
427    if (error.isCancellation()) {
428        QWebLoadRequest loadRequest(q->url(), QQuickWebView::LoadStoppedStatus);
429        emit q->loadingChanged(&loadRequest);
430        return;
431    }
432
433    int errorCode = error.errorCode();
434    if (errorCode == kWKErrorCodeFrameLoadInterruptedByPolicyChange && errorCode == kWKErrorCodePlugInWillHandleLoad) {
435        QWebLoadRequest loadRequest(q->url(), QQuickWebView::LoadSucceededStatus);
436        q->emitUrlChangeIfNeeded();
437        emit q->loadingChanged(&loadRequest);
438        return;
439    }
440
441    // We set the unreachable url unconditionally so that the current
442    // active url of the webview when the loadingChanged signal is
443    // emitted reflects the failed url, not the previously committed
444    // url. This also ensures that if the user does not do a loadHtml
445    // with an error page and and unreachable url as a reponse to the
446    // failed load, we can still detect the failed url for reloads.
447    // We need to find a way to do this via the C API or find another
448    // way to do this.
449    toImpl(frame)->setUnreachableURL(error.url());
450    q->emitUrlChangeIfNeeded();
451    QWebLoadRequest loadRequest(error.url(), QQuickWebView::LoadFailedStatus, error.description(), static_cast<QQuickWebView::ErrorDomain>(error.type()), errorCode);
452    emit q->loadingChanged(&loadRequest);
453}
454
455void QQuickWebViewPrivate::didCommitLoadForFrame(WKPageRef, WKFrameRef frame, WKTypeRef, const void* clientInfo)
456{
457    if (!WKFrameIsMainFrame(frame))
458        return;
459    QQuickWebViewPrivate* d = toQQuickWebViewPrivate(clientInfo);
460
461    PageViewportController* pageViewportController = d->viewportController();
462    if (pageViewportController)
463        pageViewportController->didCommitLoad();
464
465    QQuickWebView* const q = d->q_func();
466    ASSERT(q->loading());
467    d->m_betweenLoadCommitAndFirstFrame = true;
468    emit q->navigationHistoryChanged();
469    emit q->titleChanged();
470}
471
472void QQuickWebViewPrivate::didFinishLoadForFrame(WKPageRef, WKFrameRef frame, WKTypeRef, const void* clientInfo)
473{
474    if (!WKFrameIsMainFrame(frame))
475        return;
476
477    QQuickWebView* const q = toQQuickWebViewPrivate(clientInfo)->q_func();
478    ASSERT(!q->loading());
479
480    QWebLoadRequest loadRequest(q->url(), QQuickWebView::LoadSucceededStatus);
481    emit q->loadingChanged(&loadRequest);
482}
483
484void QQuickWebViewPrivate::didSameDocumentNavigationForFrame(WKPageRef, WKFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef userData, const void* clientInfo)
485{
486    if (!WKFrameIsMainFrame(frame))
487        return;
488    QQuickWebView* const q = toQQuickWebViewPrivate(clientInfo)->q_func();
489    q->emitUrlChangeIfNeeded();
490    emit q->navigationHistoryChanged();
491}
492
493void QQuickWebViewPrivate::didReceiveTitleForFrame(WKPageRef, WKStringRef title, WKFrameRef frame, WKTypeRef, const void* clientInfo)
494{
495    if (!WKFrameIsMainFrame(frame))
496        return;
497    emit toQQuickWebViewPrivate(clientInfo)->q_func()->titleChanged();
498}
499
500void QQuickWebViewPrivate::didStartProgress(WKPageRef, const void* clientInfo)
501{
502    toQQuickWebViewPrivate(clientInfo)->loadProgressDidChange(0);
503}
504
505void QQuickWebViewPrivate::didChangeProgress(WKPageRef page, const void* clientInfo)
506{
507    toQQuickWebViewPrivate(clientInfo)->loadProgressDidChange(WKPageGetEstimatedProgress(page) * 100);
508}
509
510void QQuickWebViewPrivate::didFinishProgress(WKPageRef, const void* clientInfo)
511{
512    toQQuickWebViewPrivate(clientInfo)->loadProgressDidChange(100);
513}
514
515void QQuickWebViewPrivate::didChangeBackForwardList(WKPageRef, WKBackForwardListItemRef, WKArrayRef, const void *clientInfo)
516{
517    toQQuickWebViewPrivate(clientInfo)->navigationHistory->d->reset();
518}
519
520void QQuickWebViewPrivate::setTransparentBackground(bool enable)
521{
522    webPageProxy->setDrawsTransparentBackground(enable);
523}
524
525bool QQuickWebViewPrivate::transparentBackground() const
526{
527    return webPageProxy->drawsTransparentBackground();
528}
529
530void QQuickWebViewPrivate::loadProgressDidChange(int loadProgress)
531{
532    Q_Q(QQuickWebView);
533
534    m_loadProgress = loadProgress;
535
536    emit q->loadProgressChanged();
537}
538
539void QQuickWebViewPrivate::handleMouseEvent(QMouseEvent* event)
540{
541    switch (event->type()) {
542    case QEvent::MouseButtonPress:
543        pageEventHandler->handleMousePressEvent(event);
544        break;
545    case QEvent::MouseMove:
546        pageEventHandler->handleMouseMoveEvent(event);
547        break;
548    case QEvent::MouseButtonRelease:
549        pageEventHandler->handleMouseReleaseEvent(event);
550        break;
551    case QEvent::MouseButtonDblClick:
552        // If a MouseButtonDblClick was received then we got a MouseButtonPress before.
553        // WebCore will build double-clicks out of press events.
554        event->accept();
555        break;
556    default:
557        ASSERT_NOT_REACHED();
558        break;
559    }
560}
561
562void QQuickWebViewPrivate::setNeedsDisplay()
563{
564    Q_Q(QQuickWebView);
565    if (renderToOffscreenBuffer()) {
566        // This is used only to mantain the rendering synchronisation between the UI and
567        // the web process when running tests even if the render loop is not active.
568        QImage dummyImage(1, 1, QImage::Format_ARGB32);
569        QPainter painter(&dummyImage);
570        q->page()->d->paint(&painter);
571        return;
572    }
573    q->page()->update();
574}
575
576void QQuickWebViewPrivate::didRenderFrame()
577{
578    Q_Q(QQuickWebView);
579    if (m_betweenLoadCommitAndFirstFrame) {
580        emit q->experimental()->loadVisuallyCommitted();
581        m_betweenLoadCommitAndFirstFrame = false;
582    }
583}
584
585void QQuickWebViewPrivate::processDidCrash()
586{
587    Q_Q(QQuickWebView);
588
589    QUrl url(KURL(WebCore::ParsedURLString, webPageProxy->urlAtProcessExit()));
590    qWarning("WARNING: The web process experienced a crash on '%s'.", qPrintable(url.toString(QUrl::RemoveUserInfo)));
591
592    pageEventHandler->resetGestureRecognizers();
593
594    // Check if loading was ongoing, when process crashed.
595    if (m_loadProgress > 0 && m_loadProgress < 100) {
596        QWebLoadRequest loadRequest(url, QQuickWebView::LoadFailedStatus, QLatin1String("The web process crashed."), QQuickWebView::InternalErrorDomain, 0);
597
598        loadProgressDidChange(100);
599        emit q->loadingChanged(&loadRequest);
600    }
601}
602
603void QQuickWebViewPrivate::didRelaunchProcess()
604{
605    qWarning("WARNING: The web process has been successfully restarted.");
606
607    if (DrawingAreaProxy *drawingArea = webPageProxy->drawingArea()) {
608        drawingArea->setSize(viewSize(), IntSize(), IntSize());
609
610        updateViewportSize();
611        updateUserScripts();
612        updateSchemeDelegates();
613    }
614}
615
616PassOwnPtr<DrawingAreaProxy> QQuickWebViewPrivate::createDrawingAreaProxy()
617{
618    return DrawingAreaProxyImpl::create(webPageProxy.get());
619}
620
621void QQuickWebViewPrivate::handleDownloadRequest(DownloadProxy* download)
622{
623    Q_Q(QQuickWebView);
624    // This function is responsible for hooking up a DownloadProxy to our API layer
625    // by creating a QWebDownloadItem. It will then wait for the QWebDownloadItem to be
626    // ready (filled with the ResourceResponse information) so we can pass it through to
627    // our WebViews.
628    QWebDownloadItem* downloadItem = new QWebDownloadItem();
629    downloadItem->d->downloadProxy = download;
630
631    q->connect(downloadItem->d, SIGNAL(receivedResponse(QWebDownloadItem*)), q, SLOT(_q_onReceivedResponseFromDownload(QWebDownloadItem*)));
632    QtWebContext::defaultContext()->downloadManager()->addDownload(toAPI(download), downloadItem);
633}
634
635void QQuickWebViewPrivate::_q_onVisibleChanged()
636{
637    webPageProxy->viewStateDidChange(WebPageProxy::ViewIsVisible);
638}
639
640void QQuickWebViewPrivate::_q_onUrlChanged()
641{
642    updateIcon();
643}
644
645void QQuickWebViewPrivate::_q_onIconChangedForPageURL(const QString& pageUrl)
646{
647    if (pageUrl != m_currentUrl)
648        return;
649
650    updateIcon();
651}
652
653/* Called either when the url changes, or when the icon for the current page changes */
654void QQuickWebViewPrivate::updateIcon()
655{
656    Q_Q(QQuickWebView);
657
658    QQuickView* view = qobject_cast<QQuickView*>(q->window());
659    if (!view)
660        return;
661
662    QWebIconImageProvider* provider = static_cast<QWebIconImageProvider*>(
663                view->engine()->imageProvider(QWebIconImageProvider::identifier()));
664    if (!provider)
665        return;
666
667    QUrl iconUrl = provider->iconURLForPageURLInContext(m_currentUrl, context);
668
669    if (iconUrl == m_iconUrl)
670        return;
671
672    m_iconUrl = iconUrl;
673    emit q->iconChanged();
674}
675
676void QQuickWebViewPrivate::_q_onReceivedResponseFromDownload(QWebDownloadItem* downloadItem)
677{
678    // Now that our downloadItem has everything we need we can emit downloadRequested.
679    if (!downloadItem)
680        return;
681
682    Q_Q(QQuickWebView);
683    QQmlEngine::setObjectOwnership(downloadItem, QQmlEngine::JavaScriptOwnership);
684    emit q->experimental()->downloadRequested(downloadItem);
685}
686
687void QQuickWebViewPrivate::runJavaScriptAlert(const QString& alertText)
688{
689    Q_Q(QQuickWebView);
690    QtDialogRunner dialogRunner(q);
691    if (!dialogRunner.initForAlert(alertText))
692        return;
693
694    dialogRunner.run();
695}
696
697bool QQuickWebViewPrivate::runJavaScriptConfirm(const QString& message)
698{
699    Q_Q(QQuickWebView);
700    QtDialogRunner dialogRunner(q);
701    if (!dialogRunner.initForConfirm(message))
702        return true;
703
704    dialogRunner.run();
705
706    return dialogRunner.wasAccepted();
707}
708
709QString QQuickWebViewPrivate::runJavaScriptPrompt(const QString& message, const QString& defaultValue, bool& ok)
710{
711    Q_Q(QQuickWebView);
712    QtDialogRunner dialogRunner(q);
713    if (!dialogRunner.initForPrompt(message, defaultValue)) {
714        ok = true;
715        return defaultValue;
716    }
717
718    dialogRunner.run();
719
720    ok = dialogRunner.wasAccepted();
721    return dialogRunner.result();
722}
723
724void QQuickWebViewPrivate::handleAuthenticationRequiredRequest(const QString& hostname, const QString& realm, const QString& prefilledUsername, QString& username, QString& password)
725{
726    Q_Q(QQuickWebView);
727    QtDialogRunner dialogRunner(q);
728    if (!dialogRunner.initForAuthentication(hostname, realm, prefilledUsername))
729        return;
730
731    dialogRunner.run();
732
733    username = dialogRunner.username();
734    password = dialogRunner.password();
735}
736
737void QQuickWebViewPrivate::handleProxyAuthenticationRequiredRequest(const QString& hostname, uint16_t port, const QString& prefilledUsername, QString& username, QString& password)
738{
739    Q_Q(QQuickWebView);
740    QtDialogRunner dialogRunner(q);
741    if (!dialogRunner.initForProxyAuthentication(hostname, port, prefilledUsername))
742        return;
743
744    dialogRunner.run();
745
746    username = dialogRunner.username();
747    password = dialogRunner.password();
748}
749
750bool QQuickWebViewPrivate::handleCertificateVerificationRequest(const QString& hostname)
751{
752    Q_Q(QQuickWebView);
753
754    if (m_allowAnyHTTPSCertificateForLocalHost
755        && (hostname == QStringLiteral("127.0.0.1") || hostname == QStringLiteral("localhost")))
756        return true;
757
758    QtDialogRunner dialogRunner(q);
759    if (!dialogRunner.initForCertificateVerification(hostname))
760        return false;
761
762    dialogRunner.run();
763
764    return dialogRunner.wasAccepted();
765}
766
767void QQuickWebViewPrivate::chooseFiles(WKOpenPanelResultListenerRef listenerRef, const QStringList& selectedFileNames, QtWebPageUIClient::FileChooserType type)
768{
769    Q_Q(QQuickWebView);
770
771    QtDialogRunner dialogRunner(q);
772    if (!dialogRunner.initForFilePicker(selectedFileNames, (type == QtWebPageUIClient::MultipleFilesSelection)))
773        return;
774
775    dialogRunner.run();
776
777    if (dialogRunner.wasAccepted()) {
778        QStringList selectedPaths = dialogRunner.filePaths();
779
780        Vector<RefPtr<APIObject> > wkFiles(selectedPaths.size());
781        for (unsigned i = 0; i < selectedPaths.size(); ++i)
782            wkFiles[i] = WebURL::create(QUrl::fromLocalFile(selectedPaths.at(i)).toString());
783
784        WKOpenPanelResultListenerChooseFiles(listenerRef, toAPI(ImmutableArray::adopt(wkFiles).leakRef()));
785    } else
786        WKOpenPanelResultListenerCancel(listenerRef);
787
788}
789
790quint64 QQuickWebViewPrivate::exceededDatabaseQuota(const QString& databaseName, const QString& displayName, WKSecurityOriginRef securityOrigin, quint64 currentQuota, quint64 currentOriginUsage, quint64 currentDatabaseUsage, quint64 expectedUsage)
791{
792    Q_Q(QQuickWebView);
793    QtDialogRunner dialogRunner(q);
794    if (!dialogRunner.initForDatabaseQuotaDialog(databaseName, displayName, securityOrigin, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage))
795        return 0;
796
797    dialogRunner.run();
798
799    return dialogRunner.wasAccepted() ? dialogRunner.databaseQuota() : 0;
800}
801
802/* The 'WebView' attached property allows items spawned by the webView to
803   refer back to the originating webView through 'WebView.view', similar
804   to how ListView.view and GridView.view is exposed to items. */
805QQuickWebViewAttached::QQuickWebViewAttached(QObject* object)
806    : QObject(object)
807    , m_view(0)
808{
809}
810
811void QQuickWebViewAttached::setView(QQuickWebView* view)
812{
813    if (m_view == view)
814        return;
815    m_view = view;
816    emit viewChanged();
817}
818
819QQuickWebViewAttached* QQuickWebView::qmlAttachedProperties(QObject* object)
820{
821    return new QQuickWebViewAttached(object);
822}
823
824
825
826void QQuickWebViewPrivate::addAttachedPropertyTo(QObject* object)
827{
828    Q_Q(QQuickWebView);
829    QQuickWebViewAttached* attached = static_cast<QQuickWebViewAttached*>(qmlAttachedPropertiesObject<QQuickWebView>(object));
830    attached->setView(q);
831}
832
833bool QQuickWebViewPrivate::navigatorQtObjectEnabled() const
834{
835    return m_navigatorQtObjectEnabled;
836}
837
838void QQuickWebViewPrivate::setNavigatorQtObjectEnabled(bool enabled)
839{
840    ASSERT(enabled != m_navigatorQtObjectEnabled);
841    // FIXME: Currently we have to keep this information in both processes and the setting is asynchronous.
842    m_navigatorQtObjectEnabled = enabled;
843
844    static WKStringRef messageName = WKStringCreateWithUTF8CString("SetNavigatorQtObjectEnabled");
845    WKRetainPtr<WKBooleanRef> wkEnabled = adoptWK(WKBooleanCreate(enabled));
846    WKPagePostMessageToInjectedBundle(webPage.get(), messageName, wkEnabled.get());
847}
848
849static WKRetainPtr<WKStringRef> readUserScript(const QUrl& url)
850{
851    QString path;
852    if (url.isLocalFile())
853        path = url.toLocalFile();
854    else if (url.scheme() == QLatin1String("qrc"))
855        path = QStringLiteral(":") + url.path();
856    else {
857        qWarning("QQuickWebView: Couldn't open '%s' as user script because only file:/// and qrc:/// URLs are supported.", qPrintable(url.toString()));
858        return 0;
859    }
860
861    QFile file(path);
862    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
863        qWarning("QQuickWebView: Couldn't open '%s' as user script due to error '%s'.", qPrintable(url.toString()), qPrintable(file.errorString()));
864        return 0;
865    }
866
867    QByteArray contents = file.readAll();
868    if (contents.isEmpty())
869        qWarning("QQuickWebView: Ignoring '%s' as user script because file is empty.", qPrintable(url.toString()));
870
871    return adoptWK(WKStringCreateWithUTF8CString(contents.constData()));
872}
873
874void QQuickWebViewPrivate::updateUserScripts()
875{
876    // This feature works per-WebView because we keep an unique page group for
877    // each Page/WebView pair we create.
878    WKPageGroupRemoveAllUserScripts(pageGroup.get());
879
880    for (unsigned i = 0; i < userScripts.size(); ++i) {
881        const QUrl& url = userScripts.at(i);
882        if (!url.isValid()) {
883            qWarning("QQuickWebView: Couldn't open '%s' as user script because URL is invalid.", qPrintable(url.toString()));
884            continue;
885        }
886
887        WKRetainPtr<WKStringRef> contents = readUserScript(url);
888        if (!contents || WKStringIsEmpty(contents.get()))
889            continue;
890        WKPageGroupAddUserScript(pageGroup.get(), contents.get(), /*baseURL*/ 0, /*whitelistedURLPatterns*/ 0, /*blacklistedURLPatterns*/ 0, kWKInjectInTopFrameOnly, kWKInjectAtDocumentEnd);
891    }
892}
893
894void QQuickWebViewPrivate::updateSchemeDelegates()
895{
896    webPageProxy->registerApplicationScheme(ASCIILiteral("qrc"));
897
898    QQmlListProperty<QQuickUrlSchemeDelegate> schemes = experimental->schemeDelegates();
899    for (int i = 0, numSchemes = experimental->schemeDelegates_Count(&schemes); i < numSchemes; ++i) {
900        QQuickUrlSchemeDelegate* scheme = experimental->schemeDelegates_At(&schemes, i);
901        webPageProxy->registerApplicationScheme(scheme->scheme());
902    }
903}
904
905QPointF QQuickWebViewPrivate::contentPos() const
906{
907    Q_Q(const QQuickWebView);
908    return QPointF(q->contentX(), q->contentY());
909}
910
911void QQuickWebViewPrivate::setContentPos(const QPointF& pos)
912{
913    Q_Q(QQuickWebView);
914    q->setContentX(pos.x());
915    q->setContentY(pos.y());
916}
917
918WebCore::IntSize QQuickWebViewPrivate::viewSize() const
919{
920    return WebCore::IntSize(pageView->width(), pageView->height());
921}
922
923/*!
924    \internal
925
926    \qmlsignal WebViewExperimental::onMessageReceived(var message)
927
928    \brief Emitted when JavaScript code executing on the web page calls navigator.qt.postMessage().
929
930    \sa postMessage
931*/
932void QQuickWebViewPrivate::didReceiveMessageFromNavigatorQtObject(WKStringRef message)
933{
934    QVariantMap variantMap;
935    variantMap.insert(QLatin1String("data"), WKStringCopyQString(message));
936    variantMap.insert(QLatin1String("origin"), q_ptr->url());
937    emit q_ptr->experimental()->messageReceived(variantMap);
938}
939
940CoordinatedGraphicsScene* QQuickWebViewPrivate::coordinatedGraphicsScene()
941{
942    if (webPageProxy && webPageProxy->drawingArea() && webPageProxy->drawingArea()->coordinatedLayerTreeHostProxy())
943        return webPageProxy->drawingArea()->coordinatedLayerTreeHostProxy()->coordinatedGraphicsScene();
944
945    return 0;
946}
947
948float QQuickWebViewPrivate::deviceScaleFactor()
949{
950    return webPageProxy->deviceScaleFactor();
951}
952
953void QQuickWebViewPrivate::setIntrinsicDeviceScaleFactor(float scaleFactor)
954{
955    webPageProxy->setIntrinsicDeviceScaleFactor(scaleFactor);
956}
957
958QQuickWebViewLegacyPrivate::QQuickWebViewLegacyPrivate(QQuickWebView* viewport)
959    : QQuickWebViewPrivate(viewport)
960{
961}
962
963void QQuickWebViewLegacyPrivate::initialize(WKContextRef contextRef, WKPageGroupRef pageGroupRef)
964{
965    QQuickWebViewPrivate::initialize(contextRef, pageGroupRef);
966
967    // Trigger setting of correct visibility flags after everything was allocated and initialized.
968    _q_onVisibleChanged();
969}
970
971void QQuickWebViewLegacyPrivate::updateViewportSize()
972{
973    Q_Q(QQuickWebView);
974    QSizeF viewportSize = q->boundingRect().size();
975    if (viewportSize.isEmpty())
976        return;
977
978    pageView->setContentsSize(viewportSize);
979
980    if (DrawingAreaProxy *drawingArea = webPageProxy->drawingArea()) {
981        // The fixed layout is handled by the FrameView and the drawing area doesn't behave differently
982        // whether its fixed or not. We still need to tell the drawing area which part of it
983        // has to be rendered on tiles, and in desktop mode it's all of it.
984        drawingArea->setSize(viewportSize.toSize(), IntSize(), IntSize());
985        // The backing store scale factor should already be set to the device pixel ratio
986        // of the underlying window, thus we set the effective scale to 1 here.
987        drawingArea->setVisibleContentsRect(FloatRect(FloatPoint(), FloatSize(viewportSize)), FloatPoint());
988    }
989}
990
991qreal QQuickWebViewLegacyPrivate::zoomFactor() const
992{
993    return WKPageGetPageZoomFactor(webPage.get());
994}
995
996void QQuickWebViewLegacyPrivate::setZoomFactor(qreal factor)
997{
998    WKPageSetPageZoomFactor(webPage.get(), factor);
999}
1000
1001QQuickWebViewFlickablePrivate::QQuickWebViewFlickablePrivate(QQuickWebView* viewport)
1002    : QQuickWebViewPrivate(viewport)
1003{
1004}
1005
1006void QQuickWebViewFlickablePrivate::initialize(WKContextRef contextRef, WKPageGroupRef pageGroupRef)
1007{
1008    QQuickWebViewPrivate::initialize(contextRef, pageGroupRef);
1009}
1010
1011void QQuickWebViewFlickablePrivate::onComponentComplete()
1012{
1013    QQuickWebViewPrivate::onComponentComplete();
1014
1015    Q_Q(QQuickWebView);
1016    m_pageViewportControllerClient.reset(new PageViewportControllerClientQt(q, pageView.data()));
1017    m_pageViewportController.reset(new PageViewportController(webPageProxy.get(), m_pageViewportControllerClient.data()));
1018    pageEventHandler->setViewportController(m_pageViewportControllerClient.data());
1019
1020    // Trigger setting of correct visibility flags after everything was allocated and initialized.
1021    _q_onVisibleChanged();
1022}
1023
1024void QQuickWebViewFlickablePrivate::didChangeViewportProperties(const WebCore::ViewportAttributes& newAttributes)
1025{
1026    if (m_pageViewportController)
1027        m_pageViewportController->didChangeViewportAttributes(newAttributes);
1028}
1029
1030void QQuickWebViewFlickablePrivate::updateViewportSize()
1031{
1032    Q_Q(QQuickWebView);
1033
1034    if (m_pageViewportController)
1035        m_pageViewportController->didChangeViewportSize(FloatSize(q->width(), q->height()));
1036}
1037
1038void QQuickWebViewFlickablePrivate::pageDidRequestScroll(const QPoint& pos)
1039{
1040    if (m_pageViewportController)
1041        m_pageViewportController->pageDidRequestScroll(pos);
1042}
1043
1044QQuickWebViewExperimental::QQuickWebViewExperimental(QQuickWebView *webView, QQuickWebViewPrivate* webViewPrivate)
1045    : QObject(webView)
1046    , q_ptr(webView)
1047    , d_ptr(webViewPrivate)
1048    , schemeParent(new QObject(this))
1049    , m_test(new QWebKitTest(webViewPrivate, this))
1050{
1051}
1052
1053QQuickWebViewExperimental::~QQuickWebViewExperimental()
1054{
1055}
1056
1057void QQuickWebViewExperimental::setRenderToOffscreenBuffer(bool enable)
1058{
1059    Q_D(QQuickWebView);
1060    d->setRenderToOffscreenBuffer(enable);
1061}
1062
1063bool QQuickWebViewExperimental::renderToOffscreenBuffer() const
1064{
1065    Q_D(const QQuickWebView);
1066    return d->renderToOffscreenBuffer();
1067}
1068
1069bool QQuickWebViewExperimental::transparentBackground() const
1070{
1071    Q_D(const QQuickWebView);
1072    return d->transparentBackground();
1073}
1074void QQuickWebViewExperimental::setTransparentBackground(bool enable)
1075{
1076    Q_D(QQuickWebView);
1077    d->setTransparentBackground(enable);
1078}
1079
1080bool QQuickWebViewExperimental::useDefaultContentItemSize() const
1081{
1082    Q_D(const QQuickWebView);
1083    return d->m_useDefaultContentItemSize;
1084}
1085
1086void QQuickWebViewExperimental::setUseDefaultContentItemSize(bool enable)
1087{
1088    Q_D(QQuickWebView);
1089    d->m_useDefaultContentItemSize = enable;
1090}
1091
1092/*!
1093    \internal
1094
1095    \qmlproperty int WebViewExperimental::preferredMinimumContentsWidth
1096    \brief Minimum contents width when not overriden by the page itself.
1097
1098    Unless the page defines how contents should be laid out, using e.g.
1099    the viewport meta tag, it is laid out given the width of the viewport
1100    (in CSS units).
1101
1102    This setting can be used to enforce a minimum width when the page
1103    does not define a width itself. This is useful for laying out pages
1104    designed for big screens, commonly knows as desktop pages, on small
1105    devices.
1106
1107    The default value is 0, but the value of 980 is recommented for small
1108    screens as it provides a good trade off between legitable pages and
1109    non-broken content.
1110 */
1111int QQuickWebViewExperimental::preferredMinimumContentsWidth() const
1112{
1113    Q_D(const QQuickWebView);
1114    return d->webPageProxy->pageGroup()->preferences()->layoutFallbackWidth();
1115}
1116
1117void QQuickWebViewExperimental::setPreferredMinimumContentsWidth(int width)
1118{
1119    Q_D(QQuickWebView);
1120    WebPreferences* webPreferences = d->webPageProxy->pageGroup()->preferences();
1121
1122    if (width == webPreferences->layoutFallbackWidth())
1123        return;
1124
1125    webPreferences->setLayoutFallbackWidth(width);
1126    emit preferredMinimumContentsWidthChanged();
1127}
1128
1129void QQuickWebViewExperimental::setFlickableViewportEnabled(bool enable)
1130{
1131    s_flickableViewportEnabled = enable;
1132}
1133
1134bool QQuickWebViewExperimental::flickableViewportEnabled()
1135{
1136    return s_flickableViewportEnabled;
1137}
1138
1139/*!
1140    \internal
1141
1142    \qmlmethod void WebViewExperimental::postMessage(string message)
1143
1144    \brief Post a message to an onmessage function registered with the navigator.qt object
1145           by JavaScript code executing on the page.
1146
1147    \sa onMessageReceived
1148*/
1149
1150void QQuickWebViewExperimental::postMessage(const QString& message)
1151{
1152    Q_D(QQuickWebView);
1153    static WKStringRef messageName = WKStringCreateWithUTF8CString("MessageToNavigatorQtObject");
1154    WKRetainPtr<WKStringRef> contents = adoptWK(WKStringCreateWithQString(message));
1155    WKPagePostMessageToInjectedBundle(d->webPage.get(), messageName, contents.get());
1156}
1157
1158QQmlComponent* QQuickWebViewExperimental::alertDialog() const
1159{
1160    Q_D(const QQuickWebView);
1161    return d->alertDialog;
1162}
1163
1164void QQuickWebViewExperimental::setAlertDialog(QQmlComponent* alertDialog)
1165{
1166    Q_D(QQuickWebView);
1167    if (d->alertDialog == alertDialog)
1168        return;
1169    d->alertDialog = alertDialog;
1170    emit alertDialogChanged();
1171}
1172
1173QQmlComponent* QQuickWebViewExperimental::confirmDialog() const
1174{
1175    Q_D(const QQuickWebView);
1176    return d->confirmDialog;
1177}
1178
1179void QQuickWebViewExperimental::setConfirmDialog(QQmlComponent* confirmDialog)
1180{
1181    Q_D(QQuickWebView);
1182    if (d->confirmDialog == confirmDialog)
1183        return;
1184    d->confirmDialog = confirmDialog;
1185    emit confirmDialogChanged();
1186}
1187
1188QWebNavigationHistory* QQuickWebViewExperimental::navigationHistory() const
1189{
1190    return d_ptr->navigationHistory.get();
1191}
1192
1193QQmlComponent* QQuickWebViewExperimental::promptDialog() const
1194{
1195    Q_D(const QQuickWebView);
1196    return d->promptDialog;
1197}
1198
1199QWebPreferences* QQuickWebViewExperimental::preferences() const
1200{
1201    QQuickWebViewPrivate* const d = d_ptr;
1202    if (!d->preferences)
1203        d->preferences = adoptPtr(QWebPreferencesPrivate::createPreferences(d));
1204    return d->preferences.get();
1205}
1206
1207void QQuickWebViewExperimental::setPromptDialog(QQmlComponent* promptDialog)
1208{
1209    Q_D(QQuickWebView);
1210    if (d->promptDialog == promptDialog)
1211        return;
1212    d->promptDialog = promptDialog;
1213    emit promptDialogChanged();
1214}
1215
1216QQmlComponent* QQuickWebViewExperimental::authenticationDialog() const
1217{
1218    Q_D(const QQuickWebView);
1219    return d->authenticationDialog;
1220}
1221
1222void QQuickWebViewExperimental::setAuthenticationDialog(QQmlComponent* authenticationDialog)
1223{
1224    Q_D(QQuickWebView);
1225    if (d->authenticationDialog == authenticationDialog)
1226        return;
1227    d->authenticationDialog = authenticationDialog;
1228    emit authenticationDialogChanged();
1229}
1230
1231QQmlComponent* QQuickWebViewExperimental::proxyAuthenticationDialog() const
1232{
1233    Q_D(const QQuickWebView);
1234    return d->proxyAuthenticationDialog;
1235}
1236
1237void QQuickWebViewExperimental::setProxyAuthenticationDialog(QQmlComponent* proxyAuthenticationDialog)
1238{
1239    Q_D(QQuickWebView);
1240    if (d->proxyAuthenticationDialog == proxyAuthenticationDialog)
1241        return;
1242    d->proxyAuthenticationDialog = proxyAuthenticationDialog;
1243    emit proxyAuthenticationDialogChanged();
1244}
1245QQmlComponent* QQuickWebViewExperimental::certificateVerificationDialog() const
1246{
1247    Q_D(const QQuickWebView);
1248    return d->certificateVerificationDialog;
1249}
1250
1251void QQuickWebViewExperimental::setCertificateVerificationDialog(QQmlComponent* certificateVerificationDialog)
1252{
1253    Q_D(QQuickWebView);
1254    if (d->certificateVerificationDialog == certificateVerificationDialog)
1255        return;
1256    d->certificateVerificationDialog = certificateVerificationDialog;
1257    emit certificateVerificationDialogChanged();
1258}
1259
1260QQmlComponent* QQuickWebViewExperimental::itemSelector() const
1261{
1262    Q_D(const QQuickWebView);
1263    return d->itemSelector;
1264}
1265
1266void QQuickWebViewExperimental::setItemSelector(QQmlComponent* itemSelector)
1267{
1268    Q_D(QQuickWebView);
1269    if (d->itemSelector == itemSelector)
1270        return;
1271    d->itemSelector = itemSelector;
1272    emit itemSelectorChanged();
1273}
1274
1275QQmlComponent* QQuickWebViewExperimental::filePicker() const
1276{
1277    Q_D(const QQuickWebView);
1278    return d->filePicker;
1279}
1280
1281void QQuickWebViewExperimental::setFilePicker(QQmlComponent* filePicker)
1282{
1283    Q_D(QQuickWebView);
1284    if (d->filePicker == filePicker)
1285        return;
1286    d->filePicker = filePicker;
1287    emit filePickerChanged();
1288}
1289
1290QQmlComponent* QQuickWebViewExperimental::databaseQuotaDialog() const
1291{
1292    Q_D(const QQuickWebView);
1293    return d->databaseQuotaDialog;
1294}
1295
1296void QQuickWebViewExperimental::setDatabaseQuotaDialog(QQmlComponent* databaseQuotaDialog)
1297{
1298    Q_D(QQuickWebView);
1299    if (d->databaseQuotaDialog == databaseQuotaDialog)
1300        return;
1301    d->databaseQuotaDialog = databaseQuotaDialog;
1302    emit databaseQuotaDialogChanged();
1303}
1304
1305QQmlComponent* QQuickWebViewExperimental::colorChooser() const
1306{
1307    Q_D(const QQuickWebView);
1308    return d->colorChooser;
1309}
1310
1311void QQuickWebViewExperimental::setColorChooser(QQmlComponent* colorChooser)
1312{
1313    Q_D(QQuickWebView);
1314    if (d->colorChooser == colorChooser)
1315        return;
1316
1317    d->colorChooser = colorChooser;
1318    emit colorChooserChanged();
1319}
1320
1321QString QQuickWebViewExperimental::userAgent() const
1322{
1323    Q_D(const QQuickWebView);
1324    WKRetainPtr<WKStringRef> ua = adoptWK(WKPageCopyCustomUserAgent(d->webPage.get()));
1325    return WKStringCopyQString(ua.get());
1326}
1327
1328void QQuickWebViewExperimental::setUserAgent(const QString& userAgent)
1329{
1330    Q_D(QQuickWebView);
1331    WKRetainPtr<WKStringRef> newUserAgent = adoptWK(WKStringCreateWithQString(userAgent));
1332    WKRetainPtr<WKStringRef> currentUserAgent = adoptWK(WKPageCopyCustomUserAgent(d->webPage.get()));
1333    if (WKStringIsEqual(newUserAgent.get(), currentUserAgent.get()))
1334        return;
1335
1336    WKPageSetCustomUserAgent(d->webPage.get(), newUserAgent.get());
1337    emit userAgentChanged();
1338}
1339
1340/*!
1341    \internal
1342
1343    \qmlproperty int WebViewExperimental::deviceWidth
1344    \brief The device width used by the viewport calculations.
1345
1346    The value used when calculation the viewport, eg. what is used for 'device-width' when
1347    used in the viewport meta tag. If unset (zero or negative width), the width of the
1348    actual viewport is used instead.
1349*/
1350
1351int QQuickWebViewExperimental::deviceWidth() const
1352{
1353    Q_D(const QQuickWebView);
1354    return d->webPageProxy->pageGroup()->preferences()->deviceWidth();
1355}
1356
1357void QQuickWebViewExperimental::setDeviceWidth(int value)
1358{
1359    Q_D(QQuickWebView);
1360    d->webPageProxy->pageGroup()->preferences()->setDeviceWidth(qMax(0, value));
1361    emit deviceWidthChanged();
1362}
1363
1364/*!
1365    \internal
1366
1367    \qmlproperty int WebViewExperimental::deviceHeight
1368    \brief The device width used by the viewport calculations.
1369
1370    The value used when calculation the viewport, eg. what is used for 'device-height' when
1371    used in the viewport meta tag. If unset (zero or negative height), the height of the
1372    actual viewport is used instead.
1373*/
1374
1375int QQuickWebViewExperimental::deviceHeight() const
1376{
1377    Q_D(const QQuickWebView);
1378    return d->webPageProxy->pageGroup()->preferences()->deviceHeight();
1379}
1380
1381void QQuickWebViewExperimental::setDeviceHeight(int value)
1382{
1383    Q_D(QQuickWebView);
1384    d->webPageProxy->pageGroup()->preferences()->setDeviceHeight(qMax(0, value));
1385    emit deviceHeightChanged();
1386}
1387
1388/*!
1389    \internal
1390
1391    \qmlmethod void WebViewExperimental::evaluateJavaScript(string script [, function(result)])
1392
1393    \brief Evaluates the specified JavaScript and, if supplied, calls a function with the result.
1394*/
1395
1396void QQuickWebViewExperimental::evaluateJavaScript(const QString& script, const QJSValue& value)
1397{
1398    JSCallbackClosure* closure = new JSCallbackClosure;
1399
1400    closure->receiver = this;
1401    closure->value = value;
1402
1403    WKRetainPtr<WKStringRef> scriptString = adoptWK(WKStringCreateWithQString(script));
1404    WKPageRunJavaScriptInMainFrame(d_ptr->webPage.get(), scriptString.get(), closure, javaScriptCallback);
1405}
1406
1407void QQuickWebViewExperimental::findText(const QString& string, FindFlags options)
1408{
1409    Q_D(QQuickWebView);
1410    if (string.isEmpty()) {
1411        WKPageHideFindUI(d->webPage.get());
1412        return;
1413    }
1414
1415    WKFindOptions wkOptions = kWKFindOptionsCaseInsensitive;
1416    if (options & FindCaseSensitively)
1417        wkOptions = wkOptions & ~kWKFindOptionsCaseInsensitive;
1418    if (options & FindBackward)
1419        wkOptions |= kWKFindOptionsBackwards;
1420    if (options & FindWrapsAroundDocument)
1421        wkOptions |= kWKFindOptionsWrapAround;
1422    if (options & FindHighlightAllOccurrences)
1423        wkOptions |= kWKFindOptionsShowHighlight;
1424
1425    WKRetainPtr<WKStringRef> str = adoptWK(WKStringCreateWithQString(string));
1426
1427    WKPageFindString(d->webPage.get(), str.get(), wkOptions, std::numeric_limits<unsigned>::max() - 1);
1428}
1429
1430QList<QUrl> QQuickWebViewExperimental::userScripts() const
1431{
1432    Q_D(const QQuickWebView);
1433    return d->userScripts;
1434}
1435
1436void QQuickWebViewExperimental::setUserScripts(const QList<QUrl>& userScripts)
1437{
1438    Q_D(QQuickWebView);
1439    if (d->userScripts == userScripts)
1440        return;
1441    d->userScripts = userScripts;
1442    d->updateUserScripts();
1443    emit userScriptsChanged();
1444}
1445
1446QUrl QQuickWebViewExperimental::remoteInspectorUrl() const
1447{
1448#if ENABLE(INSPECTOR_SERVER)
1449    return QUrl(WebInspectorServer::shared().inspectorUrlForPageID(d_ptr->webPageProxy->inspector()->remoteInspectionPageID()));
1450#else
1451    return QUrl();
1452#endif
1453}
1454
1455QQuickUrlSchemeDelegate* QQuickWebViewExperimental::schemeDelegates_At(QQmlListProperty<QQuickUrlSchemeDelegate>* property, int index)
1456{
1457    const QObjectList children = property->object->children();
1458    if (index < children.count())
1459        return static_cast<QQuickUrlSchemeDelegate*>(children.at(index));
1460    return 0;
1461}
1462
1463void QQuickWebViewExperimental::schemeDelegates_Append(QQmlListProperty<QQuickUrlSchemeDelegate>* property, QQuickUrlSchemeDelegate *scheme)
1464{
1465    if (!scheme->scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive)) {
1466        qWarning("WARNING: The qrc scheme is reserved to be handled internally. The handler will be ignored.");
1467        delete scheme;
1468        return;
1469    }
1470
1471    QObject* schemeParent = property->object;
1472    scheme->setParent(schemeParent);
1473    QQuickWebViewExperimental* webViewExperimental = qobject_cast<QQuickWebViewExperimental*>(property->object->parent());
1474    if (!webViewExperimental)
1475        return;
1476    scheme->reply()->setWebViewExperimental(webViewExperimental);
1477    QQuickWebViewPrivate* d = webViewExperimental->d_func();
1478    d->webPageProxy->registerApplicationScheme(scheme->scheme());
1479}
1480
1481int QQuickWebViewExperimental::schemeDelegates_Count(QQmlListProperty<QQuickUrlSchemeDelegate>* property)
1482{
1483    return property->object->children().count();
1484}
1485
1486void QQuickWebViewExperimental::schemeDelegates_Clear(QQmlListProperty<QQuickUrlSchemeDelegate>* property)
1487{
1488    const QObjectList children = property->object->children();
1489    for (int index = 0; index < children.count(); index++) {
1490        QObject* child = children.at(index);
1491        child->setParent(0);
1492        delete child;
1493    }
1494}
1495
1496QQmlListProperty<QQuickUrlSchemeDelegate> QQuickWebViewExperimental::schemeDelegates()
1497{
1498    return QQmlListProperty<QQuickUrlSchemeDelegate>(schemeParent, 0,
1499            QQuickWebViewExperimental::schemeDelegates_Append,
1500            QQuickWebViewExperimental::schemeDelegates_Count,
1501            QQuickWebViewExperimental::schemeDelegates_At,
1502            QQuickWebViewExperimental::schemeDelegates_Clear);
1503}
1504
1505void QQuickWebViewExperimental::invokeApplicationSchemeHandler(PassRefPtr<QtRefCountedNetworkRequestData> request)
1506{
1507    RefPtr<QtRefCountedNetworkRequestData> req = request;
1508    if (req->data().m_scheme.startsWith("qrc", false)) {
1509        QQuickQrcSchemeDelegate qrcDelegate(QUrl(QString(req->data().m_urlString)));
1510        qrcDelegate.request()->setNetworkRequestData(req);
1511        qrcDelegate.reply()->setNetworkRequestData(req);
1512        qrcDelegate.reply()->setWebViewExperimental(this);
1513        qrcDelegate.readResourceAndSend();
1514        return;
1515    }
1516
1517    const QObjectList children = schemeParent->children();
1518    for (int index = 0; index < children.count(); index++) {
1519        QQuickUrlSchemeDelegate* delegate = qobject_cast<QQuickUrlSchemeDelegate*>(children.at(index));
1520        if (!delegate)
1521            continue;
1522        if (!delegate->scheme().compare(QString(req->data().m_scheme), Qt::CaseInsensitive)) {
1523            delegate->request()->setNetworkRequestData(req);
1524            delegate->reply()->setNetworkRequestData(req);
1525            emit delegate->receivedRequest();
1526            return;
1527        }
1528    }
1529}
1530
1531void QQuickWebViewExperimental::sendApplicationSchemeReply(QQuickNetworkReply* reply)
1532{
1533    d_ptr->webPageProxy->sendApplicationSchemeReply(reply);
1534}
1535
1536void QQuickWebViewExperimental::goForwardTo(int index)
1537{
1538    d_ptr->navigationHistory->d->goForwardTo(index);
1539}
1540
1541void QQuickWebViewExperimental::goBackTo(int index)
1542{
1543    d_ptr->navigationHistory->d->goBackTo(index);
1544}
1545
1546QWebKitTest* QQuickWebViewExperimental::test()
1547{
1548    return m_test;
1549}
1550
1551QQuickWebPage* QQuickWebViewExperimental::page()
1552{
1553    return q_ptr->page();
1554}
1555
1556/*!
1557    \page index.html
1558    \title QtWebKit: QML WebView version 3.0
1559
1560    The WebView API allows QML applications to render regions of dynamic
1561    web content. A \e{WebView} component may share the screen with other
1562    QML components or encompass the full screen as specified within the
1563    QML application.
1564
1565    QML WebView version 3.0 is incompatible with previous QML \l
1566    {QtWebKit1::WebView} {WebView} API versions.  It allows an
1567    application to load pages into the WebView, either by URL or with
1568    an HTML string, and navigate within session history.  By default,
1569    links to different pages load within the same WebView, but applications
1570    may intercept requests to delegate links to other functions.
1571
1572    This sample QML application loads a web page, responds to session
1573    history context, and intercepts requests for external links:
1574
1575    \code
1576    import QtQuick 2.0
1577    import QtWebKit 3.0
1578
1579    Page {
1580        WebView {
1581            id: webview
1582            url: "http://qt-project.org"
1583            width: parent.width
1584            height: parent.height
1585            onNavigationRequested: {
1586                // detect URL scheme prefix, most likely an external link
1587                var schemaRE = /^\w+:/;
1588                if (schemaRE.test(request.url)) {
1589                    request.action = WebView.AcceptRequest;
1590                } else {
1591                    request.action = WebView.IgnoreRequest;
1592                    // delegate request.url here
1593                }
1594            }
1595        }
1596    }
1597    \endcode
1598
1599    \section1 Examples
1600
1601    There are several Qt WebKit examples located in the
1602    \l{Qt WebKit Examples} page.
1603
1604*/
1605
1606
1607/*!
1608    \qmltype WebView
1609    \instantiates QQuickWebView
1610    \inqmlmodule QtWebKit 3.0
1611    \brief A WebView renders web content within a QML application
1612*/
1613
1614QQuickWebView::QQuickWebView(QQuickItem* parent)
1615    : QQuickFlickable(parent)
1616    , d_ptr(createPrivateObject(this))
1617{
1618    Q_D(QQuickWebView);
1619    d->initialize();
1620}
1621
1622QQuickWebView::QQuickWebView(WKContextRef contextRef, WKPageGroupRef pageGroupRef, QQuickItem* parent)
1623    : QQuickFlickable(parent)
1624    , d_ptr(createPrivateObject(this))
1625{
1626    Q_D(QQuickWebView);
1627    d->initialize(contextRef, pageGroupRef);
1628}
1629
1630QQuickWebView::~QQuickWebView()
1631{
1632}
1633
1634QQuickWebPage* QQuickWebView::page()
1635{
1636    Q_D(QQuickWebView);
1637    return d->pageView.data();
1638}
1639
1640/*!
1641    \qmlmethod void WebView::goBack()
1642
1643    Go backward within the browser's session history, if possible.
1644    (Equivalent to the \c{window.history.back()} DOM method.)
1645
1646    \sa WebView::canGoBack
1647*/
1648void QQuickWebView::goBack()
1649{
1650    Q_D(QQuickWebView);
1651    WKPageGoBack(d->webPage.get());
1652}
1653
1654/*!
1655    \qmlmethod void WebView::goForward()
1656
1657    Go forward within the browser's session history, if possible.
1658    (Equivalent to the \c{window.history.forward()} DOM method.)
1659*/
1660void QQuickWebView::goForward()
1661{
1662    Q_D(QQuickWebView);
1663    WKPageGoForward(d->webPage.get());
1664}
1665
1666/*!
1667    \qmlmethod void WebView::stop()
1668
1669    Stop loading the current page.
1670*/
1671void QQuickWebView::stop()
1672{
1673    Q_D(QQuickWebView);
1674    WKPageStopLoading(d->webPage.get());
1675}
1676
1677/*!
1678    \qmlmethod void WebView::reload()
1679
1680    Reload the current page. (Equivalent to the
1681    \c{window.location.reload()} DOM method.)
1682*/
1683void QQuickWebView::reload()
1684{
1685    Q_D(QQuickWebView);
1686
1687    WebFrameProxy* mainFrame = d->webPageProxy->mainFrame();
1688    if (mainFrame && !mainFrame->unreachableURL().isEmpty() && mainFrame->url() != blankURL()) {
1689        // We are aware of the unreachable url on the UI process side, but since we haven't
1690        // loaded alternative/subsitute data for it (an error page eg.) WebCore doesn't know
1691        // about the unreachable url yet. If we just do a reload at this point WebCore will try to
1692        // reload the currently committed url instead of the unrachable url. To work around this
1693        // we override the reload here by doing a manual load.
1694        d->webPageProxy->loadURL(mainFrame->unreachableURL());
1695        // FIXME: We should make WebCore aware of the unreachable url regardless of substitute-loads
1696        return;
1697    }
1698
1699    WKPageReloadFromOrigin(d->webPage.get());
1700}
1701
1702/*!
1703    \qmlproperty url WebView::url
1704
1705    The location of the currently displaying HTML page. This writable
1706    property offers the main interface to load a page into a web view.
1707    It functions the same as the \c{window.location} DOM property.
1708
1709    \sa WebView::loadHtml()
1710*/
1711QUrl QQuickWebView::url() const
1712{
1713    Q_D(const QQuickWebView);
1714
1715    // FIXME: Enable once we are sure this should not trigger
1716    // Q_ASSERT(d->m_currentUrl == d->webPageProxy->activeURL());
1717
1718    return QUrl(d->m_currentUrl);
1719}
1720
1721void QQuickWebView::setUrl(const QUrl& url)
1722{
1723    Q_D(QQuickWebView);
1724
1725    if (url.isEmpty())
1726        return;
1727
1728    WKRetainPtr<WKURLRef> u = adoptWK(WKURLCreateWithQUrl(url));
1729    WKPageLoadURL(d->webPage.get(), u.get());
1730    emitUrlChangeIfNeeded();
1731}
1732
1733// Make sure we don't emit urlChanged unless it actually changed
1734void QQuickWebView::emitUrlChangeIfNeeded()
1735{
1736    Q_D(QQuickWebView);
1737
1738    QString activeUrl = d->webPageProxy->activeURL();
1739    if (activeUrl != d->m_currentUrl) {
1740        d->m_currentUrl = activeUrl;
1741        emit urlChanged();
1742    }
1743}
1744
1745/*!
1746    \qmlproperty url WebView::icon
1747
1748    The location of the currently displaying Web site icon, also known as favicon
1749    or shortcut icon. This read-only URL corresponds to the image used within a
1750    mobile browser application to represent a bookmarked page on the device's home
1751    screen.
1752
1753    This example uses the \c{icon} property to build an \c{Image} element:
1754
1755    \code
1756    Image {
1757        id: appIcon
1758        source: webView.icon != "" ? webView.icon : "fallbackFavIcon.png";
1759        ...
1760    }
1761    \endcode
1762*/
1763QUrl QQuickWebView::icon() const
1764{
1765    Q_D(const QQuickWebView);
1766    return d->m_iconUrl;
1767}
1768
1769/*!
1770    \qmlproperty int WebView::loadProgress
1771
1772    The amount of the page that has been loaded, expressed as an integer
1773    percentage in the range from \c{0} to \c{100}.
1774*/
1775int QQuickWebView::loadProgress() const
1776{
1777    Q_D(const QQuickWebView);
1778    return d->loadProgress();
1779}
1780
1781/*!
1782    \qmlproperty bool WebView::canGoBack
1783
1784    Returns \c{true} if there are prior session history entries, \c{false}
1785    otherwise.
1786*/
1787bool QQuickWebView::canGoBack() const
1788{
1789    Q_D(const QQuickWebView);
1790    return WKPageCanGoBack(d->webPage.get());
1791}
1792
1793/*!
1794    \qmlproperty bool WebView::canGoForward
1795
1796    Returns \c{true} if there are subsequent session history entries,
1797    \c{false} otherwise.
1798*/
1799bool QQuickWebView::canGoForward() const
1800{
1801    Q_D(const QQuickWebView);
1802    return WKPageCanGoForward(d->webPage.get());
1803}
1804
1805/*!
1806    \qmlproperty bool WebView::loading
1807
1808    Returns \c{true} if the HTML page is currently loading, \c{false} otherwise.
1809*/
1810bool QQuickWebView::loading() const
1811{
1812    Q_D(const QQuickWebView);
1813    WKFrameRef mainFrame = WKPageGetMainFrame(d->webPage.get());
1814    return mainFrame && !(kWKFrameLoadStateFinished == WKFrameGetFrameLoadState(mainFrame));
1815}
1816
1817/*!
1818    \internal
1819 */
1820
1821QPointF QQuickWebView::mapToWebContent(const QPointF& pointInViewCoordinates) const
1822{
1823    Q_D(const QQuickWebView);
1824    return d->pageView->transformFromItem().map(pointInViewCoordinates);
1825}
1826
1827/*!
1828    \internal
1829 */
1830
1831QRectF QQuickWebView::mapRectToWebContent(const QRectF& rectInViewCoordinates) const
1832{
1833    Q_D(const QQuickWebView);
1834    return d->pageView->transformFromItem().mapRect(rectInViewCoordinates);
1835}
1836
1837/*!
1838    \internal
1839 */
1840
1841QPointF QQuickWebView::mapFromWebContent(const QPointF& pointInCSSCoordinates) const
1842{
1843    Q_D(const QQuickWebView);
1844    return d->pageView->transformToItem().map(pointInCSSCoordinates);
1845}
1846
1847/*!
1848    \internal
1849 */
1850QRectF QQuickWebView::mapRectFromWebContent(const QRectF& rectInCSSCoordinates) const
1851{
1852    Q_D(const QQuickWebView);
1853    return d->pageView->transformToItem().mapRect(rectInCSSCoordinates);
1854}
1855
1856/*!
1857    \qmlproperty string WebView::title
1858
1859    The title of the currently displaying HTML page, a read-only value
1860    that reflects the contents of the \c{<title>} tag.
1861*/
1862QString QQuickWebView::title() const
1863{
1864    Q_D(const QQuickWebView);
1865    WKRetainPtr<WKStringRef> t = adoptWK(WKPageCopyTitle(d->webPage.get()));
1866    return WKStringCopyQString(t.get());
1867}
1868
1869QVariant QQuickWebView::inputMethodQuery(Qt::InputMethodQuery property) const
1870{
1871    Q_D(const QQuickWebView);
1872    const EditorState& state = d->webPageProxy->editorState();
1873
1874    switch(property) {
1875    case Qt::ImCursorRectangle:
1876        return QRectF(state.cursorRect);
1877    case Qt::ImFont:
1878        return QVariant();
1879    case Qt::ImCursorPosition:
1880        return QVariant(static_cast<int>(state.cursorPosition));
1881    case Qt::ImAnchorPosition:
1882        return QVariant(static_cast<int>(state.anchorPosition));
1883    case Qt::ImSurroundingText:
1884        return QString(state.surroundingText);
1885    case Qt::ImCurrentSelection:
1886        return QString(state.selectedText);
1887    case Qt::ImMaximumTextLength:
1888        return QVariant(); // No limit.
1889    case Qt::ImHints:
1890        return int(Qt::InputMethodHints(state.inputMethodHints));
1891    default:
1892        // Rely on the base implementation for ImEnabled, ImHints and ImPreferredLanguage.
1893        return QQuickFlickable::inputMethodQuery(property);
1894    }
1895}
1896
1897/*!
1898    internal
1899
1900    The experimental module consisting on experimental API which will break
1901    from version to version.
1902*/
1903QQuickWebViewExperimental* QQuickWebView::experimental() const
1904{
1905    Q_D(const QQuickWebView);
1906    return d->experimental;
1907}
1908
1909/*!
1910    \internal
1911*/
1912void QQuickWebView::platformInitialize()
1913{
1914    JSC::initializeThreading();
1915    WTF::initializeMainThread();
1916}
1917
1918bool QQuickWebView::childMouseEventFilter(QQuickItem* item, QEvent* event)
1919{
1920    if (!isVisible() || !isEnabled())
1921        return false;
1922
1923    // This function is used by MultiPointTouchArea and PinchArea to filter
1924    // touch events, thus to hinder the canvas from sending synthesized
1925    // mouse events to the Flickable implementation we need to reimplement
1926    // childMouseEventFilter to ignore touch and mouse events.
1927
1928    switch (event->type()) {
1929    case QEvent::MouseButtonPress:
1930    case QEvent::MouseMove:
1931    case QEvent::MouseButtonRelease:
1932    case QEvent::TouchBegin:
1933    case QEvent::TouchUpdate:
1934    case QEvent::TouchEnd:
1935        // Force all mouse and touch events through the default propagation path.
1936        return false;
1937    default:
1938        ASSERT(event->type() == QEvent::UngrabMouse);
1939        break;
1940    }
1941
1942    return QQuickFlickable::childMouseEventFilter(item, event);
1943}
1944
1945void QQuickWebView::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
1946{
1947    Q_D(QQuickWebView);
1948    QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
1949    if (newGeometry.size() != oldGeometry.size())
1950        d->updateViewportSize();
1951}
1952
1953void QQuickWebView::componentComplete()
1954{
1955    Q_D(QQuickWebView);
1956    QQuickFlickable::componentComplete();
1957
1958    d->onComponentComplete();
1959    d->updateViewportSize();
1960}
1961
1962void QQuickWebView::keyPressEvent(QKeyEvent* event)
1963{
1964    Q_D(QQuickWebView);
1965    d->pageEventHandler->handleKeyPressEvent(event);
1966}
1967
1968void QQuickWebView::keyReleaseEvent(QKeyEvent* event)
1969{
1970    Q_D(QQuickWebView);
1971    d->pageEventHandler->handleKeyReleaseEvent(event);
1972}
1973
1974void QQuickWebView::inputMethodEvent(QInputMethodEvent* event)
1975{
1976    Q_D(QQuickWebView);
1977    d->pageEventHandler->handleInputMethodEvent(event);
1978}
1979
1980void QQuickWebView::focusInEvent(QFocusEvent* event)
1981{
1982    Q_D(QQuickWebView);
1983    d->pageEventHandler->handleFocusInEvent(event);
1984}
1985
1986void QQuickWebView::itemChange(ItemChange change, const ItemChangeData &value)
1987{
1988    Q_D(QQuickWebView);
1989    if (change == ItemActiveFocusHasChanged) {
1990        bool focus = value.boolValue;
1991        if (!focus)
1992            d->pageEventHandler->handleFocusLost();
1993    }
1994    QQuickFlickable::itemChange(change, value);
1995}
1996
1997void QQuickWebView::touchEvent(QTouchEvent* event)
1998{
1999    Q_D(QQuickWebView);
2000
2001    bool lockingDisabled = flickableDirection() != AutoFlickDirection
2002                           || event->touchPoints().size() != 1
2003                           || width() >= contentWidth()
2004                           || height() >= contentHeight();
2005
2006    if (!lockingDisabled)
2007        d->axisLocker.update(event);
2008    else
2009        d->axisLocker.reset();
2010
2011    forceActiveFocus();
2012    d->pageEventHandler->handleTouchEvent(event);
2013}
2014
2015void QQuickWebView::mousePressEvent(QMouseEvent* event)
2016{
2017    Q_D(QQuickWebView);
2018    forceActiveFocus();
2019    d->handleMouseEvent(event);
2020}
2021
2022void QQuickWebView::mouseMoveEvent(QMouseEvent* event)
2023{
2024    Q_D(QQuickWebView);
2025    d->handleMouseEvent(event);
2026}
2027
2028void QQuickWebView::mouseReleaseEvent(QMouseEvent* event)
2029{
2030    Q_D(QQuickWebView);
2031    d->handleMouseEvent(event);
2032}
2033
2034void QQuickWebView::mouseDoubleClickEvent(QMouseEvent* event)
2035{
2036    Q_D(QQuickWebView);
2037    forceActiveFocus();
2038    d->handleMouseEvent(event);
2039}
2040
2041void QQuickWebView::wheelEvent(QWheelEvent* event)
2042{
2043    Q_D(QQuickWebView);
2044    d->pageEventHandler->handleWheelEvent(event);
2045}
2046
2047void QQuickWebView::hoverEnterEvent(QHoverEvent* event)
2048{
2049    Q_D(QQuickWebView);
2050    // Map HoverEnter to Move, for WebKit the distinction doesn't matter.
2051    d->pageEventHandler->handleHoverMoveEvent(event);
2052}
2053
2054void QQuickWebView::hoverMoveEvent(QHoverEvent* event)
2055{
2056    Q_D(QQuickWebView);
2057    d->pageEventHandler->handleHoverMoveEvent(event);
2058}
2059
2060void QQuickWebView::hoverLeaveEvent(QHoverEvent* event)
2061{
2062    Q_D(QQuickWebView);
2063    d->pageEventHandler->handleHoverLeaveEvent(event);
2064}
2065
2066void QQuickWebView::dragMoveEvent(QDragMoveEvent* event)
2067{
2068    Q_D(QQuickWebView);
2069    d->pageEventHandler->handleDragMoveEvent(event);
2070}
2071
2072void QQuickWebView::dragEnterEvent(QDragEnterEvent* event)
2073{
2074    Q_D(QQuickWebView);
2075    d->pageEventHandler->handleDragEnterEvent(event);
2076}
2077
2078void QQuickWebView::dragLeaveEvent(QDragLeaveEvent* event)
2079{
2080    Q_D(QQuickWebView);
2081    d->pageEventHandler->handleDragLeaveEvent(event);
2082}
2083
2084void QQuickWebView::dropEvent(QDropEvent* event)
2085{
2086    Q_D(QQuickWebView);
2087    d->pageEventHandler->handleDropEvent(event);
2088}
2089
2090bool QQuickWebView::event(QEvent* ev)
2091{
2092    // Re-implemented for possible future use without breaking binary compatibility.
2093    return QQuickFlickable::event(ev);
2094}
2095
2096WKPageRef QQuickWebView::pageRef() const
2097{
2098    Q_D(const QQuickWebView);
2099    return d->webPage.get();
2100}
2101
2102QPointF QQuickWebView::contentPos() const
2103{
2104    Q_D(const QQuickWebView);
2105    return d->contentPos();
2106}
2107
2108void QQuickWebView::setContentPos(const QPointF& pos)
2109{
2110    Q_D(QQuickWebView);
2111
2112    if (pos == contentPos())
2113        return;
2114
2115    d->setContentPos(pos);
2116}
2117
2118void QQuickWebView::handleFlickableMousePress(const QPointF& position, qint64 eventTimestampMillis)
2119{
2120    Q_D(QQuickWebView);
2121    d->axisLocker.setReferencePosition(position);
2122    QMouseEvent mouseEvent(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
2123    mouseEvent.setTimestamp(eventTimestampMillis);
2124    QQuickFlickable::mousePressEvent(&mouseEvent);
2125}
2126
2127void QQuickWebView::handleFlickableMouseMove(const QPointF& position, qint64 eventTimestampMillis)
2128{
2129    Q_D(QQuickWebView);
2130    QMouseEvent mouseEvent(QEvent::MouseMove, d->axisLocker.adjust(position), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
2131    mouseEvent.setTimestamp(eventTimestampMillis);
2132    QQuickFlickable::mouseMoveEvent(&mouseEvent);
2133}
2134
2135void QQuickWebView::handleFlickableMouseRelease(const QPointF& position, qint64 eventTimestampMillis)
2136{
2137    Q_D(QQuickWebView);
2138    QMouseEvent mouseEvent(QEvent::MouseButtonRelease, d->axisLocker.adjust(position), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
2139    d->axisLocker.reset();
2140    mouseEvent.setTimestamp(eventTimestampMillis);
2141    QQuickFlickable::mouseReleaseEvent(&mouseEvent);
2142}
2143
2144/*!
2145    \qmlmethod void WebView::loadHtml(string html, url baseUrl, url unreachableUrl)
2146    \brief Loads the specified \a html as the content of the web view.
2147
2148    (This method offers a lower-level alternative to the \c{url} property,
2149    which references HTML pages via URL.)
2150
2151    External objects such as stylesheets or images referenced in the HTML
2152    document are located relative to \a baseUrl. For example if provided \a html
2153    was originally retrieved from \c http://www.example.com/documents/overview.html
2154    and that was the base url, then an image referenced with the relative url \c diagram.png
2155    would be looked for at \c{http://www.example.com/documents/diagram.png}.
2156
2157    If an \a unreachableUrl is passed it is used as the url for the loaded
2158    content. This is typically used to display error pages for a failed
2159    load.
2160
2161    \sa WebView::url
2162*/
2163void QQuickWebView::loadHtml(const QString& html, const QUrl& baseUrl, const QUrl& unreachableUrl)
2164{
2165    Q_D(QQuickWebView);
2166    WKRetainPtr<WKStringRef> htmlRef = adoptWK(WKStringCreateWithQString(html));
2167    WKRetainPtr<WKURLRef> baseUrlRef = adoptWK(WKURLCreateWithQUrl(baseUrl));
2168    WKRetainPtr<WKURLRef> unreachableUrlRef = adoptWK(WKURLCreateWithQUrl(unreachableUrl));
2169
2170    if (unreachableUrl.isValid())
2171        WKPageLoadAlternateHTMLString(d->webPage.get(), htmlRef.get(), baseUrlRef.get(), unreachableUrlRef.get());
2172    else
2173        WKPageLoadHTMLString(d->webPage.get(), htmlRef.get(), baseUrlRef.get());
2174}
2175
2176qreal QQuickWebView::zoomFactor() const
2177{
2178    Q_D(const QQuickWebView);
2179    return d->zoomFactor();
2180}
2181
2182void QQuickWebView::setZoomFactor(qreal factor)
2183{
2184
2185    Q_D(QQuickWebView);
2186    d->setZoomFactor(factor);
2187}
2188
2189void QQuickWebView::runJavaScriptInMainFrame(const QString &script, QObject *receiver, const char *method)
2190{
2191    Q_D(QQuickWebView);
2192
2193    JSCallbackClosure* closure = new JSCallbackClosure;
2194    closure->receiver = receiver;
2195    closure->method = method;
2196
2197    WKRetainPtr<WKStringRef> scriptString = adoptWK(WKStringCreateWithQString(script));
2198    WKPageRunJavaScriptInMainFrame(d->webPage.get(), scriptString.get(), closure, javaScriptCallback);
2199}
2200
2201bool QQuickWebView::allowAnyHTTPSCertificateForLocalHost() const
2202{
2203    Q_D(const QQuickWebView);
2204    return d->m_allowAnyHTTPSCertificateForLocalHost;
2205}
2206
2207void QQuickWebView::setAllowAnyHTTPSCertificateForLocalHost(bool allow)
2208{
2209    Q_D(QQuickWebView);
2210    d->m_allowAnyHTTPSCertificateForLocalHost = allow;
2211}
2212
2213void QQuickWebViewPrivate::didFindString(WKPageRef, WKStringRef, unsigned matchCount, const void* clientInfo)
2214{
2215    QQuickWebView* q = toQQuickWebViewPrivate(clientInfo)->q_ptr;
2216    emit q->experimental()->textFound(matchCount);
2217}
2218
2219void QQuickWebViewPrivate::didFailToFindString(WKPageRef page, WKStringRef string, const void* clientInfo)
2220{
2221    QQuickWebViewPrivate::didFindString(page, string, 0, clientInfo);
2222}
2223
2224/*!
2225    \qmlsignal WebView::onLoadingChanged(loadRequest)
2226
2227    Occurs when any page load begins, ends, or fails. Various read-only
2228    parameters are available on the \a loadRequest:
2229
2230    \list
2231
2232    \li \c{url}: the location of the resource that is loading.
2233
2234    \li \c{status}: Reflects one of three load states:
2235       \c{LoadStartedStatus}, \c{LoadSucceededStatus}, or
2236       \c{LoadFailedStatus}. See \c{WebView::LoadStatus}.
2237
2238    \li \c{errorString}: description of load error.
2239
2240    \li \c{errorCode}: HTTP error code.
2241
2242    \li \c{errorDomain}: high-level error types, one of
2243    \c{NetworkErrorDomain}, \c{HttpErrorDomain}, \c{InternalErrorDomain},
2244    \c{DownloadErrorDomain}, or \c{NoErrorDomain}.  See
2245    \l{WebView::ErrorDomain}.
2246
2247    \endlist
2248
2249    \sa WebView::loading
2250*/
2251
2252/*!
2253    \qmlsignal WebView::onLinkHovered(hoveredUrl, hoveredTitle)
2254
2255    Within a mouse-driven interface, this signal is emitted when a mouse
2256    pointer passes over a link, corresponding to the \c{mouseover} DOM
2257    event.  (May also occur in touch interfaces for \c{mouseover} events
2258    that are not cancelled with \c{preventDefault()}.)  The \a{hoveredUrl}
2259    provides the link's location, and the \a{hoveredTitle} is any avalable
2260    link text.
2261*/
2262
2263/*!
2264    \qmlsignal WebView::onNavigationRequested(request)
2265
2266    Occurs for various kinds of navigation.  If the application listens
2267    for this signal, it must set the \c{request.action} to either of the
2268    following \l{WebView::NavigationRequestAction} enum values:
2269
2270    \list
2271
2272    \li \c{AcceptRequest}: Allow navigation to external pages within the
2273    web view. This represents the default behavior when no listener is
2274    active.
2275
2276    \li \c{IgnoreRequest}: Suppress navigation to new pages within the web
2277    view.  (The listener may then delegate navigation externally to
2278    the browser application.)
2279
2280    \endlist
2281
2282    The \a{request} also provides the following read-only values:
2283
2284    \list
2285
2286    \li \c{url}: The location of the requested page.
2287
2288    \li \c{navigationType}: contextual information, one of
2289    \c{LinkClickedNavigation}, \c{BackForwardNavigation},
2290    \c{ReloadNavigation}, \c{FormSubmittedNavigation},
2291    \c{FormResubmittedNavigation}, or \c{OtherNavigation} enum values.
2292    See \l{WebView::NavigationType}.
2293
2294    \li \c{keyboardModifiers}: potential states for \l{Qt::KeyboardModifier}.
2295
2296    \li \c{mouseButton}: potential states for \l{Qt::MouseButton}.
2297
2298    \endlist
2299*/
2300
2301/*!
2302    \qmlproperty enumeration WebView::ErrorDomain
2303
2304    Details various high-level error types.
2305
2306    \table
2307
2308    \header
2309    \li Constant
2310    \li Description
2311
2312    \row
2313    \li InternalErrorDomain
2314    \li Content fails to be interpreted by Qt WebKit.
2315
2316    \row
2317    \li NetworkErrorDomain
2318    \li Error results from faulty network connection.
2319
2320    \row
2321    \li HttpErrorDomain
2322    \li Error is produced by server.
2323
2324    \row
2325    \li DownloadErrorDomain
2326    \li Error in saving file.
2327
2328    \row
2329    \li NoErrorDomain
2330    \li Unspecified fallback error.
2331
2332    \endtable
2333*/
2334
2335/*!
2336    \qmlproperty enumeration WebView::NavigationType
2337
2338    Distinguishes context for various navigation actions.
2339
2340    \table
2341
2342    \header
2343    \li Constant
2344    \li Description
2345
2346    \row
2347    \li LinkClickedNavigation
2348    \li Navigation via link.
2349
2350    \row
2351    \li FormSubmittedNavigation
2352    \li Form data is posted.
2353
2354    \row
2355    \li BackForwardNavigation
2356    \li Navigation back and forth within session history.
2357
2358    \row
2359    \li ReloadNavigation
2360    \li The current page is reloaded.
2361
2362    \row
2363    \li FormResubmittedNavigation
2364    \li Form data is re-posted.
2365
2366    \row
2367    \li OtherNavigation
2368    \li Unspecified fallback method of navigation.
2369
2370    \endtable
2371*/
2372
2373/*!
2374    \qmlproperty enumeration WebView::LoadStatus
2375
2376    Reflects a page's load status.
2377
2378    \table
2379
2380    \header
2381    \li Constant
2382    \li Description
2383
2384    \row
2385    \li LoadStartedStatus
2386    \li Page is currently loading.
2387
2388    \row
2389    \li LoadSucceededStatus
2390    \li Page has successfully loaded, and is not currently loading.
2391
2392    \row
2393    \li LoadFailedStatus
2394    \li Page has failed to load, and is not currently loading.
2395
2396    \endtable
2397*/
2398
2399/*!
2400    \qmlproperty enumeration WebView::NavigationRequestAction
2401
2402    Specifies a policy when navigating a link to an external page.
2403
2404    \table
2405
2406    \header
2407    \li Constant
2408    \li Description
2409
2410    \row
2411    \li AcceptRequest
2412    \li Allow navigation to external pages within the web view.
2413
2414    \row
2415    \li IgnoreRequest
2416    \li Suppress navigation to new pages within the web view.
2417
2418    \endtable
2419*/
2420
2421#include "moc_qquickwebview_p.cpp"
2422