Patch status: *should* be fixed in qtwebengine-6.8.2

Somewhat annoying issue for qutebrowser users[1][2] resulting in hints
not always being usable on some google-based sites (e.g. youtube).

Note: as of the writing of this, [3] hasn't been merged upstream (yet)
and so the final version of this patch may differ. Was added here early
to avoid revbumping qtwebengine post-6.8.1 release, final version will
likely land in 6.8.2 instead.

[1] https://github.com/qutebrowser/qutebrowser/issues/8197
[2] https://bugreports.qt.io/browse/QTBUG-131156
[3] https://codereview.qt-project.org/c/qt/qtwebengine/+/604899
--- a/src/core/renderer_host/user_resource_controller_host.cpp
+++ b/src/core/renderer_host/user_resource_controller_host.cpp
@@ -43,8 +43,7 @@
 void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameCreated(content::RenderFrameHost *renderFrameHost)
 {
-    content::WebContents *contents = web_contents();
     auto &remote = m_controllerHost->GetUserResourceControllerRenderFrame(renderFrameHost);
-    const QList<UserScript> scripts = m_controllerHost->m_perContentsScripts.value(contents);
-    for (const UserScript &script : scripts)
+    const auto scripts = m_controllerHost->m_perContentsScripts.constFind(web_contents());
+    for (const UserScript &script : *scripts)
         remote->AddScript(script.data());
 }
@@ -57,4 +56,10 @@
         remote->ClearScripts();
     }
+    if (newHost) {
+        auto &remote = m_controllerHost->GetUserResourceControllerRenderFrame(newHost);
+        const auto scripts = m_controllerHost->m_perContentsScripts.constFind(web_contents());
+        for (const UserScript &script : *scripts)
+            remote->AddScript(script.data());
+    }
 }
 
--- a/src/core/renderer_host/web_channel_ipc_transport_host.cpp
+++ b/src/core/renderer_host/web_channel_ipc_transport_host.cpp
@@ -108,4 +108,14 @@
 }
 
+void WebChannelIPCTransportHost::RenderFrameHostChanged(content::RenderFrameHost *oldHost, content::RenderFrameHost *newHost)
+{
+    if (oldHost) {
+        if (oldHost->IsRenderFrameLive())
+            GetWebChannelIPCTransportRemote(oldHost)->ResetWorldId();
+    }
+    if (newHost) // this might set it again, but that is harmless
+        setWorldId(newHost, m_worldId);
+}
+
 void WebChannelIPCTransportHost::RenderFrameDeleted(content::RenderFrameHost *rfh)
 {
--- a/src/core/renderer_host/web_channel_ipc_transport_host.h
+++ b/src/core/renderer_host/web_channel_ipc_transport_host.h
@@ -46,4 +46,5 @@
     // WebContentsObserver
     void RenderFrameCreated(content::RenderFrameHost *frame) override;
+    void RenderFrameHostChanged(content::RenderFrameHost *oldHost, content::RenderFrameHost *newHost) override;
     void RenderFrameDeleted(content::RenderFrameHost *render_frame_host) override;
 
--- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp
+++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp
@@ -70,4 +70,5 @@
     void webChannelWithExistingQtObject();
     void navigation();
+    void navigation2();
     void webChannelWithBadString();
     void webChannelWithJavaScriptDisabled();
@@ -578,4 +579,79 @@
 }
 
+void tst_QWebEngineScript::navigation2()
+{
+    QWebEngineProfile profile("navigation2");
+    QWebEnginePage page(&profile, nullptr);
+    QWebChannel channel;
+    page.setWebChannel(&channel);
+    QWebEngineScript s1;
+    s1.setInjectionPoint(QWebEngineScript::DocumentCreation);
+    // Check webchannel is installed before DocumentCreation scripts are run
+    // onload shouldn't have run, and neither should wasready
+    s1.setWorldId(QWebEngineScript::MainWorld);
+    s1.setSourceCode("document.passCreation = 0;" \
+                     "if (typeof qt !== undefined) document.passCreation++;" \
+                     "if (document.onloadran) document.passCreation++;" \
+                     "if (document.wasready) document.passCreation++;");
+    page.scripts().insert(s1);
+    QWebEngineScript s2;
+    s2.setInjectionPoint(QWebEngineScript::DocumentReady);
+    // onload shouldn't have run
+    s2.setWorldId(QWebEngineScript::MainWorld);
+    s2.setSourceCode("document.passReady = 0;" \
+                     "if (typeof qt !== undefined) document.passReady++;" \
+                     "if (document.passCreation > 0) document.passReady++;" \
+                     "if (document.passDeferred > 0) document.passReady++;" \
+                     "if (document.onloadran) document.passReady++;" \
+                     "if (document.wasready) document.passReady++;");
+    page.scripts().insert(s2);
+    QWebEngineScript s3;
+    s3.setInjectionPoint(QWebEngineScript::Deferred);
+    // all should have run
+    s3.setWorldId(QWebEngineScript::MainWorld);
+    s3.setSourceCode("document.passDeferred = 0;" \
+                     "if (typeof qt !== undefined) document.passDeferred++;" \
+                     "if (document.passCreation > 0) document.passDeferred++;" \
+                     "if (document.passReady > 0) document.passDeferred++;" \
+                     "if (document.onloadran) document.passDeferred++;" \
+                     "if (document.wasready) document.passDeferred++;");
+    page.scripts().insert(s3);
+
+
+    QString html("<html><head><script>" \
+                 "  document.onloadran = false; document.wasready = false;"\
+                 "  document.addEventListener(\"readystatechange\", (x) => { "\
+                 "      if (x.target.readyState === \"interactive\") document.wasready= true;"\
+                 "  });"\
+                 "  function bodyload() { document.onloadran = true; };"\
+                 "</script></head>" \
+                 "<body onload='bodyload()'><p>hello world</p></body></html>");
+    page.setHtml(html, QUrl("about:blank"));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passCreation", QWebEngineScript::MainWorld),
+                 QVariant(1));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passReady", QWebEngineScript::MainWorld),
+                 QVariant(3));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passDeferred", QWebEngineScript::MainWorld),
+                 QVariant(5));
+
+    QString url2 = QStringLiteral("chrome://gpu/");
+    page.setUrl(url2);
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passCreation", QWebEngineScript::MainWorld),
+                 QVariant(1));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passReady", QWebEngineScript::MainWorld),
+                 QVariant(2));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passDeferred", QWebEngineScript::MainWorld),
+                 QVariant(3));
+
+    QString url3 = QStringLiteral("qrc:/resources/test_iframe_main.html");
+    page.setUrl(url3);
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passCreation", QWebEngineScript::MainWorld),
+                 QVariant(1));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passReady", QWebEngineScript::MainWorld),
+                 QVariant(2));
+    QTRY_COMPARE(evaluateJavaScriptSyncInWorld(&page, "document.passDeferred", QWebEngineScript::MainWorld),
+                 QVariant(3));
+}
+
 // Try to set TestObject::text to an invalid UTF-16 string.
 //
