blob: 8deabdc7e70b774b534a046e8e3db9154c87194c [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtWebView module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwinrtwebview_p.h"
#include <private/qwebviewloadrequest_p.h>
#include <functional>
#include <QPointer>
#include <QHash>
#include <QRegularExpression>
#include <QScreen>
#include <qfunctions_winrt.h>
#include <private/qeventdispatcher_winrt_p.h>
#include <private/qhighdpiscaling_p.h>
#include <wrl.h>
#include <windows.graphics.display.h>
#include <windows.system.h>
#include <windows.ui.xaml.h>
#include <windows.ui.xaml.controls.h>
#include <windows.ui.xaml.markup.h>
#include <windows.web.h>
#include <urlmon.h>
#include <WinInet.h>
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::Graphics::Display;
using namespace ABI::Windows::System;
using namespace ABI::Windows::UI;
using namespace ABI::Windows::UI::Xaml;
using namespace ABI::Windows::UI::Xaml::Controls;
using namespace ABI::Windows::UI::Xaml::Markup;
using namespace ABI::Windows::Web;
class HStringListIterator : public RuntimeClass<RuntimeClassFlags<WinRt>, IIterator<HSTRING>>
{
public:
HStringListIterator(HSTRING *data, int size) : data(data), size(size), pos(0)
{
}
HRESULT __stdcall get_Current(HSTRING *current) override
{
*current = pos >= size ? nullptr : data[pos];
return S_OK;
}
HRESULT __stdcall get_HasCurrent(boolean *hasCurrent) override
{
*hasCurrent = pos < size;
return S_OK;
}
HRESULT __stdcall MoveNext(boolean *hasCurrent) override
{
*hasCurrent = ++pos < size;
return S_OK;
}
HRESULT __stdcall GetMany(unsigned capacity, HSTRING *value, unsigned *actual)
{
unsigned i = 0;
for (; i < qMin(capacity, unsigned(size)); ++i)
value[i] = data[pos + i];
*actual = i;
pos += i;
return S_OK;
}
private:
HSTRING *data;
int size;
int pos;
};
class HStringList : public RuntimeClass<RuntimeClassFlags<WinRt>, IIterable<HSTRING>>
{
public:
HStringList(const QList<QString> &stringList)
{
d.resize(stringList.size());
for (int i = 0; i < stringList.size(); ++i) {
const QString qString = stringList.at(i).trimmed();
HStringReference hString(reinterpret_cast<const wchar_t *>(qString.utf16()), qString.length());
hString.CopyTo(&d[i++]);
}
}
~HStringList()
{
for (const HSTRING &hString : qAsConst(d))
WindowsDeleteString(hString);
}
HRESULT __stdcall First(IIterator<HSTRING> **first) override
{
ComPtr<HStringListIterator> it = Make<HStringListIterator>(d.data(), d.size());
return it.Get()->QueryInterface(IID_PPV_ARGS(first));
}
private:
QVector<HSTRING> d;
};
static QUrl qurlFromUri(IUriRuntimeClass *uri)
{
HRESULT hr;
HString uriString;
hr = uri->get_AbsoluteUri(uriString.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
quint32 uriStringLength;
const wchar_t *uriStringBuffer = uriString.GetRawBuffer(&uriStringLength);
return QUrl(QString::fromWCharArray(uriStringBuffer, uriStringLength));
}
static QString webErrorStatusString(WebErrorStatus status)
{
switch (status) {
case WebErrorStatus_Unknown:
return QStringLiteral("Unknown");
case WebErrorStatus_CertificateCommonNameIsIncorrect:
return QStringLiteral("CertificateCommonNameIsIncorrect");
case WebErrorStatus_CertificateExpired:
return QStringLiteral("CertificateExpired");
case WebErrorStatus_CertificateContainsErrors:
return QStringLiteral("CertificateContainsErrors");
case WebErrorStatus_CertificateRevoked:
return QStringLiteral("CertificateRevoked");
case WebErrorStatus_CertificateIsInvalid:
return QStringLiteral("CertificateIsInvalid");
case WebErrorStatus_ServerUnreachable:
return QStringLiteral("ServerUnreachable");
case WebErrorStatus_Timeout:
return QStringLiteral("Timeout");
case WebErrorStatus_ErrorHttpInvalidServerResponse:
return QStringLiteral("ErrorHttpInvalidServerResponse");
case WebErrorStatus_ConnectionAborted:
return QStringLiteral("ConnectionAborted");
case WebErrorStatus_ConnectionReset:
return QStringLiteral("ConnectionReset");
case WebErrorStatus_Disconnected:
return QStringLiteral("Disconnected");
case WebErrorStatus_HttpToHttpsOnRedirection:
return QStringLiteral("HttpToHttpsOnRedirection");
case WebErrorStatus_HttpsToHttpOnRedirection:
return QStringLiteral("HttpsToHttpOnRedirection");
case WebErrorStatus_CannotConnect:
return QStringLiteral("CannotConnect");
case WebErrorStatus_HostNameNotResolved:
return QStringLiteral("HostNameNotResolved");
case WebErrorStatus_OperationCanceled:
return QStringLiteral("OperationCanceled");
case WebErrorStatus_RedirectFailed:
return QStringLiteral("RedirectFailed");
case WebErrorStatus_UnexpectedStatusCode:
return QStringLiteral("UnexpectedStatusCode");
case WebErrorStatus_UnexpectedRedirection:
return QStringLiteral("UnexpectedRedirection");
case WebErrorStatus_UnexpectedClientError:
return QStringLiteral("UnexpectedClientError");
case WebErrorStatus_UnexpectedServerError:
return QStringLiteral("UnexpectedServerError");
case WebErrorStatus_MultipleChoices:
return QStringLiteral("MultipleChoices");
case WebErrorStatus_MovedPermanently:
return QStringLiteral("MovedPermanently");
case WebErrorStatus_Found:
return QStringLiteral("Found");
case WebErrorStatus_SeeOther:
return QStringLiteral("SeeOther");
case WebErrorStatus_NotModified:
return QStringLiteral("NotModified");
case WebErrorStatus_UseProxy:
return QStringLiteral("UseProxy");
case WebErrorStatus_TemporaryRedirect:
return QStringLiteral("TemporaryRedirect");
case WebErrorStatus_BadRequest:
return QStringLiteral("BadRequest");
case WebErrorStatus_Unauthorized:
return QStringLiteral("Unauthorized");
case WebErrorStatus_PaymentRequired:
return QStringLiteral("PaymentRequired");
case WebErrorStatus_Forbidden:
return QStringLiteral("Forbidden");
case WebErrorStatus_NotFound:
return QStringLiteral("NotFound");
case WebErrorStatus_MethodNotAllowed:
return QStringLiteral("MethodNotAllowed");
case WebErrorStatus_NotAcceptable:
return QStringLiteral("NotAcceptable");
case WebErrorStatus_ProxyAuthenticationRequired:
return QStringLiteral("ProxyAuthenticationRequired");
case WebErrorStatus_RequestTimeout:
return QStringLiteral("RequestTimeout");
case WebErrorStatus_Conflict:
return QStringLiteral("Conflict");
case WebErrorStatus_Gone:
return QStringLiteral("Gone");
case WebErrorStatus_LengthRequired:
return QStringLiteral("LengthRequired");
case WebErrorStatus_PreconditionFailed:
return QStringLiteral("PreconditionFailed");
case WebErrorStatus_RequestEntityTooLarge:
return QStringLiteral("RequestEntityTooLarge");
case WebErrorStatus_RequestUriTooLong:
return QStringLiteral("RequestUriTooLong");
case WebErrorStatus_UnsupportedMediaType:
return QStringLiteral("UnsupportedMediaType");
case WebErrorStatus_RequestedRangeNotSatisfiable:
return QStringLiteral("RequestedRangeNotSatisfiable");
case WebErrorStatus_ExpectationFailed:
return QStringLiteral("ExpectationFailed");
case WebErrorStatus_InternalServerError:
return QStringLiteral("InternalServerError");
case WebErrorStatus_NotImplemented:
return QStringLiteral("NotImplemented");
case WebErrorStatus_BadGateway:
return QStringLiteral("BadGateway");
case WebErrorStatus_ServiceUnavailable:
return QStringLiteral("ServiceUnavailable");
case WebErrorStatus_GatewayTimeout:
return QStringLiteral("GatewayTimeout");
case WebErrorStatus_HttpVersionNotSupported:
return QStringLiteral("HttpVersionNotSupported");
default:
break;
}
return QString();
}
struct WinRTWebView
{
ComPtr<IWebView> base;
ComPtr<IWebView2> ext;
ComPtr<IPanel> host;
ComPtr<ICanvasStatics> canvas;
ComPtr<IUriRuntimeClassFactory> uriFactory;
ComPtr<IDisplayInformation> displayInformation;
ComPtr<ILauncherStatics> launcherStatics;
QPointer<QWindow> window;
QHash<IAsyncOperation<HSTRING> *, int> callbacks;
EventRegistrationToken navigationStartingToken;
EventRegistrationToken navigationCompletedToken;
EventRegistrationToken unviewableContentToken;
bool isLoading : 1;
};
#define LSTRING(str) L#str
static const wchar_t webviewXaml[] = LSTRING(
<WebView xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
);
QWinRTWebViewPrivate::QWinRTWebViewPrivate(QObject *parent)
: QAbstractWebView(parent), d(new WinRTWebView)
{
d->isLoading = false;
QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr;
ComPtr<IXamlReaderStatics> xamlReader;
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Markup_XamlReader).Get(),
IID_PPV_ARGS(&xamlReader));
Q_ASSERT_SUCCEEDED(hr);
// Directly instantiating a WebView works, but it throws an exception
// when navigating. Using a XamlReader appears to set it up properly.
ComPtr<IInspectable> element;
hr = xamlReader->Load(HString::MakeReference(webviewXaml).Get(), &element);
Q_ASSERT_SUCCEEDED(hr);
hr = element.As(&d->base);
Q_ASSERT_SUCCEEDED(hr);
hr = d->base.As(&d->ext);
Q_ASSERT_SUCCEEDED(hr);
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Launcher).Get(),
IID_PPV_ARGS(&d->launcherStatics));
Q_ASSERT_SUCCEEDED(hr);
hr = d->ext->add_NavigationStarting(
Callback<ITypedEventHandler<WebView *, WebViewNavigationStartingEventArgs *>>(this, &QWinRTWebViewPrivate::onNavigationStarted).Get(),
&d->navigationStartingToken);
Q_ASSERT_SUCCEEDED(hr);
hr = d->ext->add_NavigationCompleted(
Callback<ITypedEventHandler<WebView *, WebViewNavigationCompletedEventArgs *>>(this, &QWinRTWebViewPrivate::onNavigationCompleted).Get(),
&d->navigationCompletedToken);
Q_ASSERT_SUCCEEDED(hr);
hr = d->ext->add_UnviewableContentIdentified(
Callback<ITypedEventHandler<WebView *, WebViewUnviewableContentIdentifiedEventArgs *>>(this, &QWinRTWebViewPrivate::onUnviewableContent).Get(),
&d->unviewableContentToken);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IDisplayInformationStatics> displayInformationStatics;
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Display_DisplayInformation).Get(),
IID_PPV_ARGS(&displayInformationStatics));
Q_ASSERT_SUCCEEDED(hr);
hr = displayInformationStatics->GetForCurrentView(&d->displayInformation);
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
HRESULT hr;
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Controls_Canvas).Get(),
IID_PPV_ARGS(&d->canvas));
Q_ASSERT_SUCCEEDED(hr);
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Foundation_Uri).Get(),
IID_PPV_ARGS(&d->uriFactory));
Q_ASSERT_SUCCEEDED(hr);
}
QWinRTWebViewPrivate::~QWinRTWebViewPrivate()
{
QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr;
hr = d->ext->remove_NavigationStarting(d->navigationStartingToken);
Q_ASSERT_SUCCEEDED(hr);
hr = d->ext->remove_NavigationCompleted(d->navigationCompletedToken);
Q_ASSERT_SUCCEEDED(hr);
hr = d->ext->remove_UnviewableContentIdentified(d->unviewableContentToken);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IVector<UIElement *>> children;
hr = d->host->get_Children(&children);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IUIElement> uiElement;
hr = d->base.As(&uiElement);
Q_ASSERT_SUCCEEDED(hr);
quint32 index;
boolean found;
hr = children->IndexOf(uiElement.Get(), &index, &found);
Q_ASSERT_SUCCEEDED(hr);
if (found) {
hr = children->RemoveAt(index);
Q_ASSERT_SUCCEEDED(hr);
}
return hr;
});
}
QString QWinRTWebViewPrivate::httpUserAgent() const
{
#ifdef QT_WINRT_URLMKGETSESSIONOPTION_NOT_AVAILABLE
qWarning() << "Used Windows SDK version (" << QString::number(QT_UCRTVERSION) << ") does not "
"support getting or setting of the user agent property from within UWP applications. Consider updating to a more recent Windows 10 "
"SDK (16299 or above).";
return "";
#else
char buffer[255];
unsigned long stringLength;
HRESULT hr = UrlMkGetSessionOption(0x10000001,&buffer,255,&stringLength,0);
Q_ASSERT_SUCCEEDED(hr);
return QString(buffer);
#endif
}
void QWinRTWebViewPrivate::setHttpUserAgent(const QString &userAgent)
{
#ifdef QT_WINRT_URLMKSETSESSIONOPTION_NOT_AVAILABLE
Q_UNUSED(userAgent);
qWarning() << "Used Windows SDK version (" << QString::number(QT_UCRTVERSION) << ") does not "
"support getting or setting of the user agent property from within UWP applications. Consider updating to a more recent Windows 10 "
"SDK (16299 or above).";
#else
HRESULT hr = UrlMkSetSessionOption(0x10000001,userAgent.toLocal8Bit().data(),userAgent.size(),0);
Q_ASSERT_SUCCEEDED(hr);
emit httpUserAgentChanged(userAgent);
#endif
}
QUrl QWinRTWebViewPrivate::url() const
{
ComPtr<IUriRuntimeClass> uri;
QEventDispatcherWinRT::runOnXamlThread([this, &uri]() {
HRESULT hr;
hr = d->base->get_Source(&uri);
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
return qurlFromUri(uri.Get());
}
void QWinRTWebViewPrivate::setUrl(const QUrl &url)
{
QEventDispatcherWinRT::runOnXamlThread([this, &url]() {
HRESULT hr;
const QString urlString = url.toString();
ComPtr<IUriRuntimeClass> uri;
HStringReference uriString(reinterpret_cast<const wchar_t *>(urlString.utf16()),
urlString.length());
hr = d->uriFactory->CreateUri(uriString.Get(), &uri);
Q_ASSERT_SUCCEEDED(hr);
hr = d->base->Navigate(uri.Get());
// Directly running into an abort means, that the URI is not supported. Ask the user what to do
if (hr == E_ABORT) {
ComPtr<IAsyncOperation<bool>> op;
hr = d->launcherStatics->LaunchUriAsync(uri.Get(), &op);
}
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
bool QWinRTWebViewPrivate::canGoBack() const
{
boolean canGoBack;
QEventDispatcherWinRT::runOnXamlThread([this, &canGoBack]() {
HRESULT hr;
hr = d->ext->get_CanGoBack(&canGoBack);
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
return canGoBack;
}
bool QWinRTWebViewPrivate::canGoForward() const
{
boolean canGoForward;
QEventDispatcherWinRT::runOnXamlThread([this, &canGoForward]() {
HRESULT hr;
hr = d->ext->get_CanGoForward(&canGoForward);
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
return canGoForward;
}
QString QWinRTWebViewPrivate::title() const
{
HString title;
QEventDispatcherWinRT::runOnXamlThread([this, &title]() {
HRESULT hr;
hr = d->ext->get_DocumentTitle(title.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
quint32 titleLength;
const wchar_t *titleBuffer = title.GetRawBuffer(&titleLength);
return QString::fromWCharArray(titleBuffer, titleLength);
}
int QWinRTWebViewPrivate::loadProgress() const
{
return d->isLoading ? 0 : 100;
}
bool QWinRTWebViewPrivate::isLoading() const
{
return d->isLoading;
}
void QWinRTWebViewPrivate::setParentView(QObject *view)
{
d->window = qobject_cast<QWindow *>(view);
if (!d->window)
return;
ComPtr<IInspectable> host = reinterpret_cast<IInspectable *>(d->window->winId());
if (!host)
return;
QEventDispatcherWinRT::runOnXamlThread([this, &host]() {
HRESULT hr;
ComPtr<IFrameworkElement> frameworkHost;
hr = host.As(&frameworkHost);
RETURN_HR_IF_FAILED("Failed to cast the window ID to IFrameworkElement");
ComPtr<IDependencyObject> parent;
hr = frameworkHost->get_Parent(&parent);
RETURN_HR_IF_FAILED("Failed to get the parent object of the window");
hr = parent.As(&d->host);
RETURN_HR_IF_FAILED("Failed to cast the window container to IPanel");
ComPtr<IVector<UIElement *>> children;
hr = d->host->get_Children(&children);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IUIElement> uiElement;
hr = d->base.As(&uiElement);
Q_ASSERT_SUCCEEDED(hr);
hr = children->Append(uiElement.Get());
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
QObject *QWinRTWebViewPrivate::parentView() const
{
return d->window;
}
void QWinRTWebViewPrivate::setGeometry(const QRect &geometry)
{
QEventDispatcherWinRT::runOnXamlThread([this, &geometry]() {
HRESULT hr;
double scaleFactor;
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
ComPtr<IDisplayInformation2> displayInformation;
hr = d->displayInformation.As(&displayInformation);
Q_ASSERT_SUCCEEDED(hr);
hr = displayInformation->get_RawPixelsPerViewPixel(&scaleFactor);
Q_ASSERT_SUCCEEDED(hr);
scaleFactor = 1.0 / scaleFactor;
Q_ASSERT(d->window);
const QScreen *screen = d->window->screen();
Q_ASSERT(screen);
const QPoint screenTopLeft = screen->availableGeometry().topLeft();
const QPointF topLeft = QHighDpi::toNativePixels(QPointF(geometry.topLeft() + screenTopLeft) * scaleFactor, screen);
const QSizeF size = QHighDpi::toNativePixels(QSizeF(geometry.size()) * scaleFactor, screen);
#else
ResolutionScale resolutionScale;
hr = d->displayInformation->get_ResolutionScale(&resolutionScale);
Q_ASSERT_SUCCEEDED(hr);
scaleFactor = 100.0 / double(resolutionScale);
const QPointF topLeft = QPointF(geometry.topLeft()) * scaleFactor;
const QSizeF size = QSizeF(geometry.size()) * scaleFactor;
#endif
ComPtr<IUIElement> uiElement;
hr = d->base.As(&uiElement);
Q_ASSERT_SUCCEEDED(hr);
hr = d->canvas->SetLeft(uiElement.Get(), topLeft.x());
Q_ASSERT_SUCCEEDED(hr);
hr = d->canvas->SetTop(uiElement.Get(), topLeft.y());
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IFrameworkElement> frameworkElement;
hr = d->base.As(&frameworkElement);
Q_ASSERT_SUCCEEDED(hr);
hr = frameworkElement->put_Width(size.width());
Q_ASSERT_SUCCEEDED(hr);
hr = frameworkElement->put_Height(size.height());
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
void QWinRTWebViewPrivate::setVisibility(QWindow::Visibility visibility)
{
setVisible(visibility != QWindow::Hidden);
}
void QWinRTWebViewPrivate::setVisible(bool visible)
{
QEventDispatcherWinRT::runOnXamlThread([this, &visible]() {
HRESULT hr;
ComPtr<IUIElement> uiElement;
hr = d->base.As(&uiElement);
Q_ASSERT_SUCCEEDED(hr);
hr = uiElement->put_Visibility(visible ? Visibility_Visible : Visibility_Collapsed);
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
void QWinRTWebViewPrivate::goBack()
{
QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr;
hr = d->ext->GoBack();
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
void QWinRTWebViewPrivate::goForward()
{
QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr;
hr = d->ext->GoForward();
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
void QWinRTWebViewPrivate::reload()
{
QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr;
hr = d->ext->Refresh();
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
void QWinRTWebViewPrivate::stop()
{
QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr;
hr = d->ext->Stop();
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
void QWinRTWebViewPrivate::loadHtml(const QString &html, const QUrl &baseUrl)
{
if (!baseUrl.isEmpty())
qWarning("Base URLs for loadHtml() are not supported under WinRT.");
HRESULT hr;
HStringReference htmlText(reinterpret_cast<const wchar_t *>(html.utf16()), html.length());
hr = d->base->NavigateToString(htmlText.Get());
Q_ASSERT_SUCCEEDED(hr);
}
void QWinRTWebViewPrivate::runJavaScriptPrivate(const QString &script, int callbackId)
{
static QRegularExpression functionTemplate(QStringLiteral("^(.*)\\((.*)\\)[\\s;]*?$"));
QRegularExpressionMatch match = functionTemplate.match(script);
if (!match.hasMatch()) {
qWarning("The WinRT WebView only supports calling global functions, so please make your call"
" in the form myFunction(a, b, c). Also note that only string arguments can be passed.");
return;
}
const QString method = match.captured(1).trimmed();
HStringReference methodString(reinterpret_cast<const wchar_t *>(method.utf16()), method.length());
ComPtr<HStringList> argumentStrings = Make<HStringList>(match.captured(2).split(QLatin1Char(',')));
QEventDispatcherWinRT::runOnXamlThread([this, &methodString, &argumentStrings, &callbackId]() {
HRESULT hr;
ComPtr<IAsyncOperation<HSTRING>> op;
hr = d->ext->InvokeScriptAsync(methodString.Get(), argumentStrings.Get(), &op);
Q_ASSERT_SUCCEEDED(hr);
d->callbacks.insert(op.Get(), callbackId);
hr = op->put_Completed(Callback<IAsyncOperationCompletedHandler<HSTRING>>([this](IAsyncOperation<HSTRING> *op, AsyncStatus status) {
int callbackId = d->callbacks.take(op);
HRESULT hr;
if (status != Completed) {
ComPtr<IAsyncInfo> info;
hr = op->QueryInterface(IID_PPV_ARGS(&info));
Q_ASSERT_SUCCEEDED(hr);
HRESULT errorCode;
hr = info->get_ErrorCode(&errorCode);
Q_ASSERT_SUCCEEDED(hr);
emit javaScriptResult(callbackId, qt_error_string(errorCode));
return S_OK;
}
HString result;
hr = op->GetResults(result.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
quint32 resultLength;
const wchar_t *resultBuffer = result.GetRawBuffer(&resultLength);
emit javaScriptResult(callbackId, QString::fromWCharArray(resultBuffer, resultLength));
return S_OK;
}).Get());
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
HRESULT QWinRTWebViewPrivate::onNavigationStarted(IWebView *, IWebViewNavigationStartingEventArgs *args)
{
d->isLoading = true;
HRESULT hr;
ComPtr<IUriRuntimeClass> uri;
hr = args->get_Uri(&uri);
Q_ASSERT_SUCCEEDED(hr);
const QUrl url = qurlFromUri(uri.Get());
emit loadingChanged(QWebViewLoadRequestPrivate(url, QWebView::LoadStartedStatus, QString()));
emit loadProgressChanged(0);
return S_OK;
}
HRESULT QWinRTWebViewPrivate::onNavigationCompleted(IWebView *, IWebViewNavigationCompletedEventArgs *args)
{
d->isLoading = false;
HRESULT hr;
ComPtr<IUriRuntimeClass> uri;
hr = args->get_Uri(&uri);
Q_ASSERT_SUCCEEDED(hr);
const QUrl url = qurlFromUri(uri.Get());
boolean isSuccess;
hr = args->get_IsSuccess(&isSuccess);
Q_ASSERT_SUCCEEDED(hr);
const QWebView::LoadStatus status = isSuccess ? QWebView::LoadSucceededStatus : QWebView::LoadFailedStatus;
WebErrorStatus errorStatus;
hr = args->get_WebErrorStatus(&errorStatus);
Q_ASSERT_SUCCEEDED(hr);
const QString errorString = webErrorStatusString(errorStatus);
emit loadingChanged(QWebViewLoadRequestPrivate(url, status, errorString));
emit titleChanged(title());
emit loadProgressChanged(100);
return S_OK;
}
HRESULT QWinRTWebViewPrivate::onUnviewableContent(IWebView *, IWebViewUnviewableContentIdentifiedEventArgs *args)
{
HRESULT hr;
ComPtr<IUriRuntimeClass> uri;
hr = args->get_Uri(&uri);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IAsyncOperation<bool>> op;
hr = d->launcherStatics->LaunchUriAsync(uri.Get(), &op);
Q_ASSERT_SUCCEEDED(hr);
return S_OK;
}