| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2017 The Qt Company Ltd. | 
| 4 | ** Contact: http://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtWebView module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL3$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see http://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at http://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or later as published by the Free | 
| 28 | ** Software Foundation and appearing in the file LICENSE.GPL included in | 
| 29 | ** the packaging of this file. Please review the following information to | 
| 30 | ** ensure the GNU General Public License version 2.0 requirements will be | 
| 31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. | 
| 32 | ** | 
| 33 | ** $QT_END_LICENSE$ | 
| 34 | ** | 
| 35 | ****************************************************************************/ | 
| 36 |  | 
| 37 | #include "qquickwebview_p.h" | 
| 38 | #include "qquickwebviewloadrequest_p.h" | 
| 39 | #include <QtWebView/private/qwebviewloadrequest_p.h> | 
| 40 | #include <QtQml/qqmlengine.h> | 
| 41 | #include <QtCore/qmutex.h> | 
| 42 |  | 
| 43 | namespace { | 
| 44 |  | 
| 45 | class CallbackStorage | 
| 46 | { | 
| 47 | public: | 
| 48 |     int insertCallback(const QJSValue &callback) | 
| 49 |     { | 
| 50 |         QMutexLocker locker(&m_mtx); | 
| 51 |         const int nextId = qMax(a: ++m_counter, b: 0); | 
| 52 |         if (nextId == 0) | 
| 53 |           m_counter = 1; | 
| 54 |  | 
| 55 |         m_callbacks.insert(akey: nextId, avalue: callback); | 
| 56 |         return nextId; | 
| 57 |     } | 
| 58 |  | 
| 59 |     QJSValue takeCallback(int callbackId) | 
| 60 |     { | 
| 61 |         QMutexLocker lock(&m_mtx); | 
| 62 |         return m_callbacks.take(akey: callbackId); | 
| 63 |     } | 
| 64 |  | 
| 65 | private: | 
| 66 |     QMutex m_mtx; | 
| 67 |     int m_counter; | 
| 68 |     QHash<int, QJSValue> m_callbacks; | 
| 69 | }; | 
| 70 |  | 
| 71 | } // namespace | 
| 72 |  | 
| 73 | Q_GLOBAL_STATIC(CallbackStorage, callbacks) | 
| 74 |  | 
| 75 | /*! | 
| 76 |     \qmltype WebView | 
| 77 |     \inqmlmodule QtWebView | 
| 78 |     \ingroup qtwebview | 
| 79 |     \brief A component for displaying web content. | 
| 80 |  | 
| 81 |     WebView is a component for displaying web content which is implemented using native | 
| 82 |     APIs on the platforms where this is available, thus it does not necessarily require | 
| 83 |     including a full web browser stack as part of the application. | 
| 84 |  | 
| 85 |     To make the Qt WebView module function correctly across all platforms, it is necessary | 
| 86 |     to call \l {qtwebview-initialize}{QtWebView::initialize}() right after creating the | 
| 87 |     QGuiApplication instance. | 
| 88 |  | 
| 89 |     \note Due to platform limitations overlapping the WebView and other QML components | 
| 90 |     is not supported. | 
| 91 | */ | 
| 92 |  | 
| 93 | QQuickWebView::QQuickWebView(QQuickItem *parent) | 
| 94 |     : QQuickViewController(parent) | 
| 95 |     , m_webView(new QWebView(this)) | 
| 96 | { | 
| 97 |     setView(m_webView); | 
| 98 |     connect(sender: m_webView, signal: &QWebView::titleChanged, receiver: this, slot: &QQuickWebView::titleChanged); | 
| 99 |     connect(sender: m_webView, signal: &QWebView::urlChanged, receiver: this, slot: &QQuickWebView::urlChanged); | 
| 100 |     connect(sender: m_webView, signal: &QWebView::loadProgressChanged, receiver: this, slot: &QQuickWebView::loadProgressChanged); | 
| 101 |     connect(sender: m_webView, signal: &QWebView::loadingChanged, receiver: this, slot: &QQuickWebView::onLoadingChanged); | 
| 102 |     connect(sender: m_webView, signal: &QWebView::requestFocus, receiver: this, slot: &QQuickWebView::onFocusRequest); | 
| 103 |     connect(sender: m_webView, signal: &QWebView::javaScriptResult, receiver: this, slot: &QQuickWebView::onRunJavaScriptResult); | 
| 104 |     connect(sender: m_webView, signal: &QWebView::httpUserAgentChanged, receiver: this, slot: &QQuickWebView::httpUserAgentChanged); | 
| 105 | } | 
| 106 |  | 
| 107 | QQuickWebView::~QQuickWebView() | 
| 108 | { | 
| 109 | } | 
| 110 |  | 
| 111 | /*! | 
| 112 |   \qmlproperty url QtWebView::WebView::httpUserAgent | 
| 113 |   \since QtWebView 1.14 | 
| 114 |   The user agent in use. | 
| 115 |  | 
| 116 |   \note on WinRT, this property affects all WebViews of the application. | 
| 117 | */ | 
| 118 |  | 
| 119 | void QQuickWebView::setHttpUserAgent(const QString &userAgent) | 
| 120 | { | 
| 121 |     m_webView->setHttpUserAgent(userAgent); | 
| 122 | } | 
| 123 |  | 
| 124 | QString QQuickWebView::httpUserAgent() const | 
| 125 | { | 
| 126 |     return m_webView->httpUserAgent(); | 
| 127 | } | 
| 128 |  | 
| 129 | /*! | 
| 130 |   \qmlproperty url QtWebView::WebView::url | 
| 131 |  | 
| 132 |   The URL of currently loaded web page. Changing this will trigger | 
| 133 |   loading new content. | 
| 134 |  | 
| 135 |   The URL is used as-is. URLs that originate from user input should | 
| 136 |   be parsed with QUrl::fromUserInput(). | 
| 137 |  | 
| 138 |   \note The WebView does not support loading content through the Qt Resource system. | 
| 139 | */ | 
| 140 |  | 
| 141 | void QQuickWebView::setUrl(const QUrl &url) | 
| 142 | { | 
| 143 |     m_webView->setUrl(url); | 
| 144 | } | 
| 145 |  | 
| 146 | /*! | 
| 147 |   \qmlproperty string QtWebView::WebView::title | 
| 148 |   \readonly | 
| 149 |  | 
| 150 |   The title of the currently loaded web page. | 
| 151 | */ | 
| 152 |  | 
| 153 | QString QQuickWebView::title() const | 
| 154 | { | 
| 155 |     return m_webView->title(); | 
| 156 | } | 
| 157 |  | 
| 158 | QUrl QQuickWebView::url() const | 
| 159 | { | 
| 160 |     return m_webView->url(); | 
| 161 | } | 
| 162 |  | 
| 163 | /*! | 
| 164 |   \qmlproperty bool QtWebView::WebView::canGoBack | 
| 165 |   \readonly | 
| 166 |  | 
| 167 |   Holds \c true if it's currently possible to navigate back in the web history. | 
| 168 | */ | 
| 169 |  | 
| 170 | bool QQuickWebView::canGoBack() const | 
| 171 | { | 
| 172 |     return m_webView->canGoBack(); | 
| 173 | } | 
| 174 |  | 
| 175 | /*! | 
| 176 |   \qmlproperty bool QtWebView::WebView::canGoForward | 
| 177 |   \readonly | 
| 178 |  | 
| 179 |   Holds \c true if it's currently possible to navigate forward in the web history. | 
| 180 | */ | 
| 181 |  | 
| 182 | bool QQuickWebView::canGoForward() const | 
| 183 | { | 
| 184 |     return m_webView->canGoForward(); | 
| 185 | } | 
| 186 |  | 
| 187 | /*! | 
| 188 |   \qmlproperty int QtWebView::WebView::loadProgress | 
| 189 |   \readonly | 
| 190 |  | 
| 191 |   The current load progress of the web content, represented as | 
| 192 |   an integer between 0 and 100. | 
| 193 | */ | 
| 194 | int QQuickWebView::loadProgress() const | 
| 195 | { | 
| 196 |     return m_webView->loadProgress(); | 
| 197 | } | 
| 198 |  | 
| 199 | /*! | 
| 200 |   \qmlproperty bool QtWebView::WebView::loading | 
| 201 |   \readonly | 
| 202 |  | 
| 203 |   Holds \c true if the WebView is currently in the process of loading | 
| 204 |   new content, \c false otherwise. | 
| 205 |  | 
| 206 |   \sa loadingChanged() | 
| 207 | */ | 
| 208 |  | 
| 209 | /*! | 
| 210 |   \qmlsignal QtWebView::WebView::loadingChanged(WebViewLoadRequest loadRequest) | 
| 211 |  | 
| 212 |   This signal is emitted when the state of loading the web content changes. | 
| 213 |   By handling this signal it's possible, for example, to react to page load | 
| 214 |   errors. | 
| 215 |  | 
| 216 |   The \a loadRequest parameter holds the \e url and \e status of the request, | 
| 217 |   as well as an \e errorString containing an error message for a failed | 
| 218 |   request. | 
| 219 |  | 
| 220 |   \sa WebViewLoadRequest | 
| 221 | */ | 
| 222 | bool QQuickWebView::isLoading() const | 
| 223 | { | 
| 224 |     return m_webView->isLoading(); | 
| 225 | } | 
| 226 |  | 
| 227 | /*! | 
| 228 |     \qmlmethod void QtWebView::WebView::goBack() | 
| 229 |  | 
| 230 |     Navigates back in the web history. | 
| 231 | */ | 
| 232 | void QQuickWebView::goBack() | 
| 233 | { | 
| 234 |     m_webView->goBack(); | 
| 235 | } | 
| 236 |  | 
| 237 | /*! | 
| 238 |     \qmlmethod void QtWebView::WebView::goForward() | 
| 239 |  | 
| 240 |     Navigates forward in the web history. | 
| 241 | */ | 
| 242 | void QQuickWebView::goForward() | 
| 243 | { | 
| 244 |     m_webView->goForward(); | 
| 245 | } | 
| 246 |  | 
| 247 | /*! | 
| 248 |     \qmlmethod void QtWebView::WebView::reload() | 
| 249 |  | 
| 250 |     Reloads the current \l url. | 
| 251 | */ | 
| 252 | void QQuickWebView::reload() | 
| 253 | { | 
| 254 |     m_webView->reload(); | 
| 255 | } | 
| 256 |  | 
| 257 | /*! | 
| 258 |     \qmlmethod void QtWebView::WebView::stop() | 
| 259 |  | 
| 260 |     Stops loading the current \l url. | 
| 261 | */ | 
| 262 | void QQuickWebView::stop() | 
| 263 | { | 
| 264 |     m_webView->stop(); | 
| 265 | } | 
| 266 |  | 
| 267 | /*! | 
| 268 |     \qmlmethod void QtWebView::WebView::loadHtml(string html, url baseUrl) | 
| 269 |  | 
| 270 |     Loads the specified \a html content to the web view. | 
| 271 |  | 
| 272 |     This method offers a lower-level alternative to the \l url property, | 
| 273 |     which references HTML pages via URL. | 
| 274 |  | 
| 275 |     External objects such as stylesheets or images referenced in the HTML | 
| 276 |     document should be located relative to \a baseUrl. For example, if \a html | 
| 277 |     is retrieved from \c http://www.example.com/documents/overview.html, which | 
| 278 |     is the base URL, then an image referenced with the relative url, \c diagram.png, | 
| 279 |     should be at \c{http://www.example.com/documents/diagram.png}. | 
| 280 |  | 
| 281 |     \note The WebView does not support loading content through the Qt Resource system. | 
| 282 |  | 
| 283 |     \sa url | 
| 284 | */ | 
| 285 | void QQuickWebView::loadHtml(const QString &html, const QUrl &baseUrl) | 
| 286 | { | 
| 287 |     m_webView->loadHtml(html, baseUrl); | 
| 288 | } | 
| 289 |  | 
| 290 | /*! | 
| 291 |     \qmlmethod void QtWebView::WebView::runJavaScript(string script, variant callback) | 
| 292 |  | 
| 293 |     Runs the specified JavaScript. | 
| 294 |     In case a \a callback function is provided, it will be invoked after the \a script | 
| 295 |     finished running. | 
| 296 |  | 
| 297 |     \badcode | 
| 298 |     runJavaScript("document.title", function(result) { console.log(result); }); | 
| 299 |     \endcode | 
| 300 | */ | 
| 301 | void QQuickWebView::runJavaScript(const QString &script, const QJSValue &callback) | 
| 302 | { | 
| 303 |     const int callbackId = callback.isCallable() ? callbacks->insertCallback(callback) | 
| 304 |                                                  : -1; | 
| 305 |     runJavaScriptPrivate(script, callbackId); | 
| 306 | } | 
| 307 |  | 
| 308 | void QQuickWebView::runJavaScriptPrivate(const QString &script, int callbackId) | 
| 309 | { | 
| 310 |     m_webView->runJavaScriptPrivate(script, callbackId); | 
| 311 | } | 
| 312 |  | 
| 313 | void QQuickWebView::itemChange(ItemChange change, const ItemChangeData &value) | 
| 314 | { | 
| 315 |     if (change == QQuickItem::ItemActiveFocusHasChanged) { | 
| 316 |         m_webView->setFocus(value.boolValue); | 
| 317 |     } | 
| 318 |     QQuickItem::itemChange(change, value); | 
| 319 | } | 
| 320 |  | 
| 321 | void QQuickWebView::onRunJavaScriptResult(int id, const QVariant &variant) | 
| 322 | { | 
| 323 |     if (id == -1) | 
| 324 |         return; | 
| 325 |  | 
| 326 |     QJSValue callback = callbacks->takeCallback(callbackId: id); | 
| 327 |     if (callback.isUndefined()) | 
| 328 |         return; | 
| 329 |  | 
| 330 |     QQmlEngine *engine = qmlEngine(this); | 
| 331 |     if (engine == 0) { | 
| 332 |         qWarning(msg: "No JavaScript engine, unable to handle JavaScript callback!" ); | 
| 333 |         return; | 
| 334 |     } | 
| 335 |  | 
| 336 |     QJSValueList args; | 
| 337 |     args.append(t: engine->toScriptValue(value: variant)); | 
| 338 |     callback.call(args); | 
| 339 | } | 
| 340 |  | 
| 341 | void QQuickWebView::onFocusRequest(bool focus) | 
| 342 | { | 
| 343 |     setFocus(focus); | 
| 344 | } | 
| 345 |  | 
| 346 | void QQuickWebView::onLoadingChanged(const QWebViewLoadRequestPrivate &loadRequest) | 
| 347 | { | 
| 348 |     QQuickWebViewLoadRequest qqLoadRequest(loadRequest); | 
| 349 |     Q_EMIT loadingChanged(loadRequest: &qqLoadRequest); | 
| 350 | } | 
| 351 |  | 
| 352 | QJSValue QQuickWebView::takeCallback(int id) | 
| 353 | { | 
| 354 |     return callbacks->takeCallback(callbackId: id); | 
| 355 | } | 
| 356 |  |