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