1/*
2    Copyright (C) 2009 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 library 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 library; 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 "qdeclarativewebview_p.h"
22
23#include <QtCore/QDebug>
24#include <QtCore/QEvent>
25#include <QtCore/QFile>
26#include <QtDeclarative/QDeclarativeContext>
27#include <QtDeclarative/QDeclarativeEngine>
28#include <QtDeclarative/qdeclarative.h>
29#include <QtGui/QApplication>
30#include <QtGui/QGraphicsSceneMouseEvent>
31#include <QtGui/QKeyEvent>
32#include <QtGui/QMouseEvent>
33#include <QtGui/QPen>
34#include "qwebelement.h"
35#include "qwebframe.h"
36#include "qwebpage.h"
37#include "qwebsettings.h"
38
39QT_BEGIN_NAMESPACE
40
41class QDeclarativeWebViewPrivate {
42public:
43    QDeclarativeWebViewPrivate(QDeclarativeWebView* qq)
44      : q(qq)
45      , preferredwidth(0)
46      , preferredheight(0)
47      , progress(1.0)
48      , status(QDeclarativeWebView::Null)
49      , pending(PendingNone)
50      , newWindowComponent(0)
51      , newWindowParent(0)
52      , rendering(true)
53    {
54    }
55
56    QDeclarativeWebView* q;
57
58    QUrl url; // page url might be different if it has not loaded yet
59    GraphicsWebView* view;
60
61    int preferredwidth, preferredheight;
62    qreal progress;
63    QDeclarativeWebView::Status status;
64    QString statusText;
65    enum { PendingNone, PendingUrl, PendingHtml, PendingContent } pending;
66    QUrl pendingUrl;
67    QString pendingString;
68    QByteArray pendingData;
69    mutable QDeclarativeWebSettings settings;
70    QDeclarativeComponent* newWindowComponent;
71    QDeclarativeItem* newWindowParent;
72
73    static void windowObjectsAppend(QDeclarativeListProperty<QObject>* prop, QObject* o)
74    {
75        static_cast<QDeclarativeWebViewPrivate*>(prop->data)->windowObjects.append(o);
76        static_cast<QDeclarativeWebViewPrivate*>(prop->data)->updateWindowObjects();
77    }
78
79    void updateWindowObjects();
80    QObjectList windowObjects;
81
82    bool rendering;
83};
84
85GraphicsWebView::GraphicsWebView(QDeclarativeWebView* parent)
86    : QGraphicsWebView(parent)
87    , parent(parent)
88    , pressTime(400)
89{
90}
91
92void GraphicsWebView::mousePressEvent(QGraphicsSceneMouseEvent* event)
93{
94    pressPoint = event->pos();
95    if (pressTime) {
96        pressTimer.start(pressTime, this);
97        parent->setKeepMouseGrab(false);
98    } else {
99        grabMouse();
100        parent->setKeepMouseGrab(true);
101    }
102    QGraphicsWebView::mousePressEvent(event);
103
104    QWebHitTestResult hit = page()->mainFrame()->hitTestContent(pressPoint.toPoint());
105    if (hit.isContentEditable())
106        parent->forceActiveFocus();
107    setFocus();
108}
109
110void GraphicsWebView::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
111{
112    QGraphicsWebView::mouseReleaseEvent(event);
113    pressTimer.stop();
114    parent->setKeepMouseGrab(false);
115    ungrabMouse();
116}
117
118void GraphicsWebView::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
119{
120    QMouseEvent* me = new QMouseEvent(QEvent::MouseButtonDblClick, (event->pos() / parent->contentsScale()).toPoint(), event->button(), event->buttons(), 0);
121    emit doubleClick(event->pos().x(), event->pos().y());
122    delete me;
123}
124
125void GraphicsWebView::timerEvent(QTimerEvent* event)
126{
127    if (event->timerId() == pressTimer.timerId()) {
128        pressTimer.stop();
129        grabMouse();
130        parent->setKeepMouseGrab(true);
131    }
132}
133
134void GraphicsWebView::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
135{
136    if (pressTimer.isActive()) {
137        if ((event->pos() - pressPoint).manhattanLength() > QApplication::startDragDistance())
138            pressTimer.stop();
139    }
140    if (parent->keepMouseGrab())
141        QGraphicsWebView::mouseMoveEvent(event);
142}
143
144bool GraphicsWebView::sceneEvent(QEvent *event)
145{
146    bool rv = QGraphicsWebView::sceneEvent(event);
147    if (event->type() == QEvent::UngrabMouse) {
148        pressTimer.stop();
149        parent->setKeepMouseGrab(false);
150    }
151    return rv;
152}
153
154/*!
155    \qmlclass WebView QDeclarativeWebView
156    \ingroup qml-view-elements
157    \since 4.7
158    \brief The WebView item allows you to add Web content to a canvas.
159    \inherits Item
160
161    A WebView renders Web content based on a URL.
162
163    This type is made available by importing the \c QtWebKit module:
164
165    \bold{import QtWebKit 1.0}
166
167    The WebView item includes no scrolling, scaling, toolbars, or other common browser
168    components. These must be implemented around WebView. See the \l{QML Web Browser}
169    example for a demonstration of this.
170
171    The page to be displayed by the item is specified using the \l url property,
172    and this can be changed to fetch and display a new page. While the page loads,
173    the \l progress property is updated to indicate how much of the page has been
174    loaded.
175
176    \section1 Appearance
177
178    If the width and height of the item is not set, they will dynamically adjust
179    to a size appropriate for the content. This width may be large for typical
180    online web pages, typically greater than 800 by 600 pixels.
181
182    If the \l{Item::}{width} or \l{Item::}{height} is explictly set, the rendered Web site will be
183    clipped, not scaled, to fit into the set dimensions.
184
185    If the preferredWidth property is set, the width will be this amount or larger,
186    usually laying out the Web content to fit the preferredWidth.
187
188    The appearance of the content can be controlled to a certain extent by changing
189    the settings.standardFontFamily property and other settings related to fonts.
190
191    The page can be zoomed by calling the heuristicZoom() method, which performs a
192    series of tests to determine whether zoomed content will be displayed in an
193    appropriate way in the space allocated to the item.
194
195    \section1 User Interaction and Navigation
196
197    By default, certain mouse and touch events are delivered to other items in
198    preference to the Web content. For example, when a scrolling view is created
199    by placing a WebView in a Flickable, move events are delivered to the Flickable
200    so that the user can scroll the page. This prevents the user from accidentally
201    selecting text in a Web page instead of scrolling.
202
203    The pressGrabTime property defines the time the user must touch or press a
204    mouse button over the WebView before the Web content will receive the move
205    events it needs to select text and images.
206
207    When this item has keyboard focus, all keyboard input will be sent directly to
208    the Web page within.
209
210    When the navigates by clicking on links, the item records the pages visited
211    in its internal history
212
213    Because this item is designed to be used as a component in a browser, it
214    exposes \l{Action}{actions} for \l back, \l forward, \l reload and \l stop.
215    These can be triggered to change the current page displayed by the item.
216
217    \section1 Example Usage
218
219    \beginfloatright
220    \inlineimage webview.png
221    \endfloat
222
223    The following example displays a scaled down Web page at a fixed size.
224
225    \snippet doc/src/snippets/declarative/webview/webview.qml document
226
227    \clearfloat
228
229    \sa {declarative/modelviews/webview}{WebView example}, {demos/declarative/webbrowser}{Web Browser demo}
230*/
231
232/*!
233    \internal
234    \class QDeclarativeWebView
235    \brief The QDeclarativeWebView class allows you to add web content to a QDeclarativeView.
236
237    A WebView renders web content base on a URL.
238
239    \image webview.png
240
241    The item includes no scrolling, scaling,
242    toolbars, etc., those must be implemented around WebView. See the WebBrowser example
243    for a demonstration of this.
244
245    A QDeclarativeWebView object can be instantiated in Qml using the tag \l WebView.
246*/
247
248QDeclarativeWebView::QDeclarativeWebView(QDeclarativeItem *parent) : QDeclarativeItem(parent)
249{
250    init();
251}
252
253QDeclarativeWebView::~QDeclarativeWebView()
254{
255    delete d;
256}
257
258void QDeclarativeWebView::init()
259{
260    d = new QDeclarativeWebViewPrivate(this);
261
262    if (QWebSettings::iconDatabasePath().isNull() &&
263        QWebSettings::globalSettings()->localStoragePath().isNull() &&
264        QWebSettings::offlineStoragePath().isNull() &&
265        QWebSettings::offlineWebApplicationCachePath().isNull())
266        QWebSettings::enablePersistentStorage();
267
268    setAcceptedMouseButtons(Qt::LeftButton);
269    setFlag(QGraphicsItem::ItemHasNoContents, true);
270    setFlag(QGraphicsItem::ItemIsFocusScope, true);
271    setClip(true);
272
273    d->view = new GraphicsWebView(this);
274    d->view->setResizesToContents(true);
275    d->view->setFocus();
276    QWebPage* wp = new QDeclarativeWebPage(this);
277    setPage(wp);
278    if (!preferredWidth())
279        setPreferredWidth(d->view->preferredWidth());
280    if (!preferredHeight())
281        setPreferredHeight(d->view->preferredHeight());
282    connect(d->view, SIGNAL(geometryChanged()), this, SLOT(updateDeclarativeWebViewSize()));
283    connect(d->view, SIGNAL(doubleClick(int, int)), this, SIGNAL(doubleClick(int, int)));
284    connect(d->view, SIGNAL(scaleChanged()), this, SIGNAL(contentsScaleChanged()));
285}
286
287void QDeclarativeWebView::componentComplete()
288{
289    QDeclarativeItem::componentComplete();
290    page()->setNetworkAccessManager(qmlEngine(this)->networkAccessManager());
291
292    switch (d->pending) {
293    case QDeclarativeWebViewPrivate::PendingUrl:
294        setUrl(d->pendingUrl);
295        break;
296    case QDeclarativeWebViewPrivate::PendingHtml:
297        setHtml(d->pendingString, d->pendingUrl);
298        break;
299    case QDeclarativeWebViewPrivate::PendingContent:
300        setContent(d->pendingData, d->pendingString, d->pendingUrl);
301        break;
302    default:
303        break;
304    }
305    d->pending = QDeclarativeWebViewPrivate::PendingNone;
306    d->updateWindowObjects();
307}
308
309QDeclarativeWebView::Status QDeclarativeWebView::status() const
310{
311    return d->status;
312}
313
314
315/*!
316    \qmlproperty real WebView::progress
317    This property holds the progress of loading the current URL, from 0 to 1.
318
319    If you just want to know when progress gets to 1, use
320    WebView::onLoadFinished() or WebView::onLoadFailed() instead.
321*/
322qreal QDeclarativeWebView::progress() const
323{
324    return d->progress;
325}
326
327void QDeclarativeWebView::doLoadStarted()
328{
329    if (!d->url.isEmpty()) {
330        d->status = Loading;
331        emit statusChanged(d->status);
332    }
333    emit loadStarted();
334}
335
336void QDeclarativeWebView::doLoadProgress(int p)
337{
338    if (d->progress == p / 100.0)
339        return;
340    d->progress = p / 100.0;
341    emit progressChanged();
342}
343
344void QDeclarativeWebView::pageUrlChanged()
345{
346    updateContentsSize();
347
348    if ((d->url.isEmpty() && page()->mainFrame()->url() != QUrl(QLatin1String("about:blank")))
349        || (d->url != page()->mainFrame()->url() && !page()->mainFrame()->url().isEmpty()))
350    {
351        d->url = page()->mainFrame()->url();
352        if (d->url == QUrl(QLatin1String("about:blank")))
353            d->url = QUrl();
354        emit urlChanged();
355    }
356}
357
358void QDeclarativeWebView::doLoadFinished(bool ok)
359{
360    if (ok) {
361        d->status = d->url.isEmpty() ? Null : Ready;
362        emit loadFinished();
363    } else {
364        d->status = Error;
365        emit loadFailed();
366    }
367    emit statusChanged(d->status);
368}
369
370/*!
371    \qmlproperty url WebView::url
372    This property holds the URL to the page displayed in this item. It can be set,
373    but also can change spontaneously (eg. because of network redirection).
374
375    If the url is empty, the page is blank.
376
377    The url is always absolute (QML will resolve relative URL strings in the context
378    of the containing QML document).
379*/
380QUrl QDeclarativeWebView::url() const
381{
382    return d->url;
383}
384
385void QDeclarativeWebView::setUrl(const QUrl& url)
386{
387    if (url == d->url)
388        return;
389
390    if (isComponentComplete()) {
391        d->url = url;
392        updateContentsSize();
393        QUrl seturl = url;
394        if (seturl.isEmpty())
395            seturl = QUrl(QLatin1String("about:blank"));
396
397        Q_ASSERT(!seturl.isRelative());
398
399        page()->mainFrame()->load(seturl);
400
401        emit urlChanged();
402    } else {
403        d->pending = d->PendingUrl;
404        d->pendingUrl = url;
405    }
406}
407
408/*!
409    \qmlproperty int WebView::preferredWidth
410    This property holds the ideal width for displaying the current URL.
411*/
412int QDeclarativeWebView::preferredWidth() const
413{
414    return d->preferredwidth;
415}
416
417void QDeclarativeWebView::setPreferredWidth(int width)
418{
419    if (d->preferredwidth == width)
420        return;
421    d->preferredwidth = width;
422    updateContentsSize();
423    emit preferredWidthChanged();
424}
425
426/*!
427    \qmlproperty int WebView::preferredHeight
428    This property holds the ideal height for displaying the current URL.
429    This only affects the area zoomed by heuristicZoom().
430*/
431int QDeclarativeWebView::preferredHeight() const
432{
433    return d->preferredheight;
434}
435
436void QDeclarativeWebView::setPreferredHeight(int height)
437{
438    if (d->preferredheight == height)
439        return;
440    d->preferredheight = height;
441    updateContentsSize();
442    emit preferredHeightChanged();
443}
444
445/*!
446    \qmlmethod bool WebView::evaluateJavaScript(string scriptSource)
447
448    Evaluates the \a scriptSource JavaScript inside the context of the
449    main web frame, and returns the result of the last executed statement.
450
451    Note that this JavaScript does \e not have any access to QML objects
452    except as made available as windowObjects.
453*/
454QVariant QDeclarativeWebView::evaluateJavaScript(const QString& scriptSource)
455{
456    return this->page()->mainFrame()->evaluateJavaScript(scriptSource);
457}
458
459void QDeclarativeWebView::updateDeclarativeWebViewSize()
460{
461    QSizeF size = d->view->geometry().size() * contentsScale();
462    setImplicitWidth(size.width());
463    setImplicitHeight(size.height());
464}
465
466void QDeclarativeWebView::initialLayout()
467{
468    // nothing useful to do at this point
469}
470
471void QDeclarativeWebView::updateContentsSize()
472{
473    if (page()) {
474        page()->setPreferredContentsSize(QSize(
475            d->preferredwidth>0 ? d->preferredwidth : width(),
476            d->preferredheight>0 ? d->preferredheight : height()));
477    }
478}
479
480void QDeclarativeWebView::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry)
481{
482    QWebPage* webPage = page();
483    if (newGeometry.size() != oldGeometry.size() && webPage) {
484        QSize contentSize = webPage->preferredContentsSize();
485        if (widthValid())
486            contentSize.setWidth(width());
487        if (heightValid())
488            contentSize.setHeight(height());
489        if (contentSize != webPage->preferredContentsSize())
490            webPage->setPreferredContentsSize(contentSize);
491    }
492    QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
493}
494
495/*!
496    \qmlproperty list<object> WebView::javaScriptWindowObjects
497
498    A list of QML objects to expose to the web page.
499
500    Each object will be added as a property of the web frame's window object.  The
501    property name is controlled by the value of \c WebView.windowObjectName
502    attached property.
503
504    Exposing QML objects to a web page allows JavaScript executing in the web
505    page itself to communicate with QML, by reading and writing properties and
506    by calling methods of the exposed QML objects.
507
508    This example shows how to call into a QML method using a window object.
509
510    \qml
511    WebView {
512        javaScriptWindowObjects: QtObject {
513            WebView.windowObjectName: "qml"
514
515            function qmlCall() {
516                console.log("This call is in QML!");
517            }
518        }
519
520        html: "<script>window.qml.qmlCall();</script>"
521    }
522    \endqml
523
524    The output of the example will be:
525    \code
526    This call is in QML!
527    \endcode
528
529    If Javascript is not enabled for the page, then this property does nothing.
530*/
531QDeclarativeListProperty<QObject> QDeclarativeWebView::javaScriptWindowObjects()
532{
533    return QDeclarativeListProperty<QObject>(this, d, &QDeclarativeWebViewPrivate::windowObjectsAppend);
534}
535
536QDeclarativeWebViewAttached* QDeclarativeWebView::qmlAttachedProperties(QObject* o)
537{
538    return new QDeclarativeWebViewAttached(o);
539}
540
541void QDeclarativeWebViewPrivate::updateWindowObjects()
542{
543    if (!q->isComponentCompletePublic() || !q->page())
544        return;
545
546    for (int i = 0; i < windowObjects.count(); ++i) {
547        QObject* object = windowObjects.at(i);
548        QDeclarativeWebViewAttached* attached = static_cast<QDeclarativeWebViewAttached *>(qmlAttachedPropertiesObject<QDeclarativeWebView>(object));
549        if (attached && !attached->windowObjectName().isEmpty())
550            q->page()->mainFrame()->addToJavaScriptWindowObject(attached->windowObjectName(), object);
551    }
552}
553
554bool QDeclarativeWebView::renderingEnabled() const
555{
556    return d->rendering;
557}
558
559void QDeclarativeWebView::setRenderingEnabled(bool enabled)
560{
561    if (d->rendering == enabled)
562        return;
563    d->rendering = enabled;
564    emit renderingEnabledChanged();
565    d->view->setTiledBackingStoreFrozen(!enabled);
566}
567
568/*!
569    \qmlsignal WebView::onDoubleClick(int clickx, int clicky)
570
571    The WebView does not pass double-click events to the web engine, but rather
572    emits this signals.
573*/
574
575/*!
576    \qmlmethod bool WebView::heuristicZoom(int clickX, int clickY, real maxzoom)
577
578    Finds a zoom that:
579    \list
580    \i shows a whole item
581    \i includes (\a clickX, \a clickY)
582    \i fits into the preferredWidth and preferredHeight
583    \i zooms by no more than \a maxZoom
584    \i is more than 10% above the current zoom
585    \endlist
586
587    If such a zoom exists, emits zoomTo(zoom,centerX,centerY) and returns true; otherwise,
588    no signal is emitted and returns false.
589*/
590bool QDeclarativeWebView::heuristicZoom(int clickX, int clickY, qreal maxZoom)
591{
592    if (contentsScale() >= maxZoom / scale())
593        return false;
594    qreal ozf = contentsScale();
595    QRect showArea = elementAreaAt(clickX, clickY, d->preferredwidth / maxZoom, d->preferredheight / maxZoom);
596    qreal z = qMin(qreal(d->preferredwidth) / showArea.width(), qreal(d->preferredheight) / showArea.height());
597    if (z > maxZoom / scale())
598        z = maxZoom / scale();
599    if (z / ozf > 1.2) {
600        QRectF r(showArea.left() * z, showArea.top() * z, showArea.width() * z, showArea.height() * z);
601        emit zoomTo(z, r.x() + r.width() / 2, r.y() + r.height() / 2);
602        return true;
603    }
604    return false;
605}
606
607/*!
608    \qmlproperty int WebView::pressGrabTime
609
610    The number of milliseconds the user must press before the WebView
611    starts passing move events through to the Web engine (rather than
612    letting other QML elements such as a Flickable take them).
613
614    Defaults to 400ms. Set to 0 to always grab and pass move events to
615    the Web engine.
616*/
617int QDeclarativeWebView::pressGrabTime() const
618{
619    return d->view->pressTime;
620}
621
622void QDeclarativeWebView::setPressGrabTime(int millis)
623{
624    if (d->view->pressTime == millis)
625        return;
626    d->view->pressTime = millis;
627    emit pressGrabTimeChanged();
628}
629
630#ifndef QT_NO_ACTION
631/*!
632    \qmlproperty action WebView::back
633    This property holds the action for causing the previous URL in the history to be displayed.
634*/
635QAction* QDeclarativeWebView::backAction() const
636{
637    return page()->action(QWebPage::Back);
638}
639
640/*!
641    \qmlproperty action WebView::forward
642    This property holds the action for causing the next URL in the history to be displayed.
643*/
644QAction* QDeclarativeWebView::forwardAction() const
645{
646    return page()->action(QWebPage::Forward);
647}
648
649/*!
650    \qmlproperty action WebView::reload
651    This property holds the action for reloading with the current URL
652*/
653QAction* QDeclarativeWebView::reloadAction() const
654{
655    return page()->action(QWebPage::Reload);
656}
657
658/*!
659    \qmlproperty action WebView::stop
660    This property holds the action for stopping loading with the current URL
661*/
662QAction* QDeclarativeWebView::stopAction() const
663{
664    return page()->action(QWebPage::Stop);
665}
666#endif // QT_NO_ACTION
667
668/*!
669    \qmlproperty string WebView::title
670    This property holds the title of the web page currently viewed
671
672    By default, this property contains an empty string.
673*/
674QString QDeclarativeWebView::title() const
675{
676    return page()->mainFrame()->title();
677}
678
679/*!
680    \qmlproperty pixmap WebView::icon
681    This property holds the icon associated with the web page currently viewed
682*/
683QPixmap QDeclarativeWebView::icon() const
684{
685    return page()->mainFrame()->icon().pixmap(QSize(256, 256));
686}
687
688/*!
689    \qmlproperty string WebView::statusText
690
691    This property is the current status suggested by the current web page. In a web browser,
692    such status is often shown in some kind of status bar.
693*/
694void QDeclarativeWebView::setStatusText(const QString& text)
695{
696    d->statusText = text;
697    emit statusTextChanged();
698}
699
700void QDeclarativeWebView::windowObjectCleared()
701{
702    d->updateWindowObjects();
703}
704
705QString QDeclarativeWebView::statusText() const
706{
707    return d->statusText;
708}
709
710QWebPage* QDeclarativeWebView::page() const
711{
712    return d->view->page();
713}
714
715// The QObject interface to settings().
716/*!
717    \qmlproperty string WebView::settings.standardFontFamily
718    \qmlproperty string WebView::settings.fixedFontFamily
719    \qmlproperty string WebView::settings.serifFontFamily
720    \qmlproperty string WebView::settings.sansSerifFontFamily
721    \qmlproperty string WebView::settings.cursiveFontFamily
722    \qmlproperty string WebView::settings.fantasyFontFamily
723
724    \qmlproperty int WebView::settings.minimumFontSize
725    \qmlproperty int WebView::settings.minimumLogicalFontSize
726    \qmlproperty int WebView::settings.defaultFontSize
727    \qmlproperty int WebView::settings.defaultFixedFontSize
728
729    \qmlproperty bool WebView::settings.autoLoadImages
730    \qmlproperty bool WebView::settings.javascriptEnabled
731    \qmlproperty bool WebView::settings.javaEnabled
732    \qmlproperty bool WebView::settings.pluginsEnabled
733    \qmlproperty bool WebView::settings.privateBrowsingEnabled
734    \qmlproperty bool WebView::settings.javascriptCanOpenWindows
735    \qmlproperty bool WebView::settings.javascriptCanAccessClipboard
736    \qmlproperty bool WebView::settings.developerExtrasEnabled
737    \qmlproperty bool WebView::settings.linksIncludedInFocusChain
738    \qmlproperty bool WebView::settings.zoomTextOnly
739    \qmlproperty bool WebView::settings.printElementBackgrounds
740    \qmlproperty bool WebView::settings.offlineStorageDatabaseEnabled
741    \qmlproperty bool WebView::settings.offlineWebApplicationCacheEnabled
742    \qmlproperty bool WebView::settings.localStorageDatabaseEnabled
743    \qmlproperty bool WebView::settings.localContentCanAccessRemoteUrls
744
745    These properties give access to the settings controlling the web view.
746
747    See QWebSettings for details of these properties.
748
749    \qml
750    WebView {
751        settings.pluginsEnabled: true
752        settings.standardFontFamily: "Arial"
753        // ...
754    }
755    \endqml
756*/
757QDeclarativeWebSettings* QDeclarativeWebView::settingsObject() const
758{
759    d->settings.s = page()->settings();
760    return &d->settings;
761}
762
763void QDeclarativeWebView::setPage(QWebPage* page)
764{
765    if (d->view->page() == page)
766        return;
767
768    d->view->setPage(page);
769    updateContentsSize();
770    page->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
771    page->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
772    connect(page->mainFrame(), SIGNAL(urlChanged(QUrl)), this, SLOT(pageUrlChanged()));
773    connect(page->mainFrame(), SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged(QString)));
774    connect(page->mainFrame(), SIGNAL(iconChanged()), this, SIGNAL(iconChanged()));
775    connect(page->mainFrame(), SIGNAL(initialLayoutCompleted()), this, SLOT(initialLayout()));
776    connect(page->mainFrame(), SIGNAL(contentsSizeChanged(QSize)), this, SIGNAL(contentsSizeChanged(QSize)));
777
778    connect(page, SIGNAL(loadStarted()), this, SLOT(doLoadStarted()));
779    connect(page, SIGNAL(loadProgress(int)), this, SLOT(doLoadProgress(int)));
780    connect(page, SIGNAL(loadFinished(bool)), this, SLOT(doLoadFinished(bool)));
781    connect(page, SIGNAL(statusBarMessage(QString)), this, SLOT(setStatusText(QString)));
782
783    connect(page->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(windowObjectCleared()));
784
785    page->settings()->setAttribute(QWebSettings::TiledBackingStoreEnabled, true);
786
787}
788
789/*!
790    \qmlsignal WebView::onLoadStarted()
791
792    This handler is called when the web engine begins loading
793    a page. Later, WebView::onLoadFinished() or WebView::onLoadFailed()
794    will be emitted.
795*/
796
797/*!
798    \qmlsignal WebView::onLoadFinished()
799
800    This handler is called when the web engine \e successfully
801    finishes loading a page, including any component content
802    (WebView::onLoadFailed() will be emitted otherwise).
803
804    \sa progress
805*/
806
807/*!
808    \qmlsignal WebView::onLoadFailed()
809
810    This handler is called when the web engine fails loading
811    a page or any component content
812    (WebView::onLoadFinished() will be emitted on success).
813*/
814
815void QDeclarativeWebView::load(const QNetworkRequest& request, QNetworkAccessManager::Operation operation, const QByteArray& body)
816{
817    page()->mainFrame()->load(request, operation, body);
818}
819
820QString QDeclarativeWebView::html() const
821{
822    return page()->mainFrame()->toHtml();
823}
824
825/*!
826    \qmlproperty string WebView::html
827    This property holds HTML text set directly
828
829    The html property can be set as a string.
830
831    \qml
832    WebView {
833        html: "<p>This is <b>HTML</b>."
834    }
835    \endqml
836*/
837void QDeclarativeWebView::setHtml(const QString& html, const QUrl& baseUrl)
838{
839    updateContentsSize();
840    if (isComponentComplete())
841        page()->mainFrame()->setHtml(html, baseUrl);
842    else {
843        d->pending = d->PendingHtml;
844        d->pendingUrl = baseUrl;
845        d->pendingString = html;
846    }
847    emit htmlChanged();
848}
849
850void QDeclarativeWebView::setContent(const QByteArray& data, const QString& mimeType, const QUrl& baseUrl)
851{
852    updateContentsSize();
853
854    if (isComponentComplete())
855        page()->mainFrame()->setContent(data, mimeType, qmlContext(this)->resolvedUrl(baseUrl));
856    else {
857        d->pending = d->PendingContent;
858        d->pendingUrl = baseUrl;
859        d->pendingString = mimeType;
860        d->pendingData = data;
861    }
862}
863
864QWebHistory* QDeclarativeWebView::history() const
865{
866    return page()->history();
867}
868
869QWebSettings* QDeclarativeWebView::settings() const
870{
871    return page()->settings();
872}
873
874QDeclarativeWebView* QDeclarativeWebView::createWindow(QWebPage::WebWindowType type)
875{
876    switch (type) {
877    case QWebPage::WebBrowserWindow: {
878        if (!d->newWindowComponent && d->newWindowParent)
879            qWarning("WebView::newWindowComponent not set - WebView::newWindowParent ignored");
880        else if (d->newWindowComponent && !d->newWindowParent)
881            qWarning("WebView::newWindowParent not set - WebView::newWindowComponent ignored");
882        else if (d->newWindowComponent && d->newWindowParent) {
883            QDeclarativeWebView* webview = 0;
884            QDeclarativeContext* windowContext = new QDeclarativeContext(qmlContext(this));
885
886            QObject* newObject = d->newWindowComponent->create(windowContext);
887            if (newObject) {
888                windowContext->setParent(newObject);
889                QDeclarativeItem* item = qobject_cast<QDeclarativeItem *>(newObject);
890                if (!item)
891                    delete newObject;
892                else {
893                    webview = item->findChild<QDeclarativeWebView*>();
894                    if (!webview)
895                        delete item;
896                    else {
897                        newObject->setParent(d->newWindowParent);
898                        static_cast<QGraphicsObject*>(item)->setParentItem(d->newWindowParent);
899                    }
900                }
901            } else
902                delete windowContext;
903
904            return webview;
905        }
906    }
907    break;
908    case QWebPage::WebModalDialog: {
909        // Not supported
910    }
911    }
912    return 0;
913}
914
915/*!
916    \qmlproperty component WebView::newWindowComponent
917
918    This property holds the component to use for new windows.
919    The component must have a WebView somewhere in its structure.
920
921    When the web engine requests a new window, it will be an instance of
922    this component.
923
924    The parent of the new window is set by newWindowParent. It must be set.
925*/
926QDeclarativeComponent* QDeclarativeWebView::newWindowComponent() const
927{
928    return d->newWindowComponent;
929}
930
931void QDeclarativeWebView::setNewWindowComponent(QDeclarativeComponent* newWindow)
932{
933    if (newWindow == d->newWindowComponent)
934        return;
935    d->newWindowComponent = newWindow;
936    emit newWindowComponentChanged();
937}
938
939
940/*!
941    \qmlproperty item WebView::newWindowParent
942
943    The parent item for new windows.
944
945    \sa newWindowComponent
946*/
947QDeclarativeItem* QDeclarativeWebView::newWindowParent() const
948{
949    return d->newWindowParent;
950}
951
952void QDeclarativeWebView::setNewWindowParent(QDeclarativeItem* parent)
953{
954    if (parent == d->newWindowParent)
955        return;
956    if (d->newWindowParent && parent) {
957        QList<QGraphicsItem *> children = d->newWindowParent->childItems();
958        for (int i = 0; i < children.count(); ++i)
959            children.at(i)->setParentItem(parent);
960    }
961    d->newWindowParent = parent;
962    emit newWindowParentChanged();
963}
964
965QSize QDeclarativeWebView::contentsSize() const
966{
967    return page()->mainFrame()->contentsSize() * contentsScale();
968}
969
970qreal QDeclarativeWebView::contentsScale() const
971{
972    return d->view->scale();
973}
974
975void QDeclarativeWebView::setContentsScale(qreal scale)
976{
977    if (scale == d->view->scale())
978        return;
979    d->view->setScale(scale);
980    updateDeclarativeWebViewSize();
981    emit contentsScaleChanged();
982}
983
984#if QT_VERSION >= 0x040703
985/*!
986    \qmlproperty color WebView::backgroundColor
987    \since QtWebKit 1.1
988    This property holds the background color of the view.
989*/
990
991QColor QDeclarativeWebView::backgroundColor() const
992{
993    return d->view->palette().base().color();
994}
995
996void QDeclarativeWebView::setBackgroundColor(const QColor& color)
997{
998    QPalette palette = d->view->palette();
999    if (palette.base().color() == color)
1000        return;
1001    palette.setBrush(QPalette::Base, color);
1002    d->view->setPalette(palette);
1003    emit backgroundColorChanged();
1004}
1005#endif
1006
1007/*!
1008    Returns the area of the largest element at position (\a x,\a y) that is no larger
1009    than \a maxWidth by \a maxHeight pixels.
1010
1011    May return an area larger in the case when no smaller element is at the position.
1012*/
1013QRect QDeclarativeWebView::elementAreaAt(int x, int y, int maxWidth, int maxHeight) const
1014{
1015    QWebHitTestResult hit = page()->mainFrame()->hitTestContent(QPoint(x, y));
1016    QRect hitRect = hit.boundingRect();
1017    QWebElement element = hit.enclosingBlockElement();
1018    if (maxWidth <= 0)
1019        maxWidth = INT_MAX;
1020    if (maxHeight <= 0)
1021        maxHeight = INT_MAX;
1022    while (!element.parent().isNull() && element.geometry().width() <= maxWidth && element.geometry().height() <= maxHeight) {
1023        hitRect = element.geometry();
1024        element = element.parent();
1025    }
1026    return hitRect;
1027}
1028
1029/*!
1030    \internal
1031    \class QDeclarativeWebPage
1032    \brief The QDeclarativeWebPage class is a QWebPage that can create QML plugins.
1033
1034    \sa QDeclarativeWebView
1035*/
1036QDeclarativeWebPage::QDeclarativeWebPage(QDeclarativeWebView* parent) :
1037    QWebPage(parent)
1038{
1039}
1040
1041QDeclarativeWebPage::~QDeclarativeWebPage()
1042{
1043}
1044
1045QString QDeclarativeWebPage::chooseFile(QWebFrame* originatingFrame, const QString& oldFile)
1046{
1047    // Not supported (it's modal)
1048    Q_UNUSED(originatingFrame)
1049    Q_UNUSED(oldFile)
1050    return oldFile;
1051}
1052
1053/*!
1054    \qmlsignal WebView::onAlert(string message)
1055
1056    The handler is called when the web engine sends a JavaScript alert. The \a message is the text
1057    to be displayed in the alert to the user.
1058*/
1059
1060
1061void QDeclarativeWebPage::javaScriptAlert(QWebFrame* originatingFrame, const QString& msg)
1062{
1063    Q_UNUSED(originatingFrame)
1064    emit viewItem()->alert(msg);
1065}
1066
1067bool QDeclarativeWebPage::javaScriptConfirm(QWebFrame* originatingFrame, const QString& msg)
1068{
1069    // Not supported (it's modal)
1070    Q_UNUSED(originatingFrame)
1071    Q_UNUSED(msg)
1072    return false;
1073}
1074
1075bool QDeclarativeWebPage::javaScriptPrompt(QWebFrame* originatingFrame, const QString& msg, const QString& defaultValue, QString* result)
1076{
1077    // Not supported (it's modal)
1078    Q_UNUSED(originatingFrame)
1079    Q_UNUSED(msg)
1080    Q_UNUSED(defaultValue)
1081    Q_UNUSED(result)
1082    return false;
1083}
1084
1085
1086QDeclarativeWebView* QDeclarativeWebPage::viewItem()
1087{
1088    return static_cast<QDeclarativeWebView*>(parent());
1089}
1090
1091QWebPage* QDeclarativeWebPage::createWindow(WebWindowType type)
1092{
1093    QDeclarativeWebView* newView = viewItem()->createWindow(type);
1094    if (newView)
1095        return newView->page();
1096    return 0;
1097}
1098
1099QT_END_NAMESPACE
1100
1101