diff --git a/src/core/common/qt_messages.h b/src/core/common/qt_messages.h
index 62d88521cf4b10f3e22461d869376189bd3a91e7..411d06bc85d69c23ddca49a515e55270ff9ceec8 100644
--- a/src/core/common/qt_messages.h
+++ b/src/core/common/qt_messages.h
@@ -40,8 +40,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 56ebfec30f9c62eea4821f22a8680cc265fd1e60..74edc43693dea66ede8444d03b0925c329082534 100644
--- a/src/core/renderer/content_renderer_client_qt.cpp
+++ b/src/core/renderer/content_renderer_client_qt.cpp
@@ -123,13 +123,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)
@@ -150,8 +151,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 534ee302dcbff8880064064f36260cbdeb35759e..bb544168f7b77a92dd28fbb8810dd5dea23da9a6 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 04041c6c7d0bc438f40bebfb977aa4e853e0f16a..19494360a5a0899908934e8e469ced657b597bef 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 b624d7e454d5322b9083e379bd80cb7a9c30edf5..6b32093a6c32f817d434a8f453b755f38bfd8fbb 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 a1e697a910ad84dd5cc8c5beb78ad616f6ccfaa2..3a814a7941997d3fcf37f4fcd4891aa8debfb98e 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 d852ca90200f14e3a5e1d9718bdc1356202fbc92..e342632e75fb875fb217b9d3a30cd8a6ce9e2616 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"