[Qt] [WK2] Move PageLoadClient related code to QtWebPageLoadClient
[WebKit.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 "QtDialogRunner.h"
25 #include "QtWebPageProxy.h"
26 #include "UtilsQt.h"
27 #include "WebPageGroup.h"
28 #include "WebPreferences.h"
29
30 #include "qquickwebpage_p_p.h"
31 #include "qquickwebview_p_p.h"
32 #include "qwebpreferences_p_p.h"
33
34 #include <QtDeclarative/QQuickCanvas>
35 #include <QtWidgets/QFileDialog>
36 #include <QtWidgets/QInputDialog>
37 #include <WKOpenPanelResultListener.h>
38
39 QQuickWebViewPrivate::QQuickWebViewPrivate(QQuickWebView* viewport, WKContextRef contextRef, WKPageGroupRef pageGroupRef)
40     : q_ptr(viewport)
41     , alertDialog(0)
42     , confirmDialog(0)
43     , promptDialog(0)
44     , postTransitionState(adoptPtr(new PostTransitionState(this)))
45     , transitioningToNewPage(false)
46     , useTraditionalDesktopBehaviour(false)
47 {
48     viewport->setFlags(QQuickItem::ItemClipsChildrenToShape);
49
50     QObject::connect(viewport, SIGNAL(visibleChanged()), viewport, SLOT(_q_onVisibleChanged()));
51     pageView.reset(new QQuickWebPage(viewport));
52
53     QQuickWebPagePrivate* const pageViewPrivate = pageView.data()->d;
54     setPageProxy(new QtWebPageProxy(pageView.data(), q_ptr, 0, this, contextRef, pageGroupRef));
55     pageViewPrivate->setPageProxy(pageProxy.data());
56
57     QWebPreferencesPrivate::get(pageProxy->preferences())->setAttribute(QWebPreferencesPrivate::AcceleratedCompositingEnabled, true);
58     pageProxy->init();
59
60     pageUIClient.reset(new QtWebPageUIClient(pageProxy->pageRef(), q_ptr));
61     pageLoadClient.reset(new QtWebPageLoadClient(pageProxy->pageRef(), q_ptr));
62 }
63
64 void QQuickWebViewPrivate::enableMouseEvents()
65 {
66     Q_Q(QQuickWebView);
67     q->setAcceptedMouseButtons(Qt::MouseButtonMask);
68     q->setAcceptHoverEvents(true);
69     pageView->setAcceptedMouseButtons(Qt::MouseButtonMask);
70     pageView->setAcceptHoverEvents(true);
71 }
72
73 void QQuickWebViewPrivate::disableMouseEvents()
74 {
75     Q_Q(QQuickWebView);
76     q->setAcceptedMouseButtons(Qt::NoButton);
77     q->setAcceptHoverEvents(false);
78     pageView->setAcceptedMouseButtons(Qt::NoButton);
79     pageView->setAcceptHoverEvents(false);
80 }
81
82 void QQuickWebViewPrivate::initializeDesktop(QQuickWebView* viewport)
83 {
84     if (interactionEngine) {
85         QObject::disconnect(interactionEngine.data(), SIGNAL(viewportUpdateRequested()), viewport, SLOT(_q_viewportUpdated()));
86         QObject::disconnect(interactionEngine.data(), SIGNAL(viewportTrajectoryVectorChanged(const QPointF&)), viewport, SLOT(_q_viewportTrajectoryVectorChanged(const QPointF&)));
87     }
88     interactionEngine.reset(0);
89     pageProxy->setViewportInteractionEngine(0);
90     enableMouseEvents();
91 }
92
93 void QQuickWebViewPrivate::initializeTouch(QQuickWebView* viewport)
94 {
95     interactionEngine.reset(new QtViewportInteractionEngine(viewport, pageView.data()));
96     pageProxy->setViewportInteractionEngine(interactionEngine.data());
97     disableMouseEvents();
98     QObject::connect(interactionEngine.data(), SIGNAL(viewportUpdateRequested()), viewport, SLOT(_q_viewportUpdated()));
99     QObject::connect(interactionEngine.data(), SIGNAL(viewportTrajectoryVectorChanged(const QPointF&)), viewport, SLOT(_q_viewportTrajectoryVectorChanged(const QPointF&)));
100     updateViewportSize();
101 }
102
103 void QQuickWebViewPrivate::loadDidCommit()
104 {
105     transitioningToNewPage = true;
106 }
107
108 void QQuickWebViewPrivate::didFinishFirstNonEmptyLayout()
109 {
110     transitioningToNewPage = false;
111
112     if (useTraditionalDesktopBehaviour)
113         return;
114
115     postTransitionState->apply();
116 }
117
118 void QQuickWebViewPrivate::didChangeContentsSize(const QSize& newSize)
119 {
120     if (useTraditionalDesktopBehaviour)
121         return;
122
123     if (isTransitioningToNewPage()) {
124         postTransitionState->contentsSize = newSize;
125         return;
126     }
127
128     pageView->setWidth(newSize.width());
129     pageView->setHeight(newSize.height());
130 }
131
132 void QQuickWebViewPrivate::didChangeViewportProperties(const WebCore::ViewportArguments& args)
133 {
134     if (useTraditionalDesktopBehaviour)
135         return;
136
137     viewportArguments = args;
138
139     if (isTransitioningToNewPage())
140         return;
141
142     interactionEngine->applyConstraints(computeViewportConstraints());
143 }
144
145 void QQuickWebViewPrivate::scrollPositionRequested(const QPoint& pos)
146 {
147     if (!useTraditionalDesktopBehaviour)
148         interactionEngine->pagePositionRequest(pos);
149 }
150
151 void QQuickWebViewPrivate::_q_viewportUpdated()
152 {
153     Q_Q(QQuickWebView);
154     const QRectF visibleRectInPageViewCoordinates = q->mapRectToItem(pageView.data(), q->boundingRect()).intersected(pageView->boundingRect());
155     float scale = pageView->scale();
156     pageProxy->setVisibleContentRectAndScale(visibleRectInPageViewCoordinates, scale);
157 }
158
159 void QQuickWebViewPrivate::_q_viewportTrajectoryVectorChanged(const QPointF& trajectoryVector)
160 {
161     pageProxy->setVisibleContentRectTrajectoryVector(trajectoryVector);
162 }
163
164 void QQuickWebViewPrivate::_q_onVisibleChanged()
165 {
166     WebPageProxy* wkPage = toImpl(pageProxy->pageRef());
167
168     wkPage->viewStateDidChange(WebPageProxy::ViewIsVisible);
169 }
170
171 void QQuickWebViewPrivate::updateViewportSize()
172 {
173     Q_Q(QQuickWebView);
174     QSize viewportSize = q->boundingRect().size().toSize();
175
176     if (viewportSize.isEmpty())
177         return;
178
179     WebPageProxy* wkPage = toImpl(pageProxy->pageRef());
180     // Let the WebProcess know about the new viewport size, so that
181     // it can resize the content accordingly.
182     wkPage->setViewportSize(viewportSize);
183
184     interactionEngine->applyConstraints(computeViewportConstraints());
185     _q_viewportUpdated();
186 }
187
188 QtViewportInteractionEngine::Constraints QQuickWebViewPrivate::computeViewportConstraints()
189 {
190     Q_Q(QQuickWebView);
191
192     QSize availableSize = q->boundingRect().size().toSize();
193
194     Q_ASSERT(!availableSize.isEmpty());
195
196     WebPageProxy* wkPage = toImpl(pageProxy->pageRef());
197     WebPreferences* wkPrefs = wkPage->pageGroup()->preferences();
198
199     // FIXME: Remove later; Hardcode some values for now to make sure the DPI adjustment is being tested.
200     wkPrefs->setDeviceDPI(240);
201     wkPrefs->setDeviceWidth(480);
202     wkPrefs->setDeviceHeight(720);
203
204     int minimumLayoutFallbackWidth = qMax<int>(wkPrefs->layoutFallbackWidth(), availableSize.width());
205
206     WebCore::ViewportAttributes attr = WebCore::computeViewportAttributes(viewportArguments, minimumLayoutFallbackWidth, wkPrefs->deviceWidth(), wkPrefs->deviceHeight(), wkPrefs->deviceDPI(), availableSize);
207     WebCore::restrictMinimumScaleFactorToViewportSize(attr, availableSize);
208     WebCore::restrictScaleFactorToInitialScaleIfNotUserScalable(attr);
209
210     QtViewportInteractionEngine::Constraints newConstraints;
211
212     newConstraints.initialScale = attr.initialScale;
213     newConstraints.minimumScale = attr.minimumScale;
214     newConstraints.maximumScale = attr.maximumScale;
215     newConstraints.devicePixelRatio = attr.devicePixelRatio;
216     newConstraints.isUserScalable = !!attr.userScalable;
217
218     return newConstraints;
219 }
220
221 void QQuickWebViewPrivate::runJavaScriptAlert(const QString& alertText)
222 {
223     if (!alertDialog)
224         return;
225
226     Q_Q(QQuickWebView);
227     QtDialogRunner dialogRunner;
228     if (!dialogRunner.initForAlert(alertDialog, q, alertText))
229         return;
230     setViewInAttachedProperties(dialogRunner.dialog());
231
232     disableMouseEvents();
233     dialogRunner.exec();
234     enableMouseEvents();
235 }
236
237 bool QQuickWebViewPrivate::runJavaScriptConfirm(const QString& message)
238 {
239     if (!confirmDialog)
240         return true;
241
242     Q_Q(QQuickWebView);
243     QtDialogRunner dialogRunner;
244     if (!dialogRunner.initForConfirm(confirmDialog, q, message))
245         return true;
246     setViewInAttachedProperties(dialogRunner.dialog());
247
248     disableMouseEvents();
249     dialogRunner.exec();
250     enableMouseEvents();
251
252     return dialogRunner.wasAccepted();
253 }
254
255 QString QQuickWebViewPrivate::runJavaScriptPrompt(const QString& message, const QString& defaultValue, bool& ok)
256 {
257     if (!promptDialog) {
258         ok = true;
259         return defaultValue;
260     }
261
262     Q_Q(QQuickWebView);
263     QtDialogRunner dialogRunner;
264     if (!dialogRunner.initForPrompt(promptDialog, q, message, defaultValue)) {
265         ok = true;
266         return defaultValue;
267     }
268     setViewInAttachedProperties(dialogRunner.dialog());
269
270     disableMouseEvents();
271     dialogRunner.exec();
272     enableMouseEvents();
273
274     ok = dialogRunner.wasAccepted();
275     return dialogRunner.result();
276 }
277
278 void QQuickWebViewPrivate::chooseFiles(WKOpenPanelResultListenerRef listenerRef, const QStringList& selectedFileNames, QtWebPageUIClient::FileChooserType type)
279 {
280 #ifndef QT_NO_FILEDIALOG
281     Q_Q(QQuickWebView);
282     openPanelResultListener = listenerRef;
283
284     // Qt does not support multiple files suggestion, so we get just the first suggestion.
285     QString selectedFileName;
286     if (!selectedFileNames.isEmpty())
287         selectedFileName = selectedFileNames.at(0);
288
289     Q_ASSERT(!fileDialog);
290
291     QWindow* window = q->canvas();
292     if (!window)
293         return;
294
295     fileDialog = new QFileDialog(0, QString(), selectedFileName);
296     fileDialog->window()->winId(); // Ensure that the dialog has a window
297     Q_ASSERT(fileDialog->window()->windowHandle());
298     fileDialog->window()->windowHandle()->setTransientParent(window);
299
300     fileDialog->open(q, SLOT(_q_onOpenPanelFilesSelected()));
301
302     q->connect(fileDialog, SIGNAL(finished(int)), SLOT(_q_onOpenPanelFinished(int)));
303 #endif
304 }
305
306 void QQuickWebViewPrivate::_q_onOpenPanelFilesSelected()
307 {
308     const QStringList fileList = fileDialog->selectedFiles();
309     Vector<RefPtr<APIObject> > wkFiles(fileList.size());
310
311     for (unsigned i = 0; i < fileList.size(); ++i)
312         wkFiles[i] = WebURL::create(QUrl::fromLocalFile(fileList.at(i)).toString());
313
314     WKOpenPanelResultListenerChooseFiles(openPanelResultListener, toAPI(ImmutableArray::adopt(wkFiles).leakRef()));
315 }
316
317 void QQuickWebViewPrivate::_q_onOpenPanelFinished(int result)
318 {
319     if (result == QDialog::Rejected)
320         WKOpenPanelResultListenerCancel(openPanelResultListener);
321
322     fileDialog->deleteLater();
323     fileDialog = 0;
324 }
325
326 void QQuickWebViewPrivate::setUseTraditionalDesktopBehaviour(bool enable)
327 {
328     Q_Q(QQuickWebView);
329     if (enable == useTraditionalDesktopBehaviour)
330         return;
331
332     useTraditionalDesktopBehaviour = enable;
333     if (useTraditionalDesktopBehaviour)
334         initializeDesktop(q);
335     else
336         initializeTouch(q);
337 }
338
339 void QQuickWebViewPrivate::setViewInAttachedProperties(QObject* object)
340 {
341     Q_Q(QQuickWebView);
342     QQuickWebViewAttached* attached = static_cast<QQuickWebViewAttached*>(qmlAttachedPropertiesObject<QQuickWebView>(object));
343     attached->setView(q);
344 }
345
346 static QtPolicyInterface::PolicyAction toPolicyAction(QQuickWebView::NavigationPolicy policy)
347 {
348     switch (policy) {
349     case QQuickWebView::UsePolicy:
350         return QtPolicyInterface::Use;
351     case QQuickWebView::DownloadPolicy:
352         return QtPolicyInterface::Download;
353     case QQuickWebView::IgnorePolicy:
354         return QtPolicyInterface::Ignore;
355     }
356     ASSERT_NOT_REACHED();
357     return QtPolicyInterface::Ignore;
358 }
359
360 static bool hasMetaMethod(QObject* object, const char* methodName)
361 {
362     int methodIndex = object->metaObject()->indexOfMethod(QMetaObject::normalizedSignature(methodName));
363     return methodIndex >= 0 && methodIndex < object->metaObject()->methodCount();
364 }
365
366 /*!
367     \qmlmethod NavigationPolicy DesktopWebView::navigationPolicyForUrl(url, button, modifiers)
368
369     This method should be implemented by the user of DesktopWebView element.
370
371     It will be called to decide the policy for a navigation: whether the WebView should ignore the navigation,
372     continue it or start a download. The return value must be one of the policies in the NavigationPolicy enumeration.
373 */
374 QtPolicyInterface::PolicyAction QQuickWebViewPrivate::navigationPolicyForURL(const QUrl& url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
375 {
376     Q_Q(QQuickWebView);
377     // We need to check this first because invokeMethod() warns if the method doesn't exist for the object.
378     if (!hasMetaMethod(q, "navigationPolicyForUrl(QVariant,QVariant,QVariant)"))
379         return QtPolicyInterface::Use;
380
381     QVariant ret;
382     if (QMetaObject::invokeMethod(q, "navigationPolicyForUrl", Q_RETURN_ARG(QVariant, ret), Q_ARG(QVariant, url), Q_ARG(QVariant, button), Q_ARG(QVariant, QVariant(modifiers))))
383         return toPolicyAction(static_cast<QQuickWebView::NavigationPolicy>(ret.toInt()));
384     return QtPolicyInterface::Use;
385 }
386
387 void QQuickWebViewPrivate::setPageProxy(QtWebPageProxy* pageProxy)
388 {
389     Q_Q(QQuickWebView);
390     this->pageProxy.reset(pageProxy);
391     QObject::connect(pageProxy, SIGNAL(receivedMessageFromNavigatorQtObject(QVariantMap)), q, SIGNAL(messageReceived(QVariantMap)));
392 }
393
394 QQuickWebViewAttached::QQuickWebViewAttached(QObject* object)
395     : QObject(object)
396     , m_view(0)
397 {
398
399 }
400
401 void QQuickWebViewAttached::setView(QQuickWebView* view)
402 {
403     if (m_view == view)
404         return;
405     m_view = view;
406     emit viewChanged();
407 }
408
409 QQuickWebViewExperimental::QQuickWebViewExperimental(QQuickWebView *webView)
410     : QObject(webView)
411     , q_ptr(webView)
412     , d_ptr(webView->d_ptr.data())
413 {
414 }
415
416 QQuickWebViewExperimental::~QQuickWebViewExperimental()
417 {
418 }
419
420 void QQuickWebViewExperimental::setUseTraditionalDesktopBehaviour(bool enable)
421 {
422     Q_D(QQuickWebView);
423     d->setUseTraditionalDesktopBehaviour(enable);
424 }
425
426 QDeclarativeComponent* QQuickWebViewExperimental::alertDialog() const
427 {
428     Q_D(const QQuickWebView);
429     return d->alertDialog;
430 }
431
432 void QQuickWebViewExperimental::setAlertDialog(QDeclarativeComponent* alertDialog)
433 {
434     Q_D(QQuickWebView);
435     if (d->alertDialog == alertDialog)
436         return;
437     d->alertDialog = alertDialog;
438     emit alertDialogChanged();
439 }
440
441 QDeclarativeComponent* QQuickWebViewExperimental::confirmDialog() const
442 {
443     Q_D(const QQuickWebView);
444     return d->confirmDialog;
445 }
446
447 void QQuickWebViewExperimental::setConfirmDialog(QDeclarativeComponent* confirmDialog)
448 {
449     Q_D(QQuickWebView);
450     if (d->confirmDialog == confirmDialog)
451         return;
452     d->confirmDialog = confirmDialog;
453     emit confirmDialogChanged();
454 }
455
456 QDeclarativeComponent* QQuickWebViewExperimental::promptDialog() const
457 {
458     Q_D(const QQuickWebView);
459     return d->promptDialog;
460 }
461
462 void QQuickWebViewExperimental::setPromptDialog(QDeclarativeComponent* promptDialog)
463 {
464     Q_D(QQuickWebView);
465     if (d->promptDialog == promptDialog)
466         return;
467     d->promptDialog = promptDialog;
468     emit promptDialogChanged();
469 }
470
471 QQuickWebView::QQuickWebView(QQuickItem* parent)
472     : QQuickItem(parent)
473     , d_ptr(new QQuickWebViewPrivate(this))
474     , m_experimental(new QQuickWebViewExperimental(this))
475 {
476     Q_D(QQuickWebView);
477     d->initializeTouch(this);
478 }
479
480 QQuickWebView::QQuickWebView(WKContextRef contextRef, WKPageGroupRef pageGroupRef, QQuickItem* parent)
481     : QQuickItem(parent)
482     , d_ptr(new QQuickWebViewPrivate(this, contextRef, pageGroupRef))
483     , m_experimental(new QQuickWebViewExperimental(this))
484 {
485     Q_D(QQuickWebView);
486     // Used by WebKitTestRunner.
487     d->setUseTraditionalDesktopBehaviour(true);
488 }
489
490 QQuickWebView::~QQuickWebView()
491 {
492 }
493
494 QQuickWebPage* QQuickWebView::page()
495 {
496     Q_D(QQuickWebView);
497     return d->pageView.data();
498 }
499
500 void QQuickWebView::load(const QUrl& url)
501 {
502     Q_D(QQuickWebView);
503     d->pageProxy->load(url);
504 }
505
506 void QQuickWebView::postMessage(const QString& message)
507 {
508     Q_D(QQuickWebView);
509     d->pageProxy->postMessageToNavigatorQtObject(message);
510 }
511
512 void QQuickWebView::goBack()
513 {
514     Q_D(QQuickWebView);
515     d->pageProxy->goBack();
516 }
517
518 void QQuickWebView::goForward()
519 {
520     Q_D(QQuickWebView);
521     d->pageProxy->goForward();
522 }
523
524 void QQuickWebView::stop()
525 {
526     Q_D(QQuickWebView);
527     d->pageProxy->stop();
528 }
529
530 void QQuickWebView::reload()
531 {
532     Q_D(QQuickWebView);
533     d->pageProxy->reload();
534 }
535
536 QUrl QQuickWebView::url() const
537 {
538     Q_D(const QQuickWebView);
539     return d->pageProxy->url();
540 }
541
542 int QQuickWebView::loadProgress() const
543 {
544     Q_D(const QQuickWebView);
545     return d->pageLoadClient->loadProgress();
546 }
547
548 bool QQuickWebView::canGoBack() const
549 {
550     Q_D(const QQuickWebView);
551     return d->pageProxy->canGoBack();
552 }
553
554 bool QQuickWebView::canGoForward() const
555 {
556     Q_D(const QQuickWebView);
557     return d->pageProxy->canGoForward();
558 }
559
560 bool QQuickWebView::canStop() const
561 {
562     Q_D(const QQuickWebView);
563     return d->pageProxy->canStop();
564 }
565
566 bool QQuickWebView::canReload() const
567 {
568     Q_D(const QQuickWebView);
569     return d->pageProxy->canReload();
570 }
571
572 QString QQuickWebView::title() const
573 {
574     Q_D(const QQuickWebView);
575     return d->pageProxy->title();
576 }
577
578 QWebPreferences* QQuickWebView::preferences() const
579 {
580     Q_D(const QQuickWebView);
581     return d->pageProxy->preferences();
582 }
583
584 QQuickWebViewExperimental* QQuickWebView::experimental() const
585 {
586     return m_experimental;
587 }
588
589 QQuickWebViewAttached* QQuickWebView::qmlAttachedProperties(QObject* object)
590 {
591     return new QQuickWebViewAttached(object);
592 }
593
594 void QQuickWebView::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
595 {
596     Q_D(QQuickWebView);
597     QQuickItem::geometryChanged(newGeometry, oldGeometry);
598     if (newGeometry.size() != oldGeometry.size()) {
599         if (d->useTraditionalDesktopBehaviour) {
600             d->pageView->setWidth(newGeometry.width());
601             d->pageView->setHeight(newGeometry.height());
602         } else
603             d->updateViewportSize();
604     }
605 }
606
607 void QQuickWebView::focusInEvent(QFocusEvent* event)
608 {
609     Q_D(QQuickWebView);
610     d->pageView->event(event);
611 }
612
613 void QQuickWebView::focusOutEvent(QFocusEvent* event)
614 {
615     Q_D(QQuickWebView);
616     d->pageView->event(event);
617 }
618
619 void QQuickWebView::touchEvent(QTouchEvent* event)
620 {
621     forceActiveFocus();
622     QQuickItem::touchEvent(event);
623 }
624
625 WKPageRef QQuickWebView::pageRef() const
626 {
627     Q_D(const QQuickWebView);
628     return d->pageProxy->pageRef();
629 }
630
631 /*!
632     Loads the specified \a html as the content of the web view.
633
634     External objects such as stylesheets or images referenced in the HTML
635     document are located relative to \a baseUrl.
636
637     \sa load()
638 */
639 void QQuickWebView::loadHtml(const QString& html, const QUrl& baseUrl)
640 {
641     Q_D(QQuickWebView);
642     d->pageProxy->loadHTMLString(html, baseUrl);
643 }
644
645 #include "moc_qquickwebview_p.cpp"