Date: Wednesday, March 21, 2018 @ 22:28:03 Author: arojas Revision: 319784
Add patches Added: qt5-webengine/kde-unstable/qtbug-66333.patch qt5-webengine/kde-unstable/qtbug-66338.patch -------------------+ qtbug-66333.patch | 870 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qtbug-66338.patch | 196 +++++++++++ 2 files changed, 1066 insertions(+) Added: qtbug-66333.patch =================================================================== --- qtbug-66333.patch (rev 0) +++ qtbug-66333.patch 2018-03-21 22:28:03 UTC (rev 319784) @@ -0,0 +1,870 @@ +From 235def8f2af6b858f5c8eafd8bc9a411a53dc7e9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=BCri=20Valdmann?= <[email protected]> +Date: Mon, 19 Feb 2018 11:25:24 +0100 +Subject: [PATCH] Make WebChannelIPCTransport into a RenderFrameObserver + +As of version 63, Chromium creates proxy frames also for the main frame in the +frame tree during cross-process navigations. This leads to a segmentation fault +in WebChannelIPCTransport because we assume that all main frames are local. + +See https://crrev.com/27caae83cb530daaf49f9a38793e427cdf493a65 for details. + +This patch refactors the renderer-side WebChannelIPCTransport from a +RenderViewObserver into a RenderFrameObserver, which prevents the segmentation +fault since the RenderFrameObserver is not created for proxy frames. Most likely +this would have to be done eventually anyway since the RenderView and +RenderViewObserver classes are deprecated and will likely be removed as part of +the Site Isolation project. + +Installation is changed to follow Chromium's RenderFrameImpl in the sense of +performing the installation from RenderFrameObserver::DidClearWindowObject +instead of ContentRendererClient::RunScriptsAtDocumentStart. This has the +benefit of avoiding the ScriptForbiddenScope DCHECK. + +Additionally there are the following minor changes: + + - The deprecated parameterless version of v8::Value::ToObject() method is + replaced with v8::Value::IsObject() check and v8::Local::Cast. + + - The deprecated v8::Handle typedef is replaced with v8::Local. + + - The deprecated single-parameter WebContentsObserver::OnMessageReceived is + replaced with the new two-parameter version. + + - blink::MainThreadIsolate() is used instead of v8::Isolate::GetCurrent() for + Install/Uninstall since we know we are executing on the main thread. + + - WebChannelIPCTransportHost is changed to ignore messages from unexpected + renderers in case something goes wrong with the renderers. + + - Logging is added to WebChannelIPCTransportHost for debugging purposes. + +Some new unit tests are added, all of which fail with the old version. + +Task-number: QTBUG-66333 +Change-Id: I936d142fb042d9f936a3f9d08d4328ecba595f1f +--- + src/core/common/qt_messages.h | 3 +- + src/core/renderer/content_renderer_client_qt.cpp | 5 +- + src/core/renderer/web_channel_ipc_transport.cpp | 237 ++++++++++----------- + src/core/renderer/web_channel_ipc_transport.h | 36 ++-- + .../web_channel_ipc_transport_host.cpp | 89 ++++---- + .../renderer_host/web_channel_ipc_transport_host.h | 32 +-- + .../qwebenginescript/tst_qwebenginescript.cpp | 121 ++++++++++- + 7 files changed, 312 insertions(+), 211 deletions(-) + +diff --git a/src/core/common/qt_messages.h b/src/core/common/qt_messages.h +index 3bedb10d217..864c3ea454d 100644 +--- a/src/core/common/qt_messages.h ++++ b/src/core/common/qt_messages.h +@@ -39,8 +39,7 @@ IPC_MESSAGE_ROUTED1(RenderViewObserverQt_FetchDocumentInnerText, + IPC_MESSAGE_ROUTED1(RenderViewObserverQt_SetBackgroundColor, + uint32_t /* color */) + +-IPC_MESSAGE_ROUTED1(WebChannelIPCTransport_Install, uint /* worldId */) +-IPC_MESSAGE_ROUTED1(WebChannelIPCTransport_Uninstall, uint /* worldId */) ++IPC_MESSAGE_ROUTED1(WebChannelIPCTransport_SetWorldId, base::Optional<uint> /* worldId */) + IPC_MESSAGE_ROUTED2(WebChannelIPCTransport_Message, std::vector<char> /*binaryJSON*/, uint /* worldId */) + + // User scripts messages +diff --git a/src/core/renderer/content_renderer_client_qt.cpp b/src/core/renderer/content_renderer_client_qt.cpp +index a77bcf7854f..4f7c5cff4ad 100644 +--- a/src/core/renderer/content_renderer_client_qt.cpp ++++ b/src/core/renderer/content_renderer_client_qt.cpp +@@ -124,13 +124,14 @@ void ContentRendererClientQt::RenderViewCreated(content::RenderView* render_view + { + // RenderViewObservers destroy themselves with their RenderView. + new RenderViewObserverQt(render_view, m_webCacheImpl.data()); +- new WebChannelIPCTransport(render_view); + UserResourceController::instance()->renderViewCreated(render_view); + } + + void ContentRendererClientQt::RenderFrameCreated(content::RenderFrame* render_frame) + { + new QtWebEngineCore::RenderFrameObserverQt(render_frame); ++ if (render_frame->IsMainFrame()) ++ new WebChannelIPCTransport(render_frame); + UserResourceController::instance()->renderFrameCreated(render_frame); + + #if BUILDFLAG(ENABLE_SPELLCHECK) +@@ -151,8 +152,6 @@ void ContentRendererClientQt::RunScriptsAtDocumentStart(content::RenderFrame* re + if (!render_frame_observer || render_frame_observer->isFrameDetached()) + return; // The frame is invisible to scripts. + +- if (WebChannelIPCTransport *transport = WebChannelIPCTransport::Get(render_frame->GetRenderView())) +- transport->RunScriptsAtDocumentStart(render_frame); + UserResourceController::instance()->RunScriptsAtDocumentStart(render_frame); + } + +diff --git a/src/core/renderer/web_channel_ipc_transport.cpp b/src/core/renderer/web_channel_ipc_transport.cpp +index 534ee302dcb..bb544168f7b 100644 +--- a/src/core/renderer/web_channel_ipc_transport.cpp ++++ b/src/core/renderer/web_channel_ipc_transport.cpp +@@ -45,13 +45,12 @@ + #include "common/qt_messages.h" + + #include "content/public/renderer/render_frame.h" +-#include "content/public/renderer/render_view.h" + #include "gin/arguments.h" + #include "gin/handle.h" + #include "gin/object_template_builder.h" + #include "gin/wrappable.h" ++#include "third_party/WebKit/public/web/WebKit.h" + #include "third_party/WebKit/public/web/WebLocalFrame.h" +-#include "third_party/WebKit/public/web/WebView.h" + #include "v8/include/v8.h" + + #include <QJsonDocument> +@@ -61,193 +60,189 @@ namespace QtWebEngineCore { + class WebChannelTransport : public gin::Wrappable<WebChannelTransport> { + public: + static gin::WrapperInfo kWrapperInfo; +- static void Install(blink::WebFrame *frame, uint worldId); +- static void Uninstall(blink::WebFrame *frame, uint worldId); ++ static void Install(blink::WebLocalFrame *frame, uint worldId); ++ static void Uninstall(blink::WebLocalFrame *frame, uint worldId); + private: +- content::RenderView *GetRenderView(v8::Isolate *isolate); +- WebChannelTransport() { } +- gin::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate *isolate) override; ++ WebChannelTransport() {} ++ bool NativeQtSendMessage(gin::Arguments *args); + +- bool NativeQtSendMessage(gin::Arguments *args) +- { +- content::RenderView *renderView = GetRenderView(args->isolate()); +- if (!renderView || args->Length() != 1) +- return false; +- v8::Handle<v8::Value> val; +- args->GetNext(&val); +- if (!val->IsString() && !val->IsStringObject()) +- return false; +- v8::String::Utf8Value utf8(val->ToString()); +- +- QByteArray valueData(*utf8, utf8.length()); +- QJsonParseError error; +- QJsonDocument doc = QJsonDocument::fromJson(valueData, &error); +- if (error.error != QJsonParseError::NoError) { +- qWarning("%s %d: Parsing error: %s",__FILE__, __LINE__, qPrintable(error.errorString())); +- return false; +- } +- int size = 0; +- const char *rawData = doc.rawData(&size); +- if (size == 0) +- return false; +- renderView->Send(new WebChannelIPCTransportHost_SendMessage(renderView->GetRoutingID(), std::vector<char>(rawData, rawData + size))); +- return true; +- } ++ // gin::WrappableBase ++ gin::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate *isolate) override; + + DISALLOW_COPY_AND_ASSIGN(WebChannelTransport); + }; + + gin::WrapperInfo WebChannelTransport::kWrapperInfo = { gin::kEmbedderNativeGin }; + +-void WebChannelTransport::Install(blink::WebFrame *frame, uint worldId) ++void WebChannelTransport::Install(blink::WebLocalFrame *frame, uint worldId) + { +- v8::Isolate *isolate = v8::Isolate::GetCurrent(); ++ v8::Isolate *isolate = blink::MainThreadIsolate(); + v8::HandleScope handleScope(isolate); +- v8::Handle<v8::Context> context; ++ v8::Local<v8::Context> context; + if (worldId == 0) +- context = frame->ToWebLocalFrame()->MainWorldScriptContext(); ++ context = frame->MainWorldScriptContext(); + else +- context = frame->ToWebLocalFrame()->IsolatedWorldScriptContext(worldId); ++ context = frame->IsolatedWorldScriptContext(worldId); + v8::Context::Scope contextScope(context); + + gin::Handle<WebChannelTransport> transport = gin::CreateHandle(isolate, new WebChannelTransport); +- v8::Handle<v8::Object> global = context->Global(); +- v8::Handle<v8::Object> qt = global->Get(gin::StringToV8(isolate, "qt"))->ToObject(); +- if (qt.IsEmpty()) { +- qt = v8::Object::New(isolate); +- global->Set(gin::StringToV8(isolate, "qt"), qt); ++ ++ v8::Local<v8::Object> global = context->Global(); ++ v8::Local<v8::Value> qtObjectValue = global->Get(gin::StringToV8(isolate, "qt")); ++ v8::Local<v8::Object> qtObject; ++ if (qtObjectValue.IsEmpty() || !qtObjectValue->IsObject()) { ++ qtObject = v8::Object::New(isolate); ++ global->Set(gin::StringToV8(isolate, "qt"), qtObject); ++ } else { ++ qtObject = v8::Local<v8::Object>::Cast(qtObjectValue); + } +- qt->Set(gin::StringToV8(isolate, "webChannelTransport"), transport.ToV8()); ++ qtObject->Set(gin::StringToV8(isolate, "webChannelTransport"), transport.ToV8()); + } + +-void WebChannelTransport::Uninstall(blink::WebFrame *frame, uint worldId) ++void WebChannelTransport::Uninstall(blink::WebLocalFrame *frame, uint worldId) + { +- v8::Isolate *isolate = v8::Isolate::GetCurrent(); ++ v8::Isolate *isolate = blink::MainThreadIsolate(); + v8::HandleScope handleScope(isolate); +- v8::Handle<v8::Context> context; ++ v8::Local<v8::Context> context; + if (worldId == 0) +- context = frame->ToWebLocalFrame()->MainWorldScriptContext(); ++ context = frame->MainWorldScriptContext(); + else +- context = frame->ToWebLocalFrame()->IsolatedWorldScriptContext(worldId); ++ context = frame->IsolatedWorldScriptContext(worldId); + v8::Context::Scope contextScope(context); + +- v8::Handle<v8::Object> global(context->Global()); +- v8::Handle<v8::Object> qt = global->Get(gin::StringToV8(isolate, "qt"))->ToObject(); +- if (qt.IsEmpty()) ++ v8::Local<v8::Object> global(context->Global()); ++ v8::Local<v8::Value> qtObjectValue = global->Get(gin::StringToV8(isolate, "qt")); ++ if (qtObjectValue.IsEmpty() || !qtObjectValue->IsObject()) + return; +- qt->Delete(gin::StringToV8(isolate, "webChannelTransport")); +-} +- +-gin::ObjectTemplateBuilder WebChannelTransport::GetObjectTemplateBuilder(v8::Isolate *isolate) +-{ +- return gin::Wrappable<WebChannelTransport>::GetObjectTemplateBuilder(isolate).SetMethod("send", &WebChannelTransport::NativeQtSendMessage); ++ v8::Local<v8::Object> qtObject = v8::Local<v8::Object>::Cast(qtObjectValue); ++ qtObject->Delete(gin::StringToV8(isolate, "webChannelTransport")); + } + +-content::RenderView *WebChannelTransport::GetRenderView(v8::Isolate *isolate) ++bool WebChannelTransport::NativeQtSendMessage(gin::Arguments *args) + { +- blink::WebLocalFrame *webframe = blink::WebLocalFrame::FrameForContext(isolate->GetCurrentContext()); +- DCHECK(webframe) << "There should be an active frame since we just got a native function called."; +- if (!webframe) +- return 0; ++ blink::WebLocalFrame *frame = blink::WebLocalFrame::FrameForCurrentContext(); ++ if (!frame || !frame->View()) ++ return false; ++ ++ content::RenderFrame *renderFrame = content::RenderFrame::FromWebFrame(frame); ++ if (!renderFrame) ++ return false; ++ ++ std::string message; ++ if (!args->GetNext(&message)) ++ return false; ++ ++ QByteArray valueData(message.data(), message.size()); ++ QJsonParseError error; ++ QJsonDocument doc = QJsonDocument::fromJson(valueData, &error); ++ if (error.error != QJsonParseError::NoError) { ++ LOG(WARNING) << "Parsing error: " << qPrintable(error.errorString()); ++ return false; ++ } + +- blink::WebView *webview = webframe->View(); +- if (!webview) +- return 0; // can happen during closing ++ int size = 0; ++ const char *rawData = doc.rawData(&size); ++ if (size == 0) ++ return false; + +- return content::RenderView::FromWebView(webview); ++ renderFrame->Send(new WebChannelIPCTransportHost_SendMessage( ++ renderFrame->GetRoutingID(), ++ std::vector<char>(rawData, rawData + size))); ++ return true; + } + +-WebChannelIPCTransport::WebChannelIPCTransport(content::RenderView *renderView) +- : content::RenderViewObserver(renderView) +- , content::RenderViewObserverTracker<WebChannelIPCTransport>(renderView) +- , m_installed(false) +- , m_installedWorldId(0) ++gin::ObjectTemplateBuilder WebChannelTransport::GetObjectTemplateBuilder(v8::Isolate *isolate) + { ++ return gin::Wrappable<WebChannelTransport>::GetObjectTemplateBuilder(isolate) ++ .SetMethod("send", &WebChannelTransport::NativeQtSendMessage); + } + +-void WebChannelIPCTransport::RunScriptsAtDocumentStart(content::RenderFrame *render_frame) ++WebChannelIPCTransport::WebChannelIPCTransport(content::RenderFrame *renderFrame) ++ : content::RenderFrameObserver(renderFrame) + { +- // JavaScript run before this point doesn't stick, and needs to be redone. +- // ### FIXME: we should try no even installing before +- if (m_installed && render_frame->IsMainFrame()) +- WebChannelTransport::Install(render_frame->GetWebFrame(), m_installedWorldId); + } + +- +-void WebChannelIPCTransport::installWebChannel(uint worldId) ++void WebChannelIPCTransport::setWorldId(base::Optional<uint> worldId) + { +- blink::WebView *webView = render_view()->GetWebView(); +- if (!webView) ++ if (m_worldId == worldId) + return; +- WebChannelTransport::Install(webView->MainFrame(), worldId); +- m_installed = true; +- m_installedWorldId = worldId; +-} + +-void WebChannelIPCTransport::uninstallWebChannel(uint worldId) +-{ +- Q_ASSERT(worldId == m_installedWorldId); +- blink::WebView *webView = render_view()->GetWebView(); +- if (!webView) +- return; +- WebChannelTransport::Uninstall(webView->MainFrame(), worldId); +- m_installed = false; ++ if (m_worldId && m_canUseContext) ++ WebChannelTransport::Uninstall(render_frame()->GetWebFrame(), *m_worldId); ++ ++ m_worldId = worldId; ++ ++ if (m_worldId && m_canUseContext) ++ WebChannelTransport::Install(render_frame()->GetWebFrame(), *m_worldId); + } + +-void WebChannelIPCTransport::dispatchWebChannelMessage(const std::vector<char> &binaryJSON, uint worldId) ++void WebChannelIPCTransport::dispatchWebChannelMessage(const std::vector<char> &binaryJson, uint worldId) + { +- blink::WebView *webView = render_view()->GetWebView(); +- if (!webView) +- return; ++ DCHECK(m_canUseContext); ++ DCHECK(m_worldId == worldId); + +- QJsonDocument doc = QJsonDocument::fromRawData(binaryJSON.data(), binaryJSON.size(), QJsonDocument::BypassValidation); +- Q_ASSERT(doc.isObject()); ++ QJsonDocument doc = QJsonDocument::fromRawData(binaryJson.data(), binaryJson.size(), QJsonDocument::BypassValidation); ++ DCHECK(doc.isObject()); + QByteArray json = doc.toJson(QJsonDocument::Compact); + +- v8::Isolate *isolate = v8::Isolate::GetCurrent(); ++ blink::WebLocalFrame *frame = render_frame()->GetWebFrame(); ++ v8::Isolate *isolate = blink::MainThreadIsolate(); + v8::HandleScope handleScope(isolate); +- blink::WebFrame *frame = webView->MainFrame(); +- v8::Handle<v8::Context> context; ++ v8::Local<v8::Context> context; + if (worldId == 0) +- context = frame->ToWebLocalFrame()->MainWorldScriptContext(); ++ context = frame->MainWorldScriptContext(); + else +- context = frame->ToWebLocalFrame()->IsolatedWorldScriptContext(worldId); ++ context = frame->IsolatedWorldScriptContext(worldId); + v8::Context::Scope contextScope(context); + +- v8::Handle<v8::Object> global(context->Global()); +- v8::Handle<v8::Value> qtObjectValue(global->Get(gin::StringToV8(isolate, "qt"))); +- if (!qtObjectValue->IsObject()) ++ v8::Local<v8::Object> global(context->Global()); ++ v8::Local<v8::Value> qtObjectValue(global->Get(gin::StringToV8(isolate, "qt"))); ++ if (qtObjectValue.IsEmpty() || !qtObjectValue->IsObject()) + return; +- v8::Handle<v8::Value> webChannelObjectValue(qtObjectValue->ToObject()->Get(gin::StringToV8(isolate, "webChannelTransport"))); +- if (!webChannelObjectValue->IsObject()) ++ v8::Local<v8::Object> qtObject = v8::Local<v8::Object>::Cast(qtObjectValue); ++ v8::Local<v8::Value> webChannelObjectValue(qtObject->Get(gin::StringToV8(isolate, "webChannelTransport"))); ++ if (webChannelObjectValue.IsEmpty() || !webChannelObjectValue->IsObject()) + return; +- v8::Handle<v8::Value> onmessageCallbackValue(webChannelObjectValue->ToObject()->Get(gin::StringToV8(isolate, "onmessage"))); +- if (!onmessageCallbackValue->IsFunction()) { +- qWarning("onmessage is not a callable property of qt.webChannelTransport. Some things might not work as expected."); ++ v8::Local<v8::Object> webChannelObject = v8::Local<v8::Object>::Cast(webChannelObjectValue); ++ v8::Local<v8::Value> callbackValue(webChannelObject->Get(gin::StringToV8(isolate, "onmessage"))); ++ if (callbackValue.IsEmpty() || !callbackValue->IsFunction()) { ++ LOG(WARNING) << "onmessage is not a callable property of qt.webChannelTransport. Some things might not work as expected."; + return; + } + +- v8::Handle<v8::Object> messageObject(v8::Object::New(isolate)); ++ v8::Local<v8::Object> messageObject(v8::Object::New(isolate)); + v8::Maybe<bool> wasSet = messageObject->DefineOwnProperty( + context, + v8::String::NewFromUtf8(isolate, "data"), + v8::String::NewFromUtf8(isolate, json.constData(), v8::String::kNormalString, json.size()), + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); +- Q_ASSERT(!wasSet.IsNothing() && wasSet.FromJust()); ++ DCHECK(!wasSet.IsNothing() && wasSet.FromJust()); ++ ++ v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(callbackValue); ++ v8::Local<v8::Value> argv[] = { messageObject }; ++ frame->CallFunctionEvenIfScriptDisabled(callback, webChannelObject, 1, argv); ++} ++ ++void WebChannelIPCTransport::WillReleaseScriptContext(v8::Local<v8::Context> context, int worldId) ++{ ++ if (static_cast<uint>(worldId) == m_worldId) ++ m_canUseContext = false; ++} + +- v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(onmessageCallbackValue); +- const int argc = 1; +- v8::Handle<v8::Value> argv[argc]; +- argv[0] = messageObject; +- frame->ToWebLocalFrame()->CallFunctionEvenIfScriptDisabled(callback, webChannelObjectValue->ToObject(), argc, argv); ++void WebChannelIPCTransport::DidClearWindowObject() ++{ ++ if (!m_canUseContext) { ++ m_canUseContext = true; ++ if (m_worldId) ++ WebChannelTransport::Install(render_frame()->GetWebFrame(), *m_worldId); ++ } + } + + bool WebChannelIPCTransport::OnMessageReceived(const IPC::Message &message) + { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(WebChannelIPCTransport, message) +- IPC_MESSAGE_HANDLER(WebChannelIPCTransport_Install, installWebChannel) +- IPC_MESSAGE_HANDLER(WebChannelIPCTransport_Uninstall, uninstallWebChannel) ++ IPC_MESSAGE_HANDLER(WebChannelIPCTransport_SetWorldId, setWorldId) + IPC_MESSAGE_HANDLER(WebChannelIPCTransport_Message, dispatchWebChannelMessage) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() +@@ -259,4 +254,4 @@ void WebChannelIPCTransport::OnDestruct() + delete this; + } + +-} // namespace ++} // namespace QtWebEngineCore +diff --git a/src/core/renderer/web_channel_ipc_transport.h b/src/core/renderer/web_channel_ipc_transport.h +index 04041c6c7d0..19494360a5a 100644 +--- a/src/core/renderer/web_channel_ipc_transport.h ++++ b/src/core/renderer/web_channel_ipc_transport.h +@@ -40,41 +40,31 @@ + #ifndef WEB_CHANNEL_IPC_TRANSPORT_H + #define WEB_CHANNEL_IPC_TRANSPORT_H + +-#include "base/values.h" +-#include "content/public/renderer/render_view_observer.h" +-#include "content/public/renderer/render_view_observer_tracker.h" ++#include "content/public/renderer/render_frame_observer.h" + + #include <QtCore/qglobal.h> + +-namespace content { +-class RenderFrame; +-} +- +-namespace v8 { +-class Extension; +-} +- + namespace QtWebEngineCore { + +-class WebChannelIPCTransport : public content::RenderViewObserver +- , public content::RenderViewObserverTracker<WebChannelIPCTransport> +-{ ++class WebChannelIPCTransport : private content::RenderFrameObserver { + public: +- WebChannelIPCTransport(content::RenderView *); +- +- void RunScriptsAtDocumentStart(content::RenderFrame *render_frame); ++ WebChannelIPCTransport(content::RenderFrame *); + + private: +- void dispatchWebChannelMessage(const std::vector<char> &binaryJSON, uint worldId); +- void installWebChannel(uint worldId); +- void uninstallWebChannel(uint worldId); ++ void setWorldId(base::Optional<uint> worldId); ++ void dispatchWebChannelMessage(const std::vector<char> &binaryJson, uint worldId); + +- // content::RenderViewObserver overrides: ++ // RenderFrameObserver ++ void WillReleaseScriptContext(v8::Local<v8::Context> context, int worldId) override; ++ void DidClearWindowObject() override; + bool OnMessageReceived(const IPC::Message &message) override; + void OnDestruct() override; + +- bool m_installed; +- uint m_installedWorldId; ++ // The worldId from our WebChannelIPCTransportHost or empty when there is no ++ // WebChannelIPCTransportHost. ++ base::Optional<uint> m_worldId; ++ // True means it's currently OK to manipulate the frame's script context. ++ bool m_canUseContext = false; + }; + + } // namespace +diff --git a/src/core/renderer_host/web_channel_ipc_transport_host.cpp b/src/core/renderer_host/web_channel_ipc_transport_host.cpp +index b624d7e454d..6b32093a6c3 100644 +--- a/src/core/renderer_host/web_channel_ipc_transport_host.cpp ++++ b/src/core/renderer_host/web_channel_ipc_transport_host.cpp +@@ -1,6 +1,6 @@ + /**************************************************************************** + ** +-** Copyright (C) 2016 The Qt Company Ltd. ++** Copyright (C) 2018 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtWebEngine module of the Qt Toolkit. +@@ -39,70 +39,71 @@ + + #include "web_channel_ipc_transport_host.h" + +-#include "base/strings/string16.h" +-#include "content/public/browser/render_view_host.h" ++#include "content/public/browser/render_frame_host.h" ++#include "content/public/browser/render_process_host.h" + #include "content/public/browser/web_contents.h" + + #include "common/qt_messages.h" +-#include "type_conversion.h" + + #include <QJsonDocument> + #include <QJsonObject> ++#include <QLoggingCategory> + + namespace QtWebEngineCore { + ++Q_LOGGING_CATEGORY(log, "qt.webengine.webchanneltransport"); ++ ++inline QDebug operator<<(QDebug stream, content::RenderFrameHost *frame) ++{ ++ return stream << "frame " << frame->GetRoutingID() << " in process " << frame->GetProcess()->GetID(); ++} ++ ++template <class T> ++inline QDebug operator<<(QDebug stream, const base::Optional<T> &opt) ++{ ++ if (opt) ++ return stream << *opt; ++ else ++ return stream << "nullopt"; ++} ++ + WebChannelIPCTransportHost::WebChannelIPCTransportHost(content::WebContents *contents, uint worldId, QObject *parent) + : QWebChannelAbstractTransport(parent) + , content::WebContentsObserver(contents) +- , m_worldId(worldId) + { +- contents->GetRenderViewHost()->Send( +- new WebChannelIPCTransport_Install( +- contents->GetRenderViewHost()->GetRoutingID(), +- m_worldId)); ++ setWorldId(worldId); + } + + WebChannelIPCTransportHost::~WebChannelIPCTransportHost() + { ++ setWorldId(base::nullopt); + } + +-void WebChannelIPCTransportHost::RenderViewHostChanged(content::RenderViewHost *oldHost, content::RenderViewHost *) +-{ +- if (oldHost) +- oldHost->Send(new WebChannelIPCTransport_Uninstall(oldHost->GetRoutingID(), m_worldId)); +-} +- +-void WebChannelIPCTransportHost::RenderViewCreated(content::RenderViewHost *view_host) ++void WebChannelIPCTransportHost::sendMessage(const QJsonObject &message) + { +- // Make sure the new view knows a webchannel is installed and in which world. +- view_host->Send(new WebChannelIPCTransport_Install(view_host->GetRoutingID(), m_worldId)); ++ QJsonDocument doc(message); ++ int size = 0; ++ const char *rawData = doc.rawData(&size); ++ content::RenderFrameHost *frame = web_contents()->GetMainFrame(); ++ qCDebug(log).nospace() << "sending webchannel message to " << frame << ": " << doc; ++ frame->Send(new WebChannelIPCTransport_Message(frame->GetRoutingID(), std::vector<char>(rawData, rawData + size), *m_worldId)); + } + +-void WebChannelIPCTransportHost::setWorldId(uint worldId) ++void WebChannelIPCTransportHost::setWorldId(base::Optional<uint> worldId) + { +- if (worldId == m_worldId) ++ if (m_worldId == worldId) + return; +- web_contents()->GetRenderViewHost()->Send( +- new WebChannelIPCTransport_Uninstall( +- web_contents()->GetRenderViewHost()->GetRoutingID(), +- m_worldId)); ++ for (content::RenderFrameHost *frame : web_contents()->GetAllFrames()) ++ setWorldId(frame, worldId); + m_worldId = worldId; +- web_contents()->GetRenderViewHost()->Send( +- new WebChannelIPCTransport_Install( +- web_contents()->GetRenderViewHost()->GetRoutingID(), +- m_worldId)); + } + +-void WebChannelIPCTransportHost::sendMessage(const QJsonObject &message) ++void WebChannelIPCTransportHost::setWorldId(content::RenderFrameHost *frame, base::Optional<uint> worldId) + { +- QJsonDocument doc(message); +- int size = 0; +- const char *rawData = doc.rawData(&size); +- web_contents()->GetRenderViewHost()->Send( +- new WebChannelIPCTransport_Message( +- web_contents()->GetRenderViewHost()->GetRoutingID(), +- std::vector<char>(rawData, rawData + size), +- m_worldId)); ++ if (!frame->IsRenderFrameLive()) ++ return; ++ qCDebug(log).nospace() << "sending setWorldId(" << worldId << ") message to " << frame; ++ frame->Send(new WebChannelIPCTransport_SetWorldId(frame->GetRoutingID(), worldId)); + } + + void WebChannelIPCTransportHost::onWebChannelMessage(const std::vector<char> &message) +@@ -110,11 +111,21 @@ void WebChannelIPCTransportHost::onWebChannelMessage(const std::vector<char> &me + Q_ASSERT(!message.empty()); + QJsonDocument doc = QJsonDocument::fromRawData(message.data(), message.size(), QJsonDocument::BypassValidation); + Q_ASSERT(doc.isObject()); ++ content::RenderFrameHost *frame = web_contents()->GetMainFrame(); ++ qCDebug(log).nospace() << "received webchannel message from " << frame << ": " << doc; + Q_EMIT messageReceived(doc.object(), this); + } + +-bool WebChannelIPCTransportHost::OnMessageReceived(const IPC::Message &message) ++void WebChannelIPCTransportHost::RenderFrameCreated(content::RenderFrameHost *frame) + { ++ setWorldId(frame, m_worldId); ++} ++ ++bool WebChannelIPCTransportHost::OnMessageReceived(const IPC::Message& message, content::RenderFrameHost *receiver) ++{ ++ if (receiver != web_contents()->GetMainFrame()) ++ return false; ++ + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(WebChannelIPCTransportHost, message) + IPC_MESSAGE_HANDLER(WebChannelIPCTransportHost_SendMessage, onWebChannelMessage) +@@ -123,4 +134,4 @@ bool WebChannelIPCTransportHost::OnMessageReceived(const IPC::Message &message) + return handled; + } + +-} // namespace ++} // namespace QtWebEngineCore +diff --git a/src/core/renderer_host/web_channel_ipc_transport_host.h b/src/core/renderer_host/web_channel_ipc_transport_host.h +index a1e697a910a..3a814a79419 100644 +--- a/src/core/renderer_host/web_channel_ipc_transport_host.h ++++ b/src/core/renderer_host/web_channel_ipc_transport_host.h +@@ -1,6 +1,6 @@ + /**************************************************************************** + ** +-** Copyright (C) 2016 The Qt Company Ltd. ++** Copyright (C) 2018 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtWebEngine module of the Qt Toolkit. +@@ -40,38 +40,40 @@ + #ifndef WEB_CHANNEL_IPC_TRANSPORT_H + #define WEB_CHANNEL_IPC_TRANSPORT_H + ++#include "qtwebenginecoreglobal.h" + +-#include <QtWebChannel/QWebChannelAbstractTransport> + #include "content/public/browser/web_contents_observer.h" + +-#include "qtwebenginecoreglobal.h" +-#include <QtCore/QObject> ++#include <QWebChannelAbstractTransport> + + QT_FORWARD_DECLARE_CLASS(QString) + + namespace QtWebEngineCore { + + class WebChannelIPCTransportHost : public QWebChannelAbstractTransport +- , public content::WebContentsObserver +-{ ++ , private content::WebContentsObserver { + public: +- WebChannelIPCTransportHost(content::WebContents *, uint worldId = 0, QObject *parent = 0); ++ WebChannelIPCTransportHost(content::WebContents *webContents, uint worldId = 0, QObject *parent = nullptr); + virtual ~WebChannelIPCTransportHost(); + +- // WebContentsObserver +- void RenderViewHostChanged(content::RenderViewHost* old_host, content::RenderViewHost* new_host) override; +- void RenderViewCreated(content::RenderViewHost* render_view_host) override; ++ void setWorldId(uint worldId) { setWorldId(base::Optional<uint>(worldId)); } ++ uint worldId() const { return *m_worldId; } + + // QWebChannelAbstractTransport + void sendMessage(const QJsonObject &message) override; + +- void setWorldId(uint worldId); +- uint worldId() const { return m_worldId; } +- + private: +- bool OnMessageReceived(const IPC::Message& message) override; ++ void setWorldId(base::Optional<uint> worldId); ++ void setWorldId(content::RenderFrameHost *frame, base::Optional<uint> worldId); + void onWebChannelMessage(const std::vector<char> &message); +- uint m_worldId; ++ ++ // WebContentsObserver ++ void RenderFrameCreated(content::RenderFrameHost *frame) override; ++ bool OnMessageReceived(const IPC::Message& message, content::RenderFrameHost *receiver) override; ++ ++ // Empty only during construction/destruction. Synchronized to all the ++ // WebChannelIPCTransports/RenderFrames in the observed WebContents. ++ base::Optional<uint> m_worldId; + }; + + } // namespace +diff --git a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp +index d852ca90200..e342632e75f 100644 +--- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp ++++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp +@@ -20,6 +20,7 @@ + #include <QtTest/QtTest> + + #include <qwebenginepage.h> ++#include <qwebengineprofile.h> + #include <qwebenginescript.h> + #include <qwebenginescriptcollection.h> + #include <qwebengineview.h> +@@ -39,6 +40,9 @@ private Q_SLOTS: + void webChannel(); + void noTransportWithoutWebChannel(); + void scriptsInNestedIframes(); ++ void webChannelResettingAndUnsetting(); ++ void webChannelWithExistingQtObject(); ++ void navigation(); + }; + + void tst_QWebEngineScript::domEditing() +@@ -183,6 +187,27 @@ private: + QString m_text; + }; + ++static QString readFile(const QString &path) ++{ ++ QFile file(path); ++ file.open(QFile::ReadOnly); ++ QByteArray contents = file.readAll(); ++ file.close(); ++ return contents; ++} ++ ++static QWebEngineScript webChannelScript() ++{ ++ QString sourceCode = readFile(QStringLiteral(":/qwebchannel.js")); ++ if (sourceCode.isEmpty()) ++ return {}; ++ ++ QWebEngineScript script; ++ script.setSourceCode(sourceCode); ++ script.setInjectionPoint(QWebEngineScript::DocumentCreation); ++ script.setWorldId(QWebEngineScript::MainWorld); ++ return script; ++} + + void tst_QWebEngineScript::webChannel_data() + { +@@ -204,15 +229,8 @@ void tst_QWebEngineScript::webChannel() + channel->registerObject(QStringLiteral("object"), &testObject); + page.setWebChannel(channel.data(), worldId); + +- QFile qwebchanneljs(":/qwebchannel.js"); +- QVERIFY(qwebchanneljs.exists()); +- qwebchanneljs.open(QFile::ReadOnly); +- QByteArray scriptSrc = qwebchanneljs.readAll(); +- qwebchanneljs.close(); +- QWebEngineScript script; +- script.setInjectionPoint(QWebEngineScript::DocumentCreation); ++ QWebEngineScript script = webChannelScript(); + script.setWorldId(worldId); +- script.setSourceCode(QString::fromLatin1(scriptSrc)); + page.scripts().insert(script); + page.setHtml(QStringLiteral("<html><body></body></html>")); + QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); +@@ -300,6 +318,93 @@ void tst_QWebEngineScript::scriptsInNestedIframes() + QVariant::fromValue(QStringLiteral("Modified Inner text"))); + } + ++void tst_QWebEngineScript::webChannelResettingAndUnsetting() ++{ ++ QWebEnginePage page; ++ ++ // There should be no webChannelTransport yet. ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "qt.webChannelTransport", QWebEngineScript::MainWorld), ++ QVariant(QVariant::Invalid)); ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "qt.webChannelTransport", QWebEngineScript::ApplicationWorld), ++ QVariant(QVariant::Invalid)); ++ ++ QWebChannel channel; ++ page.setWebChannel(&channel, QWebEngineScript::MainWorld); ++ ++ // There should be one in MainWorld now. ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "qt.webChannelTransport", QWebEngineScript::MainWorld), ++ QVariant(QVariantMap())); ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "qt.webChannelTransport", QWebEngineScript::ApplicationWorld), ++ QVariant(QVariant::Invalid)); ++ ++ page.setWebChannel(&channel, QWebEngineScript::ApplicationWorld); ++ ++ // Now it should have moved to ApplicationWorld. ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "qt.webChannelTransport", QWebEngineScript::MainWorld), ++ QVariant(QVariant::Invalid)); ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "qt.webChannelTransport", QWebEngineScript::ApplicationWorld), ++ QVariant(QVariantMap())); ++ ++ page.setWebChannel(nullptr); ++ ++ // And now it should be gone again. ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "qt.webChannelTransport", QWebEngineScript::MainWorld), ++ QVariant(QVariant::Invalid)); ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "qt.webChannelTransport", QWebEngineScript::ApplicationWorld), ++ QVariant(QVariant::Invalid)); ++} ++ ++void tst_QWebEngineScript::webChannelWithExistingQtObject() ++{ ++ QWebEnginePage page; ++ ++ evaluateJavaScriptSync(&page, "qt = 42"); ++ QCOMPARE(evaluateJavaScriptSync(&page, "qt.webChannelTransport"), QVariant(QVariant::Invalid)); ++ ++ QWebChannel channel; ++ page.setWebChannel(&channel); ++ ++ // setWebChannel should have overwritten the qt variable ++ QCOMPARE(evaluateJavaScriptSync(&page, "qt.webChannelTransport"), QVariant(QVariantMap())); ++} ++ ++static QWebEngineScript locationMonitorScript() ++{ ++ QWebEngineScript script = webChannelScript(); ++ script.setSourceCode(script.sourceCode() + QStringLiteral(R"( ++ new QWebChannel(qt.webChannelTransport, channel => { ++ channel.objects.object.text = window.location.href; ++ }) ++ )")); ++ return script; ++} ++ ++void tst_QWebEngineScript::navigation() ++{ ++ QWebEnginePage page; ++ TestObject testObject; ++ QSignalSpy spyTextChanged(&testObject, &TestObject::textChanged); ++ QWebChannel channel; ++ channel.registerObject(QStringLiteral("object"), &testObject); ++ page.setWebChannel(&channel); ++ page.scripts().insert(locationMonitorScript()); ++ ++ QString url1 = QStringLiteral("about:blank"); ++ page.setUrl(url1); ++ QTRY_COMPARE(spyTextChanged.count(), 1); ++ QCOMPARE(testObject.text(), url1); ++ ++ QString url2 = QStringLiteral("chrome://gpu/"); ++ page.setUrl(url2); ++ QTRY_COMPARE(spyTextChanged.count(), 2); ++ QCOMPARE(testObject.text(), url2); ++ ++ QString url3 = QStringLiteral("qrc:/resources/test_iframe_main.html"); ++ page.setUrl(url3); ++ QTRY_COMPARE(spyTextChanged.count(), 3); ++ QCOMPARE(testObject.text(), url3); ++} ++ + QTEST_MAIN(tst_QWebEngineScript) + + #include "tst_qwebenginescript.moc" +-- +2.16.2 + Added: qtbug-66338.patch =================================================================== --- qtbug-66338.patch (rev 0) +++ qtbug-66338.patch 2018-03-21 22:28:03 UTC (rev 319784) @@ -0,0 +1,196 @@ +From 0ab2905aeb3de6f3bcff10730be5b77a78022567 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=BCri=20Valdmann?= <[email protected]> +Date: Mon, 5 Mar 2018 11:12:41 +0100 +Subject: [PATCH] Execute scripts asynchronously in UserResourceController + +Needed to avoid ScriptForbiddenScope DCHECK when executing isolated world +scripts in subframes on document creation. The main frame does not trigger the +DCHECK because the HTML parser is suspended and resumed before trying to execute +the scripts, which bypasses the DCHECK. The subframe HTML parser seems to work +in one go however, so some extra asynchronicity is needed. + +Also ExecuteScriptInIsolatedWorld is marked as deprecated in blink. + +Task-number: QTBUG-66338 +Change-Id: Ica4acb8ada4acc38aa5e1ca00e7512a2e69b785f +--- + src/core/renderer/user_resource_controller.cpp | 10 +++- + .../qwebenginescript/tst_qwebenginescript.cpp | 67 +++++++++++++++++----- + 2 files changed, 61 insertions(+), 16 deletions(-) + +diff --git a/src/core/renderer/user_resource_controller.cpp b/src/core/renderer/user_resource_controller.cpp +index eed5208766f..129123577e4 100644 +--- a/src/core/renderer/user_resource_controller.cpp ++++ b/src/core/renderer/user_resource_controller.cpp +@@ -191,9 +191,13 @@ void UserResourceController::runScripts(UserScriptData::InjectionPoint p, blink: + if (!scriptMatchesURL(script, frame->GetDocument().Url())) + continue; + blink::WebScriptSource source(blink::WebString::FromUTF8(script.source), script.url); +- if (script.worldId) +- frame->ExecuteScriptInIsolatedWorld(script.worldId, &source, /*numSources = */1, /*contentScriptExtentsionGroup = */ 0); +- else ++ if (script.worldId) { ++ unsigned numSources = 1; ++ bool userGesture = false; ++ blink::WebLocalFrame::ScriptExecutionType executionType = blink::WebLocalFrame::kAsynchronous; ++ blink::WebScriptExecutionCallback *callback = nullptr; ++ frame->RequestExecuteScriptInIsolatedWorld(script.worldId, &source, numSources, userGesture, executionType, callback); ++ } else + frame->ExecuteScript(source); + } + } +diff --git a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp +index 43e9d313565..3a7591a6981 100644 +--- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp ++++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp +@@ -27,6 +27,9 @@ + #include "../util.h" + #include <QWebChannel> + ++Q_DECLARE_METATYPE(QWebEngineScript::InjectionPoint); ++Q_DECLARE_METATYPE(QWebEngineScript::ScriptWorldId); ++ + class tst_QWebEngineScript: public QObject { + Q_OBJECT + +@@ -39,7 +42,10 @@ private Q_SLOTS: + void webChannel_data(); + void webChannel(); + void noTransportWithoutWebChannel(); ++ void scriptsInNestedIframes_data(); + void scriptsInNestedIframes(); ++ void scriptsInNestedIframesNoDom_data(); ++ void scriptsInNestedIframesNoDom(); + void webChannelResettingAndUnsetting(); + void webChannelWithExistingQtObject(); + void navigation(); +@@ -77,12 +83,12 @@ void tst_QWebEngineScript::domEditing() + + void tst_QWebEngineScript::injectionPoint() + { +- QFETCH(int, injectionPoint); ++ QFETCH(QWebEngineScript::InjectionPoint, injectionPoint); + QFETCH(QString, testScript); + + QWebEngineScript s; + s.setSourceCode("var foo = \"foobar\";"); +- s.setInjectionPoint(static_cast<QWebEngineScript::InjectionPoint>(injectionPoint)); ++ s.setInjectionPoint(injectionPoint); + s.setWorldId(QWebEngineScript::MainWorld); + QWebEnginePage page; + QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); +@@ -95,17 +101,17 @@ void tst_QWebEngineScript::injectionPoint() + + void tst_QWebEngineScript::injectionPoint_data() + { +- QTest::addColumn<int>("injectionPoint"); ++ QTest::addColumn<QWebEngineScript::InjectionPoint>("injectionPoint"); + QTest::addColumn<QString>("testScript"); +- QTest::newRow("DocumentCreation") << static_cast<int>(QWebEngineScript::DocumentCreation) ++ QTest::newRow("DocumentCreation") << QWebEngineScript::DocumentCreation + << QStringLiteral("document.myContents = (typeof(foo) == \"undefined\")? \"FAILURE\" : \"SUCCESS\";"); +- QTest::newRow("DocumentReady") << static_cast<int>(QWebEngineScript::DocumentReady) ++ QTest::newRow("DocumentReady") << QWebEngineScript::DocumentReady + // use a zero timeout to make sure the user script got a chance to run as the order is undefined. + << QStringLiteral("document.addEventListener(\"DOMContentLoaded\", function() {\ + setTimeout(function() {\ + document.myContents = (typeof(foo) == \"undefined\")? \"FAILURE\" : \"SUCCESS\";\ + }, 0)});"); +- QTest::newRow("Deferred") << static_cast<int>(QWebEngineScript::DocumentReady) ++ QTest::newRow("Deferred") << QWebEngineScript::DocumentReady + << QStringLiteral("document.onreadystatechange = function() { \ + if (document.readyState == \"complete\") { \ + setTimeout(function() {\ +@@ -266,14 +272,21 @@ void tst_QWebEngineScript::noTransportWithoutWebChannel() + QCOMPARE(evaluateJavaScriptSync(&page, "qt.webChannelTransport"), QVariant(QVariant::Invalid)); + } + ++void tst_QWebEngineScript::scriptsInNestedIframes_data() ++{ ++ QTest::addColumn<QWebEngineScript::ScriptWorldId>("worldId"); ++ QTest::newRow("MainWorld") << QWebEngineScript::MainWorld; ++ QTest::newRow("ApplicationWorld") << QWebEngineScript::ApplicationWorld; ++} ++ + void tst_QWebEngineScript::scriptsInNestedIframes() + { ++ QFETCH(QWebEngineScript::ScriptWorldId, worldId); ++ + QWebEnginePage page; +- QWebEngineView view; +- view.setPage(&page); + QWebEngineScript s; + s.setInjectionPoint(QWebEngineScript::DocumentReady); +- s.setWorldId(QWebEngineScript::ApplicationWorld); ++ s.setWorldId(worldId); + + // Prepend a "Modified prefix" to every frame's div content. + s.setSourceCode("var elements = document.getElementsByTagName(\"div\");\ +@@ -290,13 +303,12 @@ void tst_QWebEngineScript::scriptsInNestedIframes() + + QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); + page.load(QUrl("qrc:/resources/test_iframe_main.html")); +- view.show(); + QVERIFY(spyFinished.wait()); + + // Check that main frame has modified content. + QCOMPARE( + evaluateJavaScriptSyncInWorld(&page, "document.getElementsByTagName(\"div\")[0].innerHTML", +- QWebEngineScript::ApplicationWorld), ++ worldId), + QVariant::fromValue(QStringLiteral("Modified Main text"))); + + // Check that outer frame has modified content. +@@ -304,7 +316,7 @@ void tst_QWebEngineScript::scriptsInNestedIframes() + evaluateJavaScriptSyncInWorld(&page, + "var i = document.getElementById(\"outer\").contentDocument;\ + i.getElementsByTagName(\"div\")[0].innerHTML", +- QWebEngineScript::ApplicationWorld), ++ worldId), + QVariant::fromValue(QStringLiteral("Modified Outer text"))); + + +@@ -314,10 +326,39 @@ void tst_QWebEngineScript::scriptsInNestedIframes() + "var i = document.getElementById(\"outer\").contentDocument;\ + var i2 = i.getElementById(\"inner\").contentDocument;\ + i2.getElementsByTagName(\"div\")[0].innerHTML", +- QWebEngineScript::ApplicationWorld), ++ worldId), + QVariant::fromValue(QStringLiteral("Modified Inner text"))); + } + ++void tst_QWebEngineScript::scriptsInNestedIframesNoDom_data() ++{ ++ QTest::addColumn<QWebEngineScript::InjectionPoint>("injectionPoint"); ++ QTest::addColumn<QWebEngineScript::ScriptWorldId>("worldId"); ++ QTest::newRow("DocumentCreation/MainWorld") << QWebEngineScript::DocumentCreation << QWebEngineScript::MainWorld; ++ QTest::newRow("DocumentCreation/ApplicationWorld") << QWebEngineScript::DocumentCreation << QWebEngineScript::ApplicationWorld; ++ QTest::newRow("DocumentReady/MainWorld") << QWebEngineScript::DocumentReady << QWebEngineScript::MainWorld; ++ QTest::newRow("DocumentReady/ApplicationWorld") << QWebEngineScript::DocumentReady << QWebEngineScript::ApplicationWorld; ++} ++ ++void tst_QWebEngineScript::scriptsInNestedIframesNoDom() ++{ ++ QFETCH(QWebEngineScript::InjectionPoint, injectionPoint); ++ QFETCH(QWebEngineScript::ScriptWorldId, worldId); ++ QWebEnginePage page; ++ QWebEngineScript s; ++ s.setInjectionPoint(injectionPoint); ++ s.setWorldId(worldId); ++ s.setSourceCode("window.scriptOk = true"); ++ s.setRunsOnSubFrames(true); ++ page.scripts().insert(s); ++ QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); ++ page.load(QUrl("qrc:/resources/test_iframe_main.html")); ++ QVERIFY(spyFinished.wait()); ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "window.scriptOk", worldId), QVariant::fromValue(true)); ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "window[0].scriptOk", worldId), QVariant::fromValue(true)); ++ QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "window[0][0].scriptOk", worldId), QVariant::fromValue(true)); ++} ++ + void tst_QWebEngineScript::webChannelResettingAndUnsetting() + { + QWebEnginePage page; +-- +2.16.2 +
