1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickwebview_p.h"
5#include "qquickwebviewloadrequest_p.h"
6#include "qquickwebviewsettings_p.h"
7#include <QtWebView/private/qwebviewloadrequest_p.h>
8#include <QtQml/qqmlengine.h>
9#include <QtCore/qmutex.h>
10
11#if defined(Q_OS_WASM)
12#include <QtQuick/private/qquickrendercontrol_p.h>
13#endif // Q_OS_WASM
14
15namespace {
16
17class CallbackStorage
18{
19public:
20 int insertCallback(const QJSValue &callback)
21 {
22 QMutexLocker locker(&m_mtx);
23 const int nextId = qMax(a: ++m_counter, b: 0);
24 if (nextId == 0)
25 m_counter = 1;
26
27 m_callbacks.insert(key: nextId, value: callback);
28 return nextId;
29 }
30
31 QJSValue takeCallback(int callbackId)
32 {
33 QMutexLocker lock(&m_mtx);
34 return m_callbacks.take(key: callbackId);
35 }
36
37private:
38 QMutex m_mtx;
39 int m_counter;
40 QHash<int, QJSValue> m_callbacks;
41};
42
43} // namespace
44
45Q_GLOBAL_STATIC(CallbackStorage, callbacks)
46
47/*!
48 \qmltype WebView
49 \inqmlmodule QtWebView
50 \ingroup qtwebview
51 \brief A component for displaying web content.
52
53 WebView is a component for displaying web content which is implemented using native
54 APIs on the platforms where this is available, thus it does not necessarily require
55 including a full web browser stack as part of the application.
56
57 To make the Qt WebView module function correctly across all platforms, it is necessary
58 to call \l {qtwebview-initialize}{QtWebView::initialize}() right after creating the
59 QGuiApplication instance.
60
61 \note Due to platform limitations overlapping the WebView and other QML components
62 is not supported.
63*/
64
65QQuickWebView::QQuickWebView(QQuickItem *parent)
66 : QQuickWindowContainer(parent)
67 , m_webView(new QWebView(this))
68 , m_settings(new QQuickWebViewSettings(m_webView->getSettings(), this))
69{
70 if (QWindow *nativeWindow = m_webView->nativeWindow())
71 onNativeWindowChanged(window: nativeWindow);
72
73 connect(sender: m_webView, signal: &QWebView::nativeWindowChanged, context: this, slot: &QQuickWebView::onNativeWindowChanged);
74 connect(sender: m_webView, signal: &QWebView::titleChanged, context: this, slot: &QQuickWebView::titleChanged);
75 connect(sender: m_webView, signal: &QWebView::urlChanged, context: this, slot: &QQuickWebView::urlChanged);
76 connect(sender: m_webView, signal: &QWebView::loadProgressChanged, context: this, slot: &QQuickWebView::loadProgressChanged);
77 connect(sender: m_webView, signal: &QWebView::loadingChanged, context: this, slot: &QQuickWebView::onLoadingChanged);
78 connect(sender: m_webView, signal: &QWebView::javaScriptResult, context: this, slot: &QQuickWebView::onRunJavaScriptResult);
79 connect(sender: m_webView, signal: &QWebView::httpUserAgentChanged, context: this, slot: &QQuickWebView::httpUserAgentChanged);
80 connect(sender: m_webView, signal: &QWebView::cookieAdded, context: this, slot: &QQuickWebView::cookieAdded);
81 connect(sender: m_webView, signal: &QWebView::cookieRemoved, context: this, slot: &QQuickWebView::cookieRemoved);
82}
83
84QQuickWebView::~QQuickWebView() { }
85
86/*!
87 \qmlproperty url QtWebView::WebView::httpUserAgent
88 \since QtWebView 1.14
89 The user agent in use.
90
91 \note on WinRT, this property affects all WebViews of the application.
92*/
93
94void QQuickWebView::setHttpUserAgent(const QString &userAgent)
95{
96 m_webView->setHttpUserAgent(userAgent);
97}
98
99QString QQuickWebView::httpUserAgent() const
100{
101 return m_webView->httpUserAgent();
102}
103
104/*!
105 \qmlproperty url QtWebView::WebView::url
106
107 The URL of currently loaded web page. Changing this will trigger
108 loading new content.
109
110 The URL is used as-is. URLs that originate from user input should
111 be parsed with QUrl::fromUserInput().
112
113 \note The WebView does not support loading content through the Qt Resource system.
114*/
115
116void QQuickWebView::setUrl(const QUrl &url)
117{
118 m_webView->setUrl(url);
119}
120
121/*!
122 \qmlproperty string QtWebView::WebView::title
123 \readonly
124
125 The title of the currently loaded web page.
126*/
127
128QString QQuickWebView::title() const
129{
130 return m_webView->title();
131}
132
133QUrl QQuickWebView::url() const
134{
135 return m_webView->url();
136}
137
138/*!
139 \qmlproperty bool QtWebView::WebView::canGoBack
140 \readonly
141
142 Holds \c true if it's currently possible to navigate back in the web history.
143*/
144
145bool QQuickWebView::canGoBack() const
146{
147 return m_webView->canGoBack();
148}
149
150/*!
151 \qmlproperty bool QtWebView::WebView::canGoForward
152 \readonly
153
154 Holds \c true if it's currently possible to navigate forward in the web history.
155*/
156
157bool QQuickWebView::canGoForward() const
158{
159 return m_webView->canGoForward();
160}
161
162/*!
163 \qmlproperty int QtWebView::WebView::loadProgress
164 \readonly
165
166 The current load progress of the web content, represented as
167 an integer between 0 and 100.
168*/
169int QQuickWebView::loadProgress() const
170{
171 return m_webView->loadProgress();
172}
173
174/*!
175 \qmlproperty bool QtWebView::WebView::loading
176 \readonly
177
178 Holds \c true if the WebView is currently in the process of loading
179 new content, \c false otherwise.
180
181 \sa loadingChanged()
182*/
183
184/*!
185 \qmlsignal QtWebView::WebView::loadingChanged(WebViewLoadRequest loadRequest)
186
187 This signal is emitted when the state of loading the web content changes.
188 By handling this signal it's possible, for example, to react to page load
189 errors.
190
191 The \a loadRequest parameter holds the \e url and \e status of the request,
192 as well as an \e errorString containing an error message for a failed
193 request.
194
195 \sa WebViewLoadRequest
196*/
197bool QQuickWebView::isLoading() const
198{
199 return m_webView->isLoading();
200}
201
202/*!
203 \qmlmethod void QtWebView::WebView::goBack()
204
205 Navigates back in the web history.
206*/
207void QQuickWebView::goBack()
208{
209 m_webView->goBack();
210}
211
212/*!
213 \qmlmethod void QtWebView::WebView::goForward()
214
215 Navigates forward in the web history.
216*/
217void QQuickWebView::goForward()
218{
219 m_webView->goForward();
220}
221
222/*!
223 \qmlmethod void QtWebView::WebView::reload()
224
225 Reloads the current \l url.
226*/
227void QQuickWebView::reload()
228{
229 m_webView->reload();
230}
231
232/*!
233 \qmlmethod void QtWebView::WebView::stop()
234
235 Stops loading the current \l url.
236*/
237void QQuickWebView::stop()
238{
239 m_webView->stop();
240}
241
242/*!
243 \qmlmethod void QtWebView::WebView::loadHtml(string html, url baseUrl)
244
245 Loads the specified \a html content to the web view.
246
247 This method offers a lower-level alternative to the \l url property,
248 which references HTML pages via URL.
249
250 External objects such as stylesheets or images referenced in the HTML
251 document should be located relative to \a baseUrl. For example, if \a html
252 is retrieved from \c http://www.example.com/documents/overview.html, which
253 is the base URL, then an image referenced with the relative url, \c diagram.png,
254 should be at \c{http://www.example.com/documents/diagram.png}.
255
256 \note The WebView does not support loading content through the Qt Resource system.
257
258 \sa url
259*/
260void QQuickWebView::loadHtml(const QString &html, const QUrl &baseUrl)
261{
262 m_webView->loadHtml(html, baseUrl);
263}
264
265/*!
266 \qmlmethod void QtWebView::WebView::runJavaScript(string script, variant callback)
267
268 Runs the specified JavaScript.
269 In case a \a callback function is provided, it will be invoked after the \a script
270 finished running.
271
272 \badcode
273 runJavaScript("document.title", function(result) { console.log(result); });
274 \endcode
275*/
276void QQuickWebView::runJavaScript(const QString &script, const QJSValue &callback)
277{
278 const int callbackId = callback.isCallable() ? callbacks->insertCallback(callback) : -1;
279 runJavaScriptPrivate(script, callbackId);
280}
281
282void QQuickWebView::runJavaScriptPrivate(const QString &script, int callbackId)
283{
284 m_webView->runJavaScriptPrivate(script, callbackId);
285}
286
287/*!
288 \qmlmethod void QtWebView::WebView::setCookie(string domain, string name, string value)
289 \since QtWebView 6.3
290 Adds a cookie with the specified \a domain, \a name and \a value.
291
292 The \l cookieAdded signal will be emitted when the cookie is added.
293*/
294/*!
295 \qmlsignal QtWebView::WebView::cookieAdded(string domain, string name)
296
297 This signal is emitted when a cookie is added.
298
299 The parameters provide information about the \a domain and the \a name of the added cookie.
300
301 \note When Qt WebEngine module is used as backend, cookieAdded signal will be emitted for any
302 cookie added to the underlying QWebEngineCookieStore, including those added by websites.
303 In other cases cookieAdded signal is only emitted for cookies explicitly added with \l setCookie().
304*/
305void QQuickWebView::setCookie(const QString &domain, const QString &name, const QString &value)
306{
307 m_webView->setCookie(domain, name, value);
308}
309
310/*!
311 \qmlmethod void QtWebView::WebView::deleteCookie(string domain, string name)
312 \since QtWebView 6.3
313 Deletes a cookie with the specified \a domain and \a name.
314
315 The \l cookieRemoved signal will be emitted when the cookie is deleted.
316*/
317/*!
318 \qmlsignal QtWebView::WebView::cookieRemoved(string domain, string name)
319
320 This signal is emitted when a cookie is deleted.
321
322 The parameters provide information about the \a domain and the \a name of the deleted cookie.
323*/
324void QQuickWebView::deleteCookie(const QString &domain, const QString &name)
325{
326 m_webView->deleteCookie(domain, name);
327}
328
329/*!
330 \qmlmethod void QtWebView::WebView::deleteAllCookies()
331 \since QtWebView 6.3
332 Deletes all the cookies.
333*/
334void QQuickWebView::deleteAllCookies()
335{
336 m_webView->deleteAllCookies();
337}
338
339
340#if defined(Q_OS_WASM)
341void QQuickWebView::geometryChange(const QRectF &newGeometry, const QRectF &)
342{
343 QQuickWindow *w = window();
344 if (w && m_webView) {
345 QSize itemSize = QSize(newGeometry.width(), newGeometry.height());
346 if (!itemSize.isValid())
347 return;
348
349 // Find this item's geometry in the scene.
350 QRect itemGeometry = mapRectToScene(QRect(QPoint(0, 0), itemSize)).toRect();
351 // Check if we should be clipped to our parent's shape
352 // Note: This is crude but it should give an acceptable result on all platforms.
353 QQuickItem *p = parentItem();
354 const bool clip = p != 0 ? p->clip() : false;
355 if (clip) {
356 const QSize &parentSize = QSize(p->width(), p->height());
357 const QRect &parentGeometry = p->mapRectToScene(QRect(QPoint(0, 0), parentSize)).toRect();
358 itemGeometry &= parentGeometry;
359 itemSize = itemGeometry.size();
360 }
361
362 // Find the top left position of this item, in global coordinates.
363 const QPoint &tl = w->mapToGlobal(itemGeometry.topLeft());
364 // Get the actual render window, in case we're rendering into a off-screen window.
365 QWindow *rw = QQuickRenderControl::renderWindowFor(w);
366 QWebView::get(*m_webView)->geometryChange(rw ? QRect(rw->mapFromGlobal(tl), itemSize) : itemGeometry);
367 }
368}
369#endif // Q_OS_WASM
370
371void QQuickWebView::itemChange(ItemChange change, const ItemChangeData &value)
372{
373 QQuickItem::itemChange(change, value);
374
375#if defined(Q_OS_WASM)
376 if (change == ItemChange::ItemSceneChange && m_webView)
377 QWebView::get(*m_webView)->setParentView(value.window);
378#endif // Q_OS_WASM
379}
380
381void QQuickWebView::onRunJavaScriptResult(int id, const QVariant &variant)
382{
383 if (id == -1)
384 return;
385
386 QJSValue callback = callbacks->takeCallback(callbackId: id);
387 if (callback.isUndefined())
388 return;
389
390 QQmlEngine *engine = qmlEngine(this);
391 if (engine == 0) {
392 qWarning(msg: "No JavaScript engine, unable to handle JavaScript callback!");
393 return;
394 }
395
396 QJSValueList args;
397 args.append(t: engine->toScriptValue(value: variant));
398 callback.call(args);
399}
400
401void QQuickWebView::onLoadingChanged(const QWebViewLoadRequestPrivate &loadRequest)
402{
403 QQuickWebViewLoadRequest qqLoadRequest(loadRequest);
404 Q_EMIT loadingChanged(loadRequest: &qqLoadRequest);
405}
406
407void QQuickWebView::onNativeWindowChanged(QWindow *nativeWindow)
408{
409 if (nativeWindow)
410 nativeWindow->setParent(window());
411 setContainedWindow(nativeWindow);
412}
413
414QJSValue QQuickWebView::takeCallback(int id)
415{
416 return callbacks->takeCallback(callbackId: id);
417}
418
419/*!
420 \qmlproperty WebViewSettings WebView::settings
421 \readonly
422 \since QtWebView 6.5
423
424 Settings object for the WebView.
425
426 \sa WebViewSettings
427*/
428
429QQuickWebViewSettings *QQuickWebView::settings() const
430{
431 return m_settings;
432}
433

source code of qtwebview/src/quick/qquickwebview.cpp