[Qt] [WK2] Allow user to inject JS scripts when the page loads
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / qt / qquickwebview.cpp
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 "qquickwebview_p.h"
23
24 #include "DownloadProxy.h"
25 #include "DrawingAreaProxyImpl.h"
26 #include "QtDialogRunner.h"
27 #include "QtDownloadManager.h"
28 #include "QtViewportInteractionEngine.h"
29 #include "QtWebContext.h"
30 #include "QtWebError.h"
31 #include "QtWebIconDatabaseClient.h"
32 #include "QtWebPageEventHandler.h"
33 #include "QtWebPageLoadClient.h"
34 #include "QtWebPagePolicyClient.h"
35 #include "UtilsQt.h"
36 #include "WebBackForwardList.h"
37 #if ENABLE(FULLSCREEN_API)
38 #include "WebFullScreenManagerProxy.h"
39 #endif
40 #include "WebPageGroup.h"
41 #include "WebPreferences.h"
42 #include "qquicknetworkreply_p.h"
43 #include "qquicknetworkrequest_p.h"
44 #include "qquickwebpage_p_p.h"
45 #include "qquickwebview_p_p.h"
46 #include "qwebdownloaditem_p_p.h"
47 #include "qwebkittest_p.h"
48 #include "qwebloadrequest_p.h"
49 #include "qwebnavigationhistory_p.h"
50 #include "qwebnavigationhistory_p_p.h"
51 #include "qwebpreferences_p.h"
52 #include "qwebpreferences_p_p.h"
53 #include <JavaScriptCore/InitializeThreading.h>
54 #include <JavaScriptCore/JSBase.h>
55 #include <JavaScriptCore/JSRetainPtr.h>
56 #include <QDateTime>
57 #include <QtCore/QFile>
58 #include <QtQml/QJSValue>
59 #include <WKOpenPanelResultListener.h>
60 #include <WKSerializedScriptValue.h>
61 #include <WebCore/IntPoint.h>
62 #include <WebCore/IntRect.h>
63 #include <wtf/Assertions.h>
64 #include <wtf/MainThread.h>
65 #include <wtf/Vector.h>
66 #include <wtf/text/WTFString.h>
67
68 using namespace WebCore;
69 using namespace WebKit;
70
71 static bool s_flickableViewportEnabled = true;
72 static const int kAxisLockSampleCount = 5;
73 static const qreal kAxisLockVelocityThreshold = 300;
74 static const qreal kAxisLockVelocityDirectionThreshold = 50;
75
76 struct JSCallbackClosure {
77     QPointer<QObject> receiver;
78     QByteArray method;
79     QJSValue value;
80 };
81
82 static inline QString toQString(JSStringRef string)
83 {
84     return QString(reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(string)), JSStringGetLength(string));
85 }
86
87 static inline QJSValue toQJSValue(JSStringRef string)
88 {
89     return QJSValue(toQString(string));
90 }
91
92 static QJSValue buildQJSValue(QJSEngine* engine, JSGlobalContextRef context, JSValueRef value, int depth)
93 {
94     QJSValue var;
95     JSValueRef exception = 0;
96
97     if (depth > 10)
98         return var;
99
100     switch (JSValueGetType(context, value)) {
101     case kJSTypeBoolean:
102         var = QJSValue(JSValueToBoolean(context, value));
103         break;
104     case kJSTypeNumber:
105         {
106             double number = JSValueToNumber(context, value, &exception);
107             if (!exception)
108                 var = QJSValue(number);
109         }
110         break;
111     case kJSTypeString:
112         {
113             JSRetainPtr<JSStringRef> string = JSValueToStringCopy(context, value, &exception);
114             if (!exception)
115                 var = toQJSValue(string.get());
116         }
117         break;
118     case kJSTypeObject:
119         {
120             JSObjectRef obj = JSValueToObject(context, value, &exception);
121
122             JSPropertyNameArrayRef names = JSObjectCopyPropertyNames(context, obj);
123             size_t length = JSPropertyNameArrayGetCount(names);
124
125             var = engine->newObject();
126
127             for (size_t i = 0; i < length; ++i) {
128                 JSRetainPtr<JSStringRef> name = JSPropertyNameArrayGetNameAtIndex(names, i);
129                 JSValueRef property = JSObjectGetProperty(context, obj, name.get(), &exception);
130
131                 if (!exception) {
132                     QJSValue value = buildQJSValue(engine, context, property, depth + 1);
133                     var.setProperty(toQString(name.get()), value);
134                 }
135             }
136         }
137         break;
138     }
139     return var;
140 }
141
142 static void javaScriptCallback(WKSerializedScriptValueRef valueRef, WKErrorRef, void* data)
143 {
144     JSCallbackClosure* closure = reinterpret_cast<JSCallbackClosure*>(data);
145
146     if (closure->method.size())
147         QMetaObject::invokeMethod(closure->receiver, closure->method);
148     else {
149         QJSValue function = closure->value;
150
151         // If a callable function is supplied, we build a JavaScript value accessible
152         // in the QML engine, and calls the function with that.
153         if (function.isCallable()) {
154             QJSValue var;
155             if (valueRef) {
156                 // FIXME: Slow but OK for now.
157                 JSGlobalContextRef context = JSGlobalContextCreate(0);
158
159                 JSValueRef exception = 0;
160                 JSValueRef value = WKSerializedScriptValueDeserialize(valueRef, context, &exception);
161                 var = buildQJSValue(function.engine(), context, value, /* depth */ 0);
162
163                 JSGlobalContextRelease(context);
164             }
165
166             QList<QJSValue> args;
167             args.append(var);
168             function.call(args);
169         }
170     }
171
172     delete closure;
173 }
174
175 static QQuickWebViewPrivate* createPrivateObject(QQuickWebView* publicObject)
176 {
177     if (s_flickableViewportEnabled)
178         return new QQuickWebViewFlickablePrivate(publicObject);
179     return new QQuickWebViewLegacyPrivate(publicObject);
180 }
181
182 QQuickWebViewPrivate::FlickableAxisLocker::FlickableAxisLocker()
183     : m_allowedDirection(QQuickFlickable::AutoFlickDirection)
184     , m_sampleCount(0)
185 {
186 }
187
188 QVector2D QQuickWebViewPrivate::FlickableAxisLocker::touchVelocity(const QTouchEvent* event)
189 {
190     static bool touchVelocityAvailable = event->device()->capabilities().testFlag(QTouchDevice::Velocity);
191     const QTouchEvent::TouchPoint& touchPoint = event->touchPoints().first();
192
193     if (touchVelocityAvailable)
194         return touchPoint.velocity();
195
196     const QLineF movementLine(touchPoint.screenPos(), m_initialScreenPosition);
197     const qint64 elapsed = m_time.elapsed();
198
199     if (!elapsed)
200         return QVector2D(0, 0);
201
202     // Calculate an approximate velocity vector in the unit of pixel / second.
203     return QVector2D(1000 * movementLine.dx() / elapsed, 1000 * movementLine.dy() / elapsed);
204 }
205
206 void QQuickWebViewPrivate::FlickableAxisLocker::update(const QTouchEvent* event)
207 {
208     ASSERT(event->touchPoints().size() == 1);
209     const QTouchEvent::TouchPoint& touchPoint = event->touchPoints().first();
210
211     ++m_sampleCount;
212
213     if (m_sampleCount == 1) {
214         m_initialScreenPosition = touchPoint.screenPos();
215         m_time.restart();
216         return;
217     }
218
219     if (m_sampleCount > kAxisLockSampleCount
220             || m_allowedDirection == QQuickFlickable::HorizontalFlick
221             || m_allowedDirection == QQuickFlickable::VerticalFlick)
222         return;
223
224     QVector2D velocity = touchVelocity(event);
225
226     qreal directionIndicator = qAbs(velocity.x()) - qAbs(velocity.y());
227
228     if (velocity.length() > kAxisLockVelocityThreshold && qAbs(directionIndicator) > kAxisLockVelocityDirectionThreshold)
229         m_allowedDirection = (directionIndicator > 0) ? QQuickFlickable::HorizontalFlick : QQuickFlickable::VerticalFlick;
230 }
231
232 void QQuickWebViewPrivate::FlickableAxisLocker::setReferencePosition(const QPointF& position)
233 {
234     m_lockReferencePosition = position;
235 }
236
237 void QQuickWebViewPrivate::FlickableAxisLocker::reset()
238 {
239     m_allowedDirection = QQuickFlickable::AutoFlickDirection;
240     m_sampleCount = 0;
241 }
242
243 QPointF QQuickWebViewPrivate::FlickableAxisLocker::adjust(const QPointF& position)
244 {
245     if (m_allowedDirection == QQuickFlickable::HorizontalFlick)
246         return QPointF(position.x(), m_lockReferencePosition.y());
247
248     if (m_allowedDirection == QQuickFlickable::VerticalFlick)
249         return QPointF(m_lockReferencePosition.x(), position.y());
250
251     return position;
252 }
253
254 QQuickWebViewPrivate::QQuickWebViewPrivate(QQuickWebView* viewport)
255     : q_ptr(viewport)
256     , alertDialog(0)
257     , confirmDialog(0)
258     , promptDialog(0)
259     , authenticationDialog(0)
260     , certificateVerificationDialog(0)
261     , itemSelector(0)
262     , proxyAuthenticationDialog(0)
263     , filePicker(0)
264     , databaseQuotaDialog(0)
265     , m_useDefaultContentItemSize(true)
266     , m_navigatorQtObjectEnabled(false)
267     , m_renderToOffscreenBuffer(false)
268     , m_dialogActive(false)
269     , m_allowAnyHTTPSCertificateForLocalHost(false)
270     , m_loadProgress(0)
271 {
272     viewport->setClip(true);
273     viewport->setPixelAligned(true);
274     QObject::connect(viewport, SIGNAL(visibleChanged()), viewport, SLOT(_q_onVisibleChanged()));
275     QObject::connect(viewport, SIGNAL(urlChanged()), viewport, SLOT(_q_onUrlChanged()));
276     pageView.reset(new QQuickWebPage(viewport));
277 }
278
279 QQuickWebViewPrivate::~QQuickWebViewPrivate()
280 {
281     webPageProxy->close();
282 }
283
284 // Note: we delay this initialization to make sure that QQuickWebView has its d-ptr in-place.
285 void QQuickWebViewPrivate::initialize(WKContextRef contextRef, WKPageGroupRef pageGroupRef)
286 {
287     RefPtr<WebPageGroup> pageGroup;
288     if (pageGroupRef)
289         pageGroup = toImpl(pageGroupRef);
290     else
291         pageGroup = WebPageGroup::create();
292
293     context = contextRef ? QtWebContext::create(toImpl(contextRef)) : QtWebContext::defaultContext();
294     webPageProxy = context->createWebPage(&pageClient, pageGroup.get());
295 #if ENABLE(FULLSCREEN_API)
296     webPageProxy->fullScreenManager()->setWebView(q_ptr);
297 #endif
298
299     QQuickWebPagePrivate* const pageViewPrivate = pageView.data()->d;
300     pageViewPrivate->initialize(webPageProxy.get());
301
302     pageLoadClient.reset(new QtWebPageLoadClient(toAPI(webPageProxy.get()), q_ptr));
303     pagePolicyClient.reset(new QtWebPagePolicyClient(toAPI(webPageProxy.get()), q_ptr));
304     pageUIClient.reset(new QtWebPageUIClient(toAPI(webPageProxy.get()), q_ptr));
305     navigationHistory = adoptPtr(QWebNavigationHistoryPrivate::createHistory(toAPI(webPageProxy.get())));
306
307     QtWebIconDatabaseClient* iconDatabase = context->iconDatabase();
308     QObject::connect(iconDatabase, SIGNAL(iconChangedForPageURL(QUrl, QUrl)), q_ptr, SLOT(_q_onIconChangedForPageURL(QUrl, QUrl)));
309
310     // Any page setting should preferrable be set before creating the page.
311     webPageProxy->pageGroup()->preferences()->setAcceleratedCompositingEnabled(true);
312     webPageProxy->pageGroup()->preferences()->setForceCompositingMode(true);
313     webPageProxy->pageGroup()->preferences()->setFrameFlatteningEnabled(true);
314
315     pageClient.initialize(q_ptr, pageViewPrivate->eventHandler.data(), &undoController);
316     webPageProxy->initializeWebPage();
317 }
318
319 void QQuickWebViewPrivate::setTransparentBackground(bool enable)
320 {
321     webPageProxy->setDrawsTransparentBackground(enable);
322 }
323
324 bool QQuickWebViewPrivate::transparentBackground() const
325 {
326     return webPageProxy->drawsTransparentBackground();
327 }
328
329 /*!
330     \qmlsignal WebView::loadingChanged(WebLoadRequest request)
331 */
332
333 void QQuickWebViewPrivate::provisionalLoadDidStart(const WTF::String& url)
334 {
335     Q_Q(QQuickWebView);
336
337     q->emitUrlChangeIfNeeded();
338
339     QWebLoadRequest loadRequest(QString(url), QQuickWebView::LoadStartedStatus);
340     emit q->loadingChanged(&loadRequest);
341 }
342
343 void QQuickWebViewPrivate::didReceiveServerRedirectForProvisionalLoad(const WTF::String&)
344 {
345     Q_Q(QQuickWebView);
346
347     q->emitUrlChangeIfNeeded();
348 }
349
350 void QQuickWebViewPrivate::loadDidCommit()
351 {
352     Q_Q(QQuickWebView);
353     ASSERT(q->loading());
354
355     emit q->navigationHistoryChanged();
356     emit q->titleChanged();
357 }
358
359 void QQuickWebViewPrivate::didSameDocumentNavigation()
360 {
361     Q_Q(QQuickWebView);
362
363     q->emitUrlChangeIfNeeded();
364     emit q->navigationHistoryChanged();
365 }
366
367 void QQuickWebViewPrivate::titleDidChange()
368 {
369     Q_Q(QQuickWebView);
370
371     emit q->titleChanged();
372 }
373
374 void QQuickWebViewPrivate::loadProgressDidChange(int loadProgress)
375 {
376     Q_Q(QQuickWebView);
377
378     if (!loadProgress)
379         setIcon(QUrl());
380
381     m_loadProgress = loadProgress;
382
383     emit q->loadProgressChanged();
384 }
385
386 void QQuickWebViewPrivate::backForwardListDidChange()
387 {
388     navigationHistory->d->reset();
389 }
390
391 void QQuickWebViewPrivate::loadDidSucceed()
392 {
393     Q_Q(QQuickWebView);
394     ASSERT(!q->loading());
395
396     QWebLoadRequest loadRequest(q->url(), QQuickWebView::LoadSucceededStatus);
397     emit q->loadingChanged(&loadRequest);
398 }
399
400 void QQuickWebViewPrivate::loadDidFail(const QtWebError& error)
401 {
402     Q_Q(QQuickWebView);
403     ASSERT(!q->loading());
404
405     QWebLoadRequest loadRequest(error.url(), QQuickWebView::LoadFailedStatus, error.description(), static_cast<QQuickWebView::ErrorDomain>(error.type()), error.errorCode());
406     emit q->loadingChanged(&loadRequest);
407 }
408
409 void QQuickWebViewPrivate::setNeedsDisplay()
410 {
411     Q_Q(QQuickWebView);
412     if (renderToOffscreenBuffer()) {
413         // TODO: we can maintain a real image here and use it for pixel tests. Right now this is used only for running the rendering code-path while running tests.
414         QImage dummyImage(1, 1, QImage::Format_ARGB32);
415         QPainter painter(&dummyImage);
416         q->page()->d->paint(&painter);
417         return;
418     }
419
420     q->page()->update();
421 }
422
423 void QQuickWebViewPrivate::_q_onIconChangedForPageURL(const QUrl& pageURL, const QUrl& iconURL)
424 {
425     Q_Q(QQuickWebView);
426
427     if (q->url() != pageURL)
428         return;
429
430     setIcon(iconURL);
431 }
432
433 void QQuickWebViewPrivate::processDidCrash()
434 {
435     Q_Q(QQuickWebView);
436
437     QUrl url(KURL(WebCore::ParsedURLString, webPageProxy->urlAtProcessExit()));
438     qWarning("WARNING: The web process experienced a crash on '%s'.", qPrintable(url.toString(QUrl::RemoveUserInfo)));
439
440     pageView->eventHandler()->resetGestureRecognizers();
441
442     // Check if loading was ongoing, when process crashed.
443     if (m_loadProgress > 0 && m_loadProgress < 100) {
444         QWebLoadRequest loadRequest(url, QQuickWebView::LoadFailedStatus, QLatin1String("The web process crashed."), QQuickWebView::InternalErrorDomain, 0);
445
446         loadProgressDidChange(100);
447         emit q->loadingChanged(&loadRequest);
448     }
449 }
450
451 void QQuickWebViewPrivate::didRelaunchProcess()
452 {
453     qWarning("WARNING: The web process has been successfully restarted.");
454
455     webPageProxy->drawingArea()->setSize(viewSize(), IntSize());
456     updateViewportSize();
457     updateUserScripts();
458 }
459
460 PassOwnPtr<DrawingAreaProxy> QQuickWebViewPrivate::createDrawingAreaProxy()
461 {
462     return DrawingAreaProxyImpl::create(webPageProxy.get());
463 }
464
465 void QQuickWebViewPrivate::handleDownloadRequest(DownloadProxy* download)
466 {
467     Q_Q(QQuickWebView);
468     // This function is responsible for hooking up a DownloadProxy to our API layer
469     // by creating a QWebDownloadItem. It will then wait for the QWebDownloadItem to be
470     // ready (filled with the ResourceResponse information) so we can pass it through to
471     // our WebViews.
472     QWebDownloadItem* downloadItem = new QWebDownloadItem();
473     downloadItem->d->downloadProxy = download;
474
475     q->connect(downloadItem->d, SIGNAL(receivedResponse(QWebDownloadItem*)), q, SLOT(_q_onReceivedResponseFromDownload(QWebDownloadItem*)));
476     context->downloadManager()->addDownload(download, downloadItem);
477 }
478
479 void QQuickWebViewPrivate::_q_onVisibleChanged()
480 {
481     webPageProxy->viewStateDidChange(WebPageProxy::ViewIsVisible);
482 }
483
484 void QQuickWebViewPrivate::_q_onUrlChanged()
485 {
486     Q_Q(QQuickWebView);
487     context->iconDatabase()->requestIconForPageURL(q->url());
488 }
489
490 void QQuickWebViewPrivate::_q_onReceivedResponseFromDownload(QWebDownloadItem* downloadItem)
491 {
492     // Now that our downloadItem has everything we need we can emit downloadRequested.
493     if (!downloadItem)
494         return;
495
496     Q_Q(QQuickWebView);
497     QQmlEngine::setObjectOwnership(downloadItem, QQmlEngine::JavaScriptOwnership);
498     emit q->experimental()->downloadRequested(downloadItem);
499 }
500
501 void QQuickWebViewPrivate::runJavaScriptAlert(const QString& alertText)
502 {
503     Q_Q(QQuickWebView);
504     QtDialogRunner dialogRunner(q);
505     if (!dialogRunner.initForAlert(alertText))
506         return;
507
508     dialogRunner.run();
509 }
510
511 bool QQuickWebViewPrivate::runJavaScriptConfirm(const QString& message)
512 {
513     Q_Q(QQuickWebView);
514     QtDialogRunner dialogRunner(q);
515     if (!dialogRunner.initForConfirm(message))
516         return true;
517
518     dialogRunner.run();
519
520     return dialogRunner.wasAccepted();
521 }
522
523 QString QQuickWebViewPrivate::runJavaScriptPrompt(const QString& message, const QString& defaultValue, bool& ok)
524 {
525     Q_Q(QQuickWebView);
526     QtDialogRunner dialogRunner(q);
527     if (!dialogRunner.initForPrompt(message, defaultValue)) {
528         ok = true;
529         return defaultValue;
530     }
531
532     dialogRunner.run();
533
534     ok = dialogRunner.wasAccepted();
535     return dialogRunner.result();
536 }
537
538 void QQuickWebViewPrivate::handleAuthenticationRequiredRequest(const QString& hostname, const QString& realm, const QString& prefilledUsername, QString& username, QString& password)
539 {
540     Q_Q(QQuickWebView);
541     QtDialogRunner dialogRunner(q);
542     if (!dialogRunner.initForAuthentication(hostname, realm, prefilledUsername))
543         return;
544
545     dialogRunner.run();
546
547     username = dialogRunner.username();
548     password = dialogRunner.password();
549 }
550
551 void QQuickWebViewPrivate::handleProxyAuthenticationRequiredRequest(const QString& hostname, uint16_t port, const QString& prefilledUsername, QString& username, QString& password)
552 {
553     Q_Q(QQuickWebView);
554     QtDialogRunner dialogRunner(q);
555     if (!dialogRunner.initForProxyAuthentication(hostname, port, prefilledUsername))
556         return;
557
558     dialogRunner.run();
559
560     username = dialogRunner.username();
561     password = dialogRunner.password();
562 }
563
564 bool QQuickWebViewPrivate::handleCertificateVerificationRequest(const QString& hostname)
565 {
566     Q_Q(QQuickWebView);
567
568     if (m_allowAnyHTTPSCertificateForLocalHost
569         && (hostname == QStringLiteral("127.0.0.1") || hostname == QStringLiteral("localhost")))
570         return true;
571
572     QtDialogRunner dialogRunner(q);
573     if (!dialogRunner.initForCertificateVerification(hostname))
574         return false;
575
576     dialogRunner.run();
577
578     return dialogRunner.wasAccepted();
579 }
580
581 void QQuickWebViewPrivate::chooseFiles(WKOpenPanelResultListenerRef listenerRef, const QStringList& selectedFileNames, QtWebPageUIClient::FileChooserType type)
582 {
583     Q_Q(QQuickWebView);
584
585     QtDialogRunner dialogRunner(q);
586     if (!dialogRunner.initForFilePicker(selectedFileNames, (type == QtWebPageUIClient::MultipleFilesSelection)))
587         return;
588
589     dialogRunner.run();
590
591     if (dialogRunner.wasAccepted()) {
592         QStringList selectedPaths = dialogRunner.filePaths();
593
594         Vector<RefPtr<APIObject> > wkFiles(selectedPaths.size());
595         for (unsigned i = 0; i < selectedPaths.size(); ++i)
596             wkFiles[i] = WebURL::create(QUrl::fromLocalFile(selectedPaths.at(i)).toString());            
597
598         WKOpenPanelResultListenerChooseFiles(listenerRef, toAPI(ImmutableArray::adopt(wkFiles).leakRef()));
599     } else
600         WKOpenPanelResultListenerCancel(listenerRef);
601
602 }
603
604 quint64 QQuickWebViewPrivate::exceededDatabaseQuota(const QString& databaseName, const QString& displayName, WKSecurityOriginRef securityOrigin, quint64 currentQuota, quint64 currentOriginUsage, quint64 currentDatabaseUsage, quint64 expectedUsage)
605 {
606     Q_Q(QQuickWebView);
607     QtDialogRunner dialogRunner(q);
608     if (!dialogRunner.initForDatabaseQuotaDialog(databaseName, displayName, securityOrigin, currentQuota, currentOriginUsage, currentDatabaseUsage, expectedUsage))
609         return 0;
610
611     dialogRunner.run();
612
613     return dialogRunner.wasAccepted() ? dialogRunner.databaseQuota() : 0;
614 }
615
616 /* The 'WebView' attached property allows items spawned by the webView to
617    refer back to the originating webView through 'WebView.view', similar
618    to how ListView.view and GridView.view is exposed to items. */
619 QQuickWebViewAttached::QQuickWebViewAttached(QObject* object)
620     : QObject(object)
621     , m_view(0)
622 {
623 }
624
625 void QQuickWebViewAttached::setView(QQuickWebView* view)
626 {
627     if (m_view == view)
628         return;
629     m_view = view;
630     emit viewChanged();
631 }
632
633 QQuickWebViewAttached* QQuickWebView::qmlAttachedProperties(QObject* object)
634 {
635     return new QQuickWebViewAttached(object);
636 }
637
638
639
640 void QQuickWebViewPrivate::addAttachedPropertyTo(QObject* object)
641 {
642     Q_Q(QQuickWebView);
643     QQuickWebViewAttached* attached = static_cast<QQuickWebViewAttached*>(qmlAttachedPropertiesObject<QQuickWebView>(object));
644     attached->setView(q);
645 }
646
647 void QQuickWebViewPrivate::setIcon(const QUrl& iconURL)
648 {
649     Q_Q(QQuickWebView);
650     if (m_iconURL == iconURL)
651         return;
652
653     if (!webPageProxy->mainFrame())
654         return;
655
656     String oldPageURL = QUrl::fromPercentEncoding(m_iconURL.encodedFragment());
657     String newPageURL = webPageProxy->mainFrame()->url();
658
659     if (oldPageURL != newPageURL) {
660         QtWebIconDatabaseClient* iconDatabase = context->iconDatabase();
661         if (!oldPageURL.isEmpty())
662             iconDatabase->releaseIconForPageURL(oldPageURL);
663
664         if (!newPageURL.isEmpty())
665             iconDatabase->retainIconForPageURL(newPageURL);
666     }
667
668     m_iconURL = iconURL;
669     emit q->iconChanged();
670 }
671
672 bool QQuickWebViewPrivate::navigatorQtObjectEnabled() const
673 {
674     return m_navigatorQtObjectEnabled;
675 }
676
677 void QQuickWebViewPrivate::setNavigatorQtObjectEnabled(bool enabled)
678 {
679     ASSERT(enabled != m_navigatorQtObjectEnabled);
680     // FIXME: Currently we have to keep this information in both processes and the setting is asynchronous.
681     m_navigatorQtObjectEnabled = enabled;
682     context->setNavigatorQtObjectEnabled(webPageProxy.get(), enabled);
683 }
684
685 static QString readUserScript(const QUrl& url)
686 {
687     QString path;
688     if (url.isLocalFile())
689         path = url.toLocalFile();
690     else if (url.scheme() == QLatin1String("qrc"))
691         path = QStringLiteral(":") + url.path();
692     else {
693         qWarning("QQuickWebView: Couldn't open '%s' as user script because only file:/// and qrc:/// URLs are supported.", qPrintable(url.toString()));
694         return QString();
695     }
696
697     QFile file(path);
698     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
699         qWarning("QQuickWebView: Couldn't open '%s' as user script due to error '%s'.", qPrintable(url.toString()), qPrintable(file.errorString()));
700         return QString();
701     }
702
703     QString contents = QString::fromUtf8(file.readAll());
704     if (contents.isEmpty())
705         qWarning("QQuickWebView: Ignoring '%s' as user script because file is empty.", qPrintable(url.toString()));
706
707     return contents;
708 }
709
710 void QQuickWebViewPrivate::updateUserScripts()
711 {
712     Vector<String> scripts;
713     scripts.reserveCapacity(userScripts.size());
714
715     for (unsigned i = 0; i < userScripts.size(); ++i) {
716         const QUrl& url = userScripts.at(i);
717         if (!url.isValid()) {
718             qWarning("QQuickWebView: Couldn't open '%s' as user script because URL is invalid.", qPrintable(url.toString()));
719             continue;
720         }
721
722         QString contents = readUserScript(url);
723         if (contents.isEmpty())
724             continue;
725         scripts.append(String(contents));
726     }
727
728     webPageProxy->setUserScripts(scripts);
729 }
730
731 QPointF QQuickWebViewPrivate::contentPos() const
732 {
733     Q_Q(const QQuickWebView);
734     return QPointF(q->contentX(), q->contentY());
735 }
736
737 void QQuickWebViewPrivate::setContentPos(const QPointF& pos)
738 {
739     Q_Q(QQuickWebView);
740     q->setContentX(pos.x());
741     q->setContentY(pos.y());
742 }
743
744 QRect QQuickWebViewPrivate::visibleContentsRect() const
745 {
746     Q_Q(const QQuickWebView);
747     const QRectF visibleRect(q->boundingRect().intersected(pageView->boundingRect()));
748
749     // We avoid using toAlignedRect() because it produces inconsistent width and height.
750     QRectF mappedRect(q->mapRectToWebContent(visibleRect));
751     return QRect(floor(mappedRect.x()), floor(mappedRect.y()), floor(mappedRect.width()), floor(mappedRect.height()));
752 }
753
754 WebCore::IntSize QQuickWebViewPrivate::viewSize() const
755 {
756     return WebCore::IntSize(pageView->width(), pageView->height());
757 }
758
759 /*!
760     \internal
761
762     \qmlsignal WebViewExperimental::onMessageReceived(var message)
763
764     \brief Emitted when JavaScript code executing on the web page calls navigator.qt.postMessage().
765
766     \sa postMessage
767 */
768 void QQuickWebViewPrivate::didReceiveMessageFromNavigatorQtObject(const String& message)
769 {
770     QVariantMap variantMap;
771     variantMap.insert(QLatin1String("data"), QString(message));
772     variantMap.insert(QLatin1String("origin"), q_ptr->url());
773     emit q_ptr->experimental()->messageReceived(variantMap);
774 }
775
776 QQuickWebViewLegacyPrivate::QQuickWebViewLegacyPrivate(QQuickWebView* viewport)
777     : QQuickWebViewPrivate(viewport)
778 {
779     // Default values for the Legacy view.
780     attributes.devicePixelRatio = 1;
781     attributes.initialScale = 1;
782     attributes.minimumScale = 1;
783     attributes.maximumScale = 1;
784     attributes.userScalable = 0;
785 }
786
787 void QQuickWebViewLegacyPrivate::initialize(WKContextRef contextRef, WKPageGroupRef pageGroupRef)
788 {
789     QQuickWebViewPrivate::initialize(contextRef, pageGroupRef);
790     enableMouseEvents();
791
792     // Trigger setting of correct visibility flags after everything was allocated and initialized.
793     _q_onVisibleChanged();
794 }
795
796 void QQuickWebViewLegacyPrivate::updateViewportSize()
797 {
798     Q_Q(QQuickWebView);
799     QSize viewportSize = q->boundingRect().size().toSize();
800     pageView->setContentsSize(viewportSize);
801     // The fixed layout is handled by the FrameView and the drawing area doesn't behave differently
802     // whether its fixed or not. We still need to tell the drawing area which part of it
803     // has to be rendered on tiles, and in desktop mode it's all of it.
804     webPageProxy->drawingArea()->setSize(viewportSize, IntSize());
805     webPageProxy->drawingArea()->setVisibleContentsRect(IntRect(IntPoint(), viewportSize), 1, FloatPoint());
806 }
807
808 void QQuickWebViewLegacyPrivate::enableMouseEvents()
809 {
810     Q_Q(QQuickWebView);
811     q->setAcceptedMouseButtons(Qt::MouseButtonMask);
812     q->setAcceptHoverEvents(true);
813 }
814
815 void QQuickWebViewLegacyPrivate::disableMouseEvents()
816 {
817     Q_Q(QQuickWebView);
818     q->setAcceptedMouseButtons(Qt::NoButton);
819     q->setAcceptHoverEvents(false);
820 }
821
822 qreal QQuickWebViewLegacyPrivate::zoomFactor() const
823 {
824     return webPageProxy->pageZoomFactor();
825 }
826
827 void QQuickWebViewLegacyPrivate::setZoomFactor(qreal factor)
828 {
829     webPageProxy->setPageZoomFactor(factor);
830 }
831
832 QQuickWebViewFlickablePrivate::QQuickWebViewFlickablePrivate(QQuickWebView* viewport)
833     : QQuickWebViewPrivate(viewport)
834     , pageIsSuspended(true)
835 {
836     // Disable mouse events on the flickable web view so we do not
837     // select text during pan gestures on platforms which send both
838     // touch and mouse events simultaneously.
839     // FIXME: Temporary workaround code which should be removed when
840     // bug http://codereview.qt-project.org/21896 is fixed.
841     viewport->setAcceptedMouseButtons(Qt::NoButton);
842     viewport->setAcceptHoverEvents(false);
843 }
844
845 QQuickWebViewFlickablePrivate::~QQuickWebViewFlickablePrivate()
846 {
847     interactionEngine->disconnect();
848 }
849
850 void QQuickWebViewFlickablePrivate::initialize(WKContextRef contextRef, WKPageGroupRef pageGroupRef)
851 {
852     QQuickWebViewPrivate::initialize(contextRef, pageGroupRef);
853     webPageProxy->setUseFixedLayout(true);
854 }
855
856 void QQuickWebViewFlickablePrivate::onComponentComplete()
857 {
858     Q_Q(QQuickWebView);
859
860     interactionEngine.reset(new QtViewportInteractionEngine(q, pageView.data()));
861     pageView->eventHandler()->setViewportInteractionEngine(interactionEngine.data());
862
863     QObject::connect(interactionEngine.data(), SIGNAL(contentSuspendRequested()), q, SLOT(_q_suspend()));
864     QObject::connect(interactionEngine.data(), SIGNAL(contentResumeRequested()), q, SLOT(_q_resume()));
865     QObject::connect(interactionEngine.data(), SIGNAL(informVisibleContentChange(QPointF)), q, SLOT(_q_onInformVisibleContentChange(QPointF)));
866
867     _q_resume();
868
869     // Trigger setting of correct visibility flags after everything was allocated and initialized.
870     _q_onVisibleChanged();
871 }
872
873 void QQuickWebViewFlickablePrivate::didChangeViewportProperties(const WebCore::ViewportAttributes& newAttributes)
874 {
875     Q_Q(QQuickWebView);
876
877     // FIXME: Revise these when implementing fit-to-width.
878     WebCore::ViewportAttributes attr = newAttributes;
879     WebCore::restrictScaleFactorToInitialScaleIfNotUserScalable(attr);
880
881     // FIXME: Resetting here can reset more than needed. For instance it will end deferrers.
882     // This needs to be revised at some point.
883     interactionEngine->reset();
884
885     interactionEngine->setContentToDevicePixelRatio(attr.devicePixelRatio);
886
887     interactionEngine->setAllowsUserScaling(!!attr.userScalable);
888     interactionEngine->setCSSScaleBounds(attr.minimumScale, attr.maximumScale);
889
890     if (!interactionEngine->hadUserInteraction() && !pageIsSuspended)
891         interactionEngine->setCSSScale(attr.initialScale);
892
893     this->attributes = attr;
894     emit q->experimental()->test()->viewportChanged();
895
896     // If the web app successively changes the viewport on purpose
897     // it wants to be in control and we should disable animations.
898     interactionEngine->setItemRectVisible(interactionEngine->nearestValidBounds());
899 }
900
901 void QQuickWebViewFlickablePrivate::updateViewportSize()
902 {
903     Q_Q(QQuickWebView);
904     QSize viewportSize = q->boundingRect().size().toSize();
905
906     if (viewportSize.isEmpty() || !interactionEngine)
907         return;
908
909     WebPreferences* wkPrefs = webPageProxy->pageGroup()->preferences();
910     wkPrefs->setDeviceWidth(viewportSize.width());
911     wkPrefs->setDeviceHeight(viewportSize.height());
912
913     // Let the WebProcess know about the new viewport size, so that
914     // it can resize the content accordingly.
915     webPageProxy->setViewportSize(viewportSize);
916
917     _q_onInformVisibleContentChange(QPointF());
918 }
919
920 void QQuickWebViewFlickablePrivate::_q_onInformVisibleContentChange(const QPointF& trajectoryVector)
921 {
922     Q_Q(QQuickWebView);
923
924     DrawingAreaProxy* drawingArea = webPageProxy->drawingArea();
925     if (!drawingArea)
926         return;
927
928     const QRect visibleRect(visibleContentsRect());
929     float scale = pageView->contentsScale();
930
931     emit q->experimental()->test()->contentsScaleChanged();
932
933     QRectF accurateVisibleRect(q->boundingRect());
934     accurateVisibleRect.translate(contentPos());
935     drawingArea->setVisibleContentsRect(visibleRect, scale, trajectoryVector, FloatPoint(accurateVisibleRect.x(), accurateVisibleRect.y()));
936
937     // Ensure that updatePaintNode is always called before painting.
938     pageView->update();
939 }
940
941 void QQuickWebViewFlickablePrivate::_q_suspend()
942 {
943     pageIsSuspended = true;
944     webPageProxy->suspendActiveDOMObjectsAndAnimations();
945 }
946
947 void QQuickWebViewFlickablePrivate::_q_resume()
948 {
949     if (!interactionEngine)
950         return;
951
952     pageIsSuspended = false;
953     webPageProxy->resumeActiveDOMObjectsAndAnimations();
954
955     _q_onInformVisibleContentChange(QPointF());
956 }
957
958 void QQuickWebViewFlickablePrivate::pageDidRequestScroll(const QPoint& pos)
959 {
960     interactionEngine->pagePositionRequest(pos);
961 }
962
963 void QQuickWebViewFlickablePrivate::didChangeContentsSize(const QSize& newSize)
964 {
965     Q_Q(QQuickWebView);
966     QSize viewportSize = q->boundingRect().size().toSize();
967
968     pageView->setContentsSize(newSize); // emits contentsSizeChanged()
969
970     float minimumScale = WebCore::computeMinimumScaleFactorForContentContained(attributes, viewportSize, newSize);
971
972     bool scaleCommitNotified = false;
973
974     if (!qFuzzyCompare(minimumScale, attributes.minimumScale)) {
975         interactionEngine->setCSSScaleBounds(minimumScale, attributes.maximumScale);
976         emit q->experimental()->test()->viewportChanged();
977
978         if (!interactionEngine->hadUserInteraction() && !pageIsSuspended) {
979             // Emits contentsScaleCommitted();
980             scaleCommitNotified = true;
981             interactionEngine->setCSSScale(minimumScale);
982         }
983     }
984
985     // Emit for testing purposes, so that it can be verified that
986     // we didn't do scale adjustment.
987     if (!scaleCommitNotified)
988         emit q->experimental()->test()->contentsScaleCommitted();
989 }
990
991
992 /*!
993     \qmlsignal WebView::onNavigationRequested(WebNavigationRequest request)
994
995     This signal is emitted for every navigation request. The request object contains url,
996     button and modifiers properties describing the navigation action, e.g. "a middle click
997     with shift key pressed to 'http://qt-project.org'".
998
999     The navigation will be accepted by default. To change that, one can set the action
1000     property to WebView.IgnoreRequest to reject the request or WebView.DownloadRequest to
1001     trigger a download instead of navigating to the url.
1002
1003     The request object cannot be used after the signal handler function ends.
1004
1005     \sa WebNavigationRequest
1006 */
1007
1008 QQuickWebViewExperimental::QQuickWebViewExperimental(QQuickWebView *webView)
1009     : QObject(webView)
1010     , q_ptr(webView)
1011     , d_ptr(webView->d_ptr.data())
1012     , schemeParent(new QObject(this))
1013     , m_test(new QWebKitTest(webView->d_ptr.data(), this))
1014 {
1015 }
1016
1017 QQuickWebViewExperimental::~QQuickWebViewExperimental()
1018 {
1019 }
1020
1021 void QQuickWebViewExperimental::setRenderToOffscreenBuffer(bool enable)
1022 {
1023     Q_D(QQuickWebView);
1024     d->setRenderToOffscreenBuffer(enable);
1025 }
1026
1027 bool QQuickWebViewExperimental::renderToOffscreenBuffer() const
1028 {
1029     Q_D(const QQuickWebView);
1030     return d->renderToOffscreenBuffer();
1031 }
1032
1033 bool QQuickWebViewExperimental::transparentBackground() const
1034 {
1035     Q_D(const QQuickWebView);
1036     return d->transparentBackground();
1037 }
1038 void QQuickWebViewExperimental::setTransparentBackground(bool enable)
1039 {
1040     Q_D(QQuickWebView);
1041     d->setTransparentBackground(enable);
1042 }
1043
1044 bool QQuickWebViewExperimental::useDefaultContentItemSize() const
1045 {
1046     Q_D(const QQuickWebView);
1047     return d->m_useDefaultContentItemSize;
1048 }
1049
1050 void QQuickWebViewExperimental::setUseDefaultContentItemSize(bool enable)
1051 {
1052     Q_D(QQuickWebView);
1053     d->m_useDefaultContentItemSize = enable;
1054 }
1055
1056 /*!
1057     \internal
1058
1059     \qmlproperty int WebViewExperimental::preferredMinimumContentsWidth
1060     \brief Minimum contents width when not overriden by the page itself.
1061
1062     Unless the page defines how contents should be laid out, using e.g.
1063     the viewport meta tag, it is laid out given the width of the viewport
1064     (in CSS units).
1065
1066     This setting can be used to enforce a minimum width when the page
1067     does not define a width itself. This is useful for laying out pages
1068     designed for big screens, commonly knows as desktop pages, on small
1069     devices.
1070
1071     The default value is 0, but the value of 980 is recommented for small
1072     screens as it provides a good trade off between legitable pages and
1073     non-broken content.
1074  */
1075 int QQuickWebViewExperimental::preferredMinimumContentsWidth() const
1076 {
1077     Q_D(const QQuickWebView);
1078     return d->webPageProxy->pageGroup()->preferences()->layoutFallbackWidth();
1079 }
1080
1081 void QQuickWebViewExperimental::setPreferredMinimumContentsWidth(int width)
1082 {
1083     Q_D(QQuickWebView);
1084     d->webPageProxy->pageGroup()->preferences()->setLayoutFallbackWidth(width);
1085 }
1086
1087 void QQuickWebViewExperimental::setFlickableViewportEnabled(bool enable)
1088 {
1089     s_flickableViewportEnabled = enable;
1090 }
1091
1092 bool QQuickWebViewExperimental::flickableViewportEnabled()
1093 {
1094     return s_flickableViewportEnabled;
1095 }
1096
1097 /*!
1098     \internal
1099
1100     \qmlmethod void WebViewExperimental::postMessage(string message)
1101
1102     \brief Post a message to an onmessage function registered with the navigator.qt object
1103            by JavaScript code executing on the page.
1104
1105     \sa onMessageReceived
1106 */
1107
1108 void QQuickWebViewExperimental::postMessage(const QString& message)
1109 {
1110     Q_D(QQuickWebView);
1111     d->context->postMessageToNavigatorQtObject(d->webPageProxy.get(), message);
1112 }
1113
1114 QQmlComponent* QQuickWebViewExperimental::alertDialog() const
1115 {
1116     Q_D(const QQuickWebView);
1117     return d->alertDialog;
1118 }
1119
1120 void QQuickWebViewExperimental::setAlertDialog(QQmlComponent* alertDialog)
1121 {
1122     Q_D(QQuickWebView);
1123     if (d->alertDialog == alertDialog)
1124         return;
1125     d->alertDialog = alertDialog;
1126     emit alertDialogChanged();
1127 }
1128
1129 QQmlComponent* QQuickWebViewExperimental::confirmDialog() const
1130 {
1131     Q_D(const QQuickWebView);
1132     return d->confirmDialog;
1133 }
1134
1135 void QQuickWebViewExperimental::setConfirmDialog(QQmlComponent* confirmDialog)
1136 {
1137     Q_D(QQuickWebView);
1138     if (d->confirmDialog == confirmDialog)
1139         return;
1140     d->confirmDialog = confirmDialog;
1141     emit confirmDialogChanged();
1142 }
1143
1144 QWebNavigationHistory* QQuickWebViewExperimental::navigationHistory() const
1145 {
1146     return d_ptr->navigationHistory.get();
1147 }
1148
1149 QQmlComponent* QQuickWebViewExperimental::promptDialog() const
1150 {
1151     Q_D(const QQuickWebView);
1152     return d->promptDialog;
1153 }
1154
1155 QWebPreferences* QQuickWebViewExperimental::preferences() const
1156 {
1157     QQuickWebViewPrivate* const d = d_ptr;
1158     if (!d->preferences)
1159         d->preferences = adoptPtr(QWebPreferencesPrivate::createPreferences(d));
1160     return d->preferences.get();
1161 }
1162
1163 void QQuickWebViewExperimental::setPromptDialog(QQmlComponent* promptDialog)
1164 {
1165     Q_D(QQuickWebView);
1166     if (d->promptDialog == promptDialog)
1167         return;
1168     d->promptDialog = promptDialog;
1169     emit promptDialogChanged();
1170 }
1171
1172 QQmlComponent* QQuickWebViewExperimental::authenticationDialog() const
1173 {
1174     Q_D(const QQuickWebView);
1175     return d->authenticationDialog;
1176 }
1177
1178 void QQuickWebViewExperimental::setAuthenticationDialog(QQmlComponent* authenticationDialog)
1179 {
1180     Q_D(QQuickWebView);
1181     if (d->authenticationDialog == authenticationDialog)
1182         return;
1183     d->authenticationDialog = authenticationDialog;
1184     emit authenticationDialogChanged();
1185 }
1186
1187 QQmlComponent* QQuickWebViewExperimental::proxyAuthenticationDialog() const
1188 {
1189     Q_D(const QQuickWebView);
1190     return d->proxyAuthenticationDialog;
1191 }
1192
1193 void QQuickWebViewExperimental::setProxyAuthenticationDialog(QQmlComponent* proxyAuthenticationDialog)
1194 {
1195     Q_D(QQuickWebView);
1196     if (d->proxyAuthenticationDialog == proxyAuthenticationDialog)
1197         return;
1198     d->proxyAuthenticationDialog = proxyAuthenticationDialog;
1199     emit proxyAuthenticationDialogChanged();
1200 }
1201 QQmlComponent* QQuickWebViewExperimental::certificateVerificationDialog() const
1202 {
1203     Q_D(const QQuickWebView);
1204     return d->certificateVerificationDialog;
1205 }
1206
1207 void QQuickWebViewExperimental::setCertificateVerificationDialog(QQmlComponent* certificateVerificationDialog)
1208 {
1209     Q_D(QQuickWebView);
1210     if (d->certificateVerificationDialog == certificateVerificationDialog)
1211         return;
1212     d->certificateVerificationDialog = certificateVerificationDialog;
1213     emit certificateVerificationDialogChanged();
1214 }
1215
1216 QQmlComponent* QQuickWebViewExperimental::itemSelector() const
1217 {
1218     Q_D(const QQuickWebView);
1219     return d->itemSelector;
1220 }
1221
1222 void QQuickWebViewExperimental::setItemSelector(QQmlComponent* itemSelector)
1223 {
1224     Q_D(QQuickWebView);
1225     if (d->itemSelector == itemSelector)
1226         return;
1227     d->itemSelector = itemSelector;
1228     emit itemSelectorChanged();
1229 }
1230
1231 QQmlComponent* QQuickWebViewExperimental::filePicker() const
1232 {
1233     Q_D(const QQuickWebView);
1234     return d->filePicker;
1235 }
1236
1237 void QQuickWebViewExperimental::setFilePicker(QQmlComponent* filePicker)
1238 {
1239     Q_D(QQuickWebView);
1240     if (d->filePicker == filePicker)
1241         return;
1242     d->filePicker = filePicker;
1243     emit filePickerChanged();
1244 }
1245
1246 QQmlComponent* QQuickWebViewExperimental::databaseQuotaDialog() const
1247 {
1248     Q_D(const QQuickWebView);
1249     return d->databaseQuotaDialog;
1250 }
1251
1252 void QQuickWebViewExperimental::setDatabaseQuotaDialog(QQmlComponent* databaseQuotaDialog)
1253 {
1254     Q_D(QQuickWebView);
1255     if (d->databaseQuotaDialog == databaseQuotaDialog)
1256         return;
1257     d->databaseQuotaDialog = databaseQuotaDialog;
1258     emit databaseQuotaDialogChanged();
1259 }
1260
1261 QString QQuickWebViewExperimental::userAgent() const
1262 {
1263     Q_D(const QQuickWebView);
1264     return d->webPageProxy->userAgent();
1265 }
1266
1267 void QQuickWebViewExperimental::setUserAgent(const QString& userAgent)
1268 {
1269     Q_D(QQuickWebView);
1270     if (userAgent == QString(d->webPageProxy->userAgent()))
1271         return;
1272
1273     d->webPageProxy->setUserAgent(userAgent);
1274     emit userAgentChanged();
1275 }
1276
1277 /*!
1278     \internal
1279
1280     \qmlproperty real WebViewExperimental::devicePixelRatio
1281     \brief The ratio between the CSS units and device pixels when the content is unscaled.
1282
1283     When designing touch-friendly contents, knowing the approximated target size on a device
1284     is important for contents providers in order to get the intented layout and element
1285     sizes.
1286
1287     As most first generation touch devices had a PPI of approximately 160, this became a
1288     de-facto value, when used in conjunction with the viewport meta tag.
1289
1290     Devices with a higher PPI learning towards 240 or 320, applies a pre-scaling on all
1291     content, of either 1.5 or 2.0, not affecting the CSS scale or pinch zooming.
1292
1293     This value can be set using this property and it is exposed to CSS media queries using
1294     the -webkit-device-pixel-ratio query.
1295
1296     For instance, if you want to load an image without having it upscaled on a web view
1297     using a device pixel ratio of 2.0 it can be done by loading an image of say 100x100
1298     pixels but showing it at half the size.
1299
1300     FIXME: Move documentation example out in separate files
1301
1302     @media (-webkit-min-device-pixel-ratio: 1.5) {
1303         .icon {
1304             width: 50px;
1305             height: 50px;
1306             url: "/images/icon@2x.png"; // This is actually a 100x100 image
1307         }
1308     }
1309
1310     If the above is used on a device with device pixel ratio of 1.5, it will be scaled
1311     down but still provide a better looking image.
1312  */
1313
1314 double QQuickWebViewExperimental::devicePixelRatio() const
1315 {
1316     Q_D(const QQuickWebView);
1317     return d->webPageProxy->pageGroup()->preferences()->devicePixelRatio();
1318 }
1319
1320 void QQuickWebViewExperimental::setDevicePixelRatio(double devicePixelRatio)
1321 {
1322     Q_D(QQuickWebView);
1323     if (devicePixelRatio == this->devicePixelRatio())
1324         return;
1325
1326     d->webPageProxy->pageGroup()->preferences()->setDevicePixelRatio(devicePixelRatio);
1327     emit devicePixelRatioChanged();
1328 }
1329
1330 /*!
1331     \internal
1332
1333     \qmlmethod void WebViewExperimental::evaluateJavaScript(string script [, function(result)])
1334
1335     \brief Evaluates the specified JavaScript and, if supplied, calls a function with the result.
1336 */
1337
1338 void QQuickWebViewExperimental::evaluateJavaScript(const QString& script, const QJSValue& value)
1339 {
1340     JSCallbackClosure* closure = new JSCallbackClosure;
1341
1342     closure->receiver = this;
1343     closure->value = value;
1344
1345     d_ptr->webPageProxy.get()->runJavaScriptInMainFrame(script, ScriptValueCallback::create(closure, javaScriptCallback));
1346 }
1347
1348 QList<QUrl> QQuickWebViewExperimental::userScripts() const
1349 {
1350     Q_D(const QQuickWebView);
1351     return d->userScripts;
1352 }
1353
1354 void QQuickWebViewExperimental::setUserScripts(const QList<QUrl>& userScripts)
1355 {
1356     Q_D(QQuickWebView);
1357     if (d->userScripts == userScripts)
1358         return;
1359     d->userScripts = userScripts;
1360     d->updateUserScripts();
1361     emit userScriptsChanged();
1362 }
1363
1364 QQuickUrlSchemeDelegate* QQuickWebViewExperimental::schemeDelegates_At(QQmlListProperty<QQuickUrlSchemeDelegate>* property, int index)
1365 {
1366     const QObjectList children = property->object->children();
1367     if (index < children.count())
1368         return static_cast<QQuickUrlSchemeDelegate*>(children.at(index));
1369     return 0;
1370 }
1371
1372 void QQuickWebViewExperimental::schemeDelegates_Append(QQmlListProperty<QQuickUrlSchemeDelegate>* property, QQuickUrlSchemeDelegate *scheme)
1373 {
1374     QObject* schemeParent = property->object;
1375     scheme->setParent(schemeParent);
1376     QQuickWebViewExperimental* webViewExperimental = qobject_cast<QQuickWebViewExperimental*>(property->object->parent());
1377     if (!webViewExperimental)
1378         return;
1379     scheme->reply()->setWebViewExperimental(webViewExperimental);
1380     QQuickWebViewPrivate* d = webViewExperimental->d_func();
1381     d->webPageProxy->registerApplicationScheme(scheme->scheme());
1382 }
1383
1384 int QQuickWebViewExperimental::schemeDelegates_Count(QQmlListProperty<QQuickUrlSchemeDelegate>* property)
1385 {
1386     return property->object->children().count();
1387 }
1388
1389 void QQuickWebViewExperimental::schemeDelegates_Clear(QQmlListProperty<QQuickUrlSchemeDelegate>* property)
1390 {
1391     const QObjectList children = property->object->children();
1392     for (int index = 0; index < children.count(); index++) {
1393         QObject* child = children.at(index);
1394         child->setParent(0);
1395         delete child;
1396     }
1397 }
1398
1399 QQmlListProperty<QQuickUrlSchemeDelegate> QQuickWebViewExperimental::schemeDelegates()
1400 {
1401     return QQmlListProperty<QQuickUrlSchemeDelegate>(schemeParent, 0,
1402             QQuickWebViewExperimental::schemeDelegates_Append,
1403             QQuickWebViewExperimental::schemeDelegates_Count,
1404             QQuickWebViewExperimental::schemeDelegates_At,
1405             QQuickWebViewExperimental::schemeDelegates_Clear);
1406 }
1407
1408 void QQuickWebViewExperimental::invokeApplicationSchemeHandler(PassRefPtr<QtRefCountedNetworkRequestData> request)
1409 {
1410     RefPtr<QtRefCountedNetworkRequestData> req = request;
1411     const QObjectList children = schemeParent->children();
1412     for (int index = 0; index < children.count(); index++) {
1413         QQuickUrlSchemeDelegate* delegate = qobject_cast<QQuickUrlSchemeDelegate*>(children.at(index));
1414         if (!delegate)
1415             continue;
1416         if (!delegate->scheme().compare(QString(req->data().m_scheme), Qt::CaseInsensitive)) {
1417             delegate->request()->setNetworkRequestData(req);
1418             delegate->reply()->setNetworkRequestData(req);
1419             emit delegate->receivedRequest();
1420             return;
1421         }
1422     }
1423 }
1424
1425 void QQuickWebViewExperimental::sendApplicationSchemeReply(QQuickNetworkReply* reply)
1426 {
1427     d_ptr->webPageProxy->sendApplicationSchemeReply(reply);
1428 }
1429
1430 void QQuickWebViewExperimental::goForwardTo(int index)
1431 {
1432     d_ptr->navigationHistory->d->goForwardTo(index);
1433 }
1434
1435 void QQuickWebViewExperimental::goBackTo(int index)
1436 {
1437     d_ptr->navigationHistory->d->goBackTo(index);
1438 }
1439
1440 QWebKitTest* QQuickWebViewExperimental::test()
1441 {
1442     return m_test;
1443 }
1444
1445 QQuickWebPage* QQuickWebViewExperimental::page()
1446 {
1447     return q_ptr->page();
1448 }
1449
1450 /*!
1451     \qmlclass WebView QWebView
1452     \inqmlmodule QtWebKit 3.0
1453 */
1454
1455 /*!
1456    \qmlmethod WebView(Item parent)
1457    \brief Constructs a WebView with a parent.
1458 */
1459
1460 QQuickWebView::QQuickWebView(QQuickItem* parent)
1461     : QQuickFlickable(parent)
1462     , d_ptr(createPrivateObject(this))
1463     , m_experimental(new QQuickWebViewExperimental(this))
1464 {
1465     Q_D(QQuickWebView);
1466     d->initialize();
1467 }
1468
1469 QQuickWebView::QQuickWebView(WKContextRef contextRef, WKPageGroupRef pageGroupRef, QQuickItem* parent)
1470     : QQuickFlickable(parent)
1471     , d_ptr(createPrivateObject(this))
1472     , m_experimental(new QQuickWebViewExperimental(this))
1473 {
1474     Q_D(QQuickWebView);
1475     d->initialize(contextRef, pageGroupRef);
1476 }
1477
1478 QQuickWebView::~QQuickWebView()
1479 {
1480 }
1481
1482 QQuickWebPage* QQuickWebView::page()
1483 {
1484     Q_D(QQuickWebView);
1485     return d->pageView.data();
1486 }
1487
1488 void QQuickWebView::goBack()
1489 {
1490     Q_D(QQuickWebView);
1491     d->webPageProxy->goBack();
1492 }
1493
1494 void QQuickWebView::goForward()
1495 {
1496     Q_D(QQuickWebView);
1497     d->webPageProxy->goForward();
1498 }
1499
1500 void QQuickWebView::stop()
1501 {
1502     Q_D(QQuickWebView);
1503     d->webPageProxy->stopLoading();
1504 }
1505
1506 void QQuickWebView::reload()
1507 {
1508     Q_D(QQuickWebView);
1509
1510     WebFrameProxy* mainFrame = d->webPageProxy->mainFrame();
1511     if (mainFrame && !mainFrame->unreachableURL().isEmpty() && mainFrame->url() != blankURL()) {
1512         // We are aware of the unreachable url on the UI process side, but since we haven't
1513         // loaded alternative/subsitute data for it (an error page eg.) WebCore doesn't know
1514         // about the unreachable url yet. If we just do a reload at this point WebCore will try to
1515         // reload the currently committed url instead of the unrachable url. To work around this
1516         // we override the reload here by doing a manual load.
1517         d->webPageProxy->loadURL(mainFrame->unreachableURL());
1518         // FIXME: We should make WebCore aware of the unreachable url regardless of substitute-loads
1519         return;
1520     }
1521
1522     const bool reloadFromOrigin = true;
1523     d->webPageProxy->reload(reloadFromOrigin);
1524 }
1525
1526 QUrl QQuickWebView::url() const
1527 {
1528     Q_D(const QQuickWebView);
1529
1530     // FIXME: Enable once we are sure this should not trigger
1531     // Q_ASSERT(d->m_currentUrl == d->webPageProxy->activeURL());
1532
1533     return QUrl(d->m_currentUrl);
1534 }
1535
1536 void QQuickWebView::setUrl(const QUrl& url)
1537 {
1538     Q_D(QQuickWebView);
1539
1540     if (url.isEmpty())
1541         return;
1542
1543     d->webPageProxy->loadURL(url.toString());
1544     emitUrlChangeIfNeeded();
1545 }
1546
1547 // Make sure we don't emit urlChanged unless it actually changed
1548 void QQuickWebView::emitUrlChangeIfNeeded()
1549 {
1550     Q_D(QQuickWebView);
1551
1552     WTF::String activeUrl = d->webPageProxy->activeURL();
1553     if (activeUrl != d->m_currentUrl) {
1554         d->m_currentUrl = activeUrl;
1555         emit urlChanged();
1556     }
1557 }
1558
1559 QUrl QQuickWebView::icon() const
1560 {
1561     Q_D(const QQuickWebView);
1562     return d->m_iconURL;
1563 }
1564
1565 /*!
1566     \qmlproperty int WebView::loadProgress
1567     \brief The progress of loading the current web page.
1568
1569     The range is from 0 to 100.
1570 */
1571
1572 int QQuickWebView::loadProgress() const
1573 {
1574     Q_D(const QQuickWebView);
1575     return d->loadProgress();
1576 }
1577
1578 bool QQuickWebView::canGoBack() const
1579 {
1580     Q_D(const QQuickWebView);
1581     return d->webPageProxy->canGoBack();
1582 }
1583
1584 bool QQuickWebView::canGoForward() const
1585 {
1586     Q_D(const QQuickWebView);
1587     return d->webPageProxy->canGoForward();
1588 }
1589
1590 /*!
1591     \qmlproperty bool WebView::loading
1592     \brief True if the web view is currently loading a web page, false otherwise.
1593 */
1594
1595 bool QQuickWebView::loading() const
1596 {
1597     Q_D(const QQuickWebView);
1598     RefPtr<WebKit::WebFrameProxy> mainFrame = d->webPageProxy->mainFrame();
1599     return mainFrame && !(WebFrameProxy::LoadStateFinished == mainFrame->loadState());
1600 }
1601
1602 /*!
1603     \internal
1604  */
1605
1606 QPointF QQuickWebView::mapToWebContent(const QPointF& pointInViewCoordinates) const
1607 {
1608     Q_D(const QQuickWebView);
1609     return d->pageView->transformFromItem().map(pointInViewCoordinates);
1610 }
1611
1612 /*!
1613     \internal
1614  */
1615
1616 QRectF QQuickWebView::mapRectToWebContent(const QRectF& rectInViewCoordinates) const
1617 {
1618     Q_D(const QQuickWebView);
1619     return d->pageView->transformFromItem().mapRect(rectInViewCoordinates);
1620 }
1621
1622 /*!
1623     \internal
1624  */
1625
1626 QPointF QQuickWebView::mapFromWebContent(const QPointF& pointInCSSCoordinates) const
1627 {
1628     Q_D(const QQuickWebView);
1629     return d->pageView->transformToItem().map(pointInCSSCoordinates);
1630 }
1631
1632 /*!
1633     \internal
1634  */
1635 QRectF QQuickWebView::mapRectFromWebContent(const QRectF& rectInCSSCoordinates) const
1636 {
1637     Q_D(const QQuickWebView);
1638     return d->pageView->transformToItem().mapRect(rectInCSSCoordinates);
1639 }
1640
1641 /*!
1642     \qmlproperty string WebView::title
1643     \brief The title of the loaded page.
1644 */
1645
1646 QString QQuickWebView::title() const
1647 {
1648     Q_D(const QQuickWebView);
1649     return d->webPageProxy->pageTitle();
1650 }
1651
1652 QVariant QQuickWebView::inputMethodQuery(Qt::InputMethodQuery property) const
1653 {
1654     Q_D(const QQuickWebView);
1655     const EditorState& state = d->webPageProxy->editorState();
1656
1657     switch(property) {
1658     case Qt::ImCursorRectangle:
1659         return QRectF(state.cursorRect);
1660     case Qt::ImFont:
1661         return QVariant();
1662     case Qt::ImCursorPosition:
1663         return QVariant(static_cast<int>(state.cursorPosition));
1664     case Qt::ImAnchorPosition:
1665         return QVariant(static_cast<int>(state.anchorPosition));
1666     case Qt::ImSurroundingText:
1667         return QString(state.surroundingText);
1668     case Qt::ImCurrentSelection:
1669         return QString(state.selectedText);
1670     case Qt::ImMaximumTextLength:
1671         return QVariant(); // No limit.
1672     case Qt::ImHints:
1673         return int(Qt::InputMethodHints(state.inputMethodHints));
1674     default:
1675         // Rely on the base implementation for ImEnabled, ImHints and ImPreferredLanguage.
1676         return QQuickFlickable::inputMethodQuery(property);
1677     }
1678 }
1679
1680 /*!
1681     \preliminary
1682
1683     The experimental module consisting on experimental API which will break
1684     from version to version.
1685 */
1686 QQuickWebViewExperimental* QQuickWebView::experimental() const
1687 {
1688     return m_experimental;
1689 }
1690
1691 /*!
1692     \internal
1693 */
1694 void QQuickWebView::platformInitialize()
1695 {
1696     JSC::initializeThreading();
1697     WTF::initializeMainThread();
1698 }
1699
1700 void QQuickWebView::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
1701 {
1702     Q_D(QQuickWebView);
1703     QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
1704     if (newGeometry.size() != oldGeometry.size())
1705         d->updateViewportSize();
1706 }
1707
1708 void QQuickWebView::componentComplete()
1709 {
1710     Q_D(QQuickWebView);
1711     QQuickFlickable::componentComplete();
1712
1713     d->onComponentComplete();
1714     d->updateViewportSize();
1715 }
1716
1717 void QQuickWebView::keyPressEvent(QKeyEvent* event)
1718 {
1719     Q_D(QQuickWebView);
1720     d->pageView->eventHandler()->handleKeyPressEvent(event);
1721 }
1722
1723 void QQuickWebView::keyReleaseEvent(QKeyEvent* event)
1724 {
1725     Q_D(QQuickWebView);
1726     d->pageView->eventHandler()->handleKeyReleaseEvent(event);
1727 }
1728
1729 void QQuickWebView::inputMethodEvent(QInputMethodEvent* event)
1730 {
1731     Q_D(QQuickWebView);
1732     d->pageView->eventHandler()->handleInputMethodEvent(event);
1733 }
1734
1735 void QQuickWebView::focusInEvent(QFocusEvent* event)
1736 {
1737     Q_D(QQuickWebView);
1738     d->pageView->eventHandler()->handleFocusInEvent(event);
1739 }
1740
1741 void QQuickWebView::focusOutEvent(QFocusEvent* event)
1742 {
1743     Q_D(QQuickWebView);
1744     d->pageView->eventHandler()->handleFocusOutEvent(event);
1745 }
1746
1747 void QQuickWebView::touchEvent(QTouchEvent* event)
1748 {
1749     Q_D(QQuickWebView);
1750     if (d->m_dialogActive) {
1751         event->ignore();
1752         return;
1753     }
1754
1755     bool lockingDisabled = flickableDirection() != AutoFlickDirection
1756                            || event->touchPoints().size() != 1
1757                            || width() >= contentWidth()
1758                            || height() >= contentHeight();
1759
1760     if (!lockingDisabled)
1761         d->axisLocker.update(event);
1762     else
1763         d->axisLocker.reset();
1764
1765     forceActiveFocus();
1766     d->pageView->eventHandler()->handleTouchEvent(event);
1767 }
1768
1769 void QQuickWebView::mousePressEvent(QMouseEvent* event)
1770 {
1771     Q_D(QQuickWebView);
1772     forceActiveFocus();
1773     d->pageView->eventHandler()->handleMousePressEvent(event);
1774 }
1775
1776 void QQuickWebView::mouseMoveEvent(QMouseEvent* event)
1777 {
1778     Q_D(QQuickWebView);
1779     d->pageView->eventHandler()->handleMouseMoveEvent(event);
1780 }
1781
1782 void QQuickWebView::mouseReleaseEvent(QMouseEvent* event)
1783 {
1784     Q_D(QQuickWebView);
1785     d->pageView->eventHandler()->handleMouseReleaseEvent(event);
1786 }
1787
1788 void QQuickWebView::mouseDoubleClickEvent(QMouseEvent* event)
1789 {
1790     Q_D(QQuickWebView);
1791
1792     forceActiveFocus();
1793     // If a MouseButtonDblClick was received then we got a MouseButtonPress before
1794     // handleMousePressEvent will take care of double clicks.
1795     d->pageView->eventHandler()->handleMousePressEvent(event);
1796 }
1797
1798 void QQuickWebView::wheelEvent(QWheelEvent* event)
1799 {
1800     Q_D(QQuickWebView);
1801     d->pageView->eventHandler()->handleWheelEvent(event);
1802 }
1803
1804 void QQuickWebView::hoverEnterEvent(QHoverEvent* event)
1805 {
1806     Q_D(QQuickWebView);
1807     // Map HoverEnter to Move, for WebKit the distinction doesn't matter.
1808     d->pageView->eventHandler()->handleHoverMoveEvent(event);
1809 }
1810
1811 void QQuickWebView::hoverMoveEvent(QHoverEvent* event)
1812 {
1813     Q_D(QQuickWebView);
1814     d->pageView->eventHandler()->handleHoverMoveEvent(event);
1815 }
1816
1817 void QQuickWebView::hoverLeaveEvent(QHoverEvent* event)
1818 {
1819     Q_D(QQuickWebView);
1820     d->pageView->eventHandler()->handleHoverLeaveEvent(event);
1821 }
1822
1823 void QQuickWebView::dragMoveEvent(QDragMoveEvent* event)
1824 {
1825     Q_D(QQuickWebView);
1826     d->pageView->eventHandler()->handleDragMoveEvent(event);
1827 }
1828
1829 void QQuickWebView::dragEnterEvent(QDragEnterEvent* event)
1830 {
1831     Q_D(QQuickWebView);
1832     d->pageView->eventHandler()->handleDragEnterEvent(event);
1833 }
1834
1835 void QQuickWebView::dragLeaveEvent(QDragLeaveEvent* event)
1836 {
1837     Q_D(QQuickWebView);
1838     d->pageView->eventHandler()->handleDragLeaveEvent(event);
1839 }
1840
1841 void QQuickWebView::dropEvent(QDropEvent* event)
1842 {
1843     Q_D(QQuickWebView);
1844     d->pageView->eventHandler()->handleDropEvent(event);
1845 }
1846
1847 bool QQuickWebView::event(QEvent* ev)
1848 {
1849     // Re-implemented for possible future use without breaking binary compatibility.
1850     return QQuickFlickable::event(ev);
1851 }
1852
1853 WKPageRef QQuickWebView::pageRef() const
1854 {
1855     Q_D(const QQuickWebView);
1856     return toAPI(d->webPageProxy.get());
1857 }
1858
1859 QPointF QQuickWebView::contentPos() const
1860 {
1861     Q_D(const QQuickWebView);
1862     return d->contentPos();
1863 }
1864
1865 void QQuickWebView::setContentPos(const QPointF& pos)
1866 {
1867     Q_D(QQuickWebView);
1868     d->setContentPos(pos);
1869 }
1870
1871 void QQuickWebView::handleFlickableMousePress(const QPointF& position, qint64 eventTimestampMillis)
1872 {
1873     Q_D(QQuickWebView);
1874     d->axisLocker.setReferencePosition(position);
1875     QMouseEvent mouseEvent(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1876     mouseEvent.setTimestamp(eventTimestampMillis);
1877     QQuickFlickable::mousePressEvent(&mouseEvent);
1878 }
1879
1880 void QQuickWebView::handleFlickableMouseMove(const QPointF& position, qint64 eventTimestampMillis)
1881 {
1882     Q_D(QQuickWebView);
1883     QMouseEvent mouseEvent(QEvent::MouseMove, d->axisLocker.adjust(position), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1884     mouseEvent.setTimestamp(eventTimestampMillis);
1885     QQuickFlickable::mouseMoveEvent(&mouseEvent);
1886 }
1887
1888 void QQuickWebView::handleFlickableMouseRelease(const QPointF& position, qint64 eventTimestampMillis)
1889 {
1890     Q_D(QQuickWebView);
1891     QMouseEvent mouseEvent(QEvent::MouseButtonRelease, d->axisLocker.adjust(position), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1892     d->axisLocker.reset();
1893     mouseEvent.setTimestamp(eventTimestampMillis);
1894     QQuickFlickable::mouseReleaseEvent(&mouseEvent);
1895 }
1896
1897 /*!
1898     \qmlmethod void WebView::loadHtml(string html, url baseUrl, url unreachableUrl)
1899     \brief Loads the specified \a html as the content of the web view.
1900
1901     External objects such as stylesheets or images referenced in the HTML
1902     document are located relative to \a baseUrl.
1903
1904     If an \a unreachableUrl is passed it is used as the url for the loaded
1905     content. This is typically used to display error pages for a failed
1906     load.
1907
1908     \sa WebView::url
1909 */
1910 void QQuickWebView::loadHtml(const QString& html, const QUrl& baseUrl, const QUrl& unreachableUrl)
1911 {
1912     Q_D(QQuickWebView);
1913     if (unreachableUrl.isValid())
1914         d->webPageProxy->loadAlternateHTMLString(html, baseUrl.toString(), unreachableUrl.toString());
1915     else
1916         d->webPageProxy->loadHTMLString(html, baseUrl.toString());
1917 }
1918
1919 qreal QQuickWebView::zoomFactor() const
1920 {
1921     Q_D(const QQuickWebView);
1922     return d->zoomFactor();
1923 }
1924
1925 void QQuickWebView::setZoomFactor(qreal factor)
1926 {
1927
1928     Q_D(QQuickWebView);
1929     d->setZoomFactor(factor);
1930 }
1931
1932 void QQuickWebView::runJavaScriptInMainFrame(const QString &script, QObject *receiver, const char *method)
1933 {
1934     Q_D(QQuickWebView);
1935
1936     JSCallbackClosure* closure = new JSCallbackClosure;
1937     closure->receiver = receiver;
1938     closure->method = method;
1939
1940     d->webPageProxy.get()->runJavaScriptInMainFrame(script, ScriptValueCallback::create(closure, javaScriptCallback));
1941 }
1942
1943 bool QQuickWebView::allowAnyHTTPSCertificateForLocalHost() const
1944 {
1945     Q_D(const QQuickWebView);
1946     return d->m_allowAnyHTTPSCertificateForLocalHost;
1947 }
1948
1949 void QQuickWebView::setAllowAnyHTTPSCertificateForLocalHost(bool allow)
1950 {
1951     Q_D(QQuickWebView);
1952     d->m_allowAnyHTTPSCertificateForLocalHost = allow;
1953 }
1954
1955
1956 #include "moc_qquickwebview_p.cpp"