Bug 1380186 implement SimpleChannel Parent/Child IPC, r?mayhemer,kmag
MozReview-Commit-ID: 8zgF2MLwdu2
--- a/netwerk/base/SimpleChannel.cpp
+++ b/netwerk/base/SimpleChannel.cpp
@@ -1,33 +1,44 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "SimpleChannel.h"
+
#include "nsBaseChannel.h"
+#include "nsIChannel.h"
+#include "nsIChildChannel.h"
#include "nsIInputStream.h"
#include "nsIRequest.h"
-#include "SimpleChannel.h"
+#include "nsISupportsImpl.h"
+#include "nsNetUtil.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/PSimpleChannelChild.h"
namespace mozilla {
namespace net {
// Like MOZ_TRY, but returns the unwrapped error value rather than a
// GenericErrorResult on failure.
#define TRY_VAR(target, expr) \
do { \
auto result = (expr); \
if (result.isErr()) { \
return result.unwrapErr(); \
} \
(target) = result.unwrap(); \
} while (0)
-class SimpleChannel final : public nsBaseChannel
+
+class SimpleChannel : public nsBaseChannel
{
public:
explicit SimpleChannel(UniquePtr<SimpleChannelCallbacks>&& aCallbacks);
protected:
virtual ~SimpleChannel() {}
virtual nsresult OpenContentStream(bool async, nsIInputStream **streamOut,
@@ -73,20 +84,106 @@ SimpleChannel::BeginAsyncRead(nsIStreamL
mCallbacks = nullptr;
req.forget(request);
return NS_OK;
}
#undef TRY_VAR
+class SimpleChannelChild final : public SimpleChannel
+ , public nsIChildChannel
+ , public PSimpleChannelChild
+{
+public:
+ explicit SimpleChannelChild(UniquePtr<SimpleChannelCallbacks>&& aCallbacks);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSICHILDCHANNEL
+
+protected:
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+private:
+ virtual ~SimpleChannelChild() = default;
+
+ void AddIPDLReference();
+
+ RefPtr<SimpleChannelChild> mIPDLRef;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(SimpleChannelChild, SimpleChannel, nsIChildChannel)
+
+SimpleChannelChild::SimpleChannelChild(UniquePtr<SimpleChannelCallbacks>&& aCallbacks)
+ : SimpleChannel(Move(aCallbacks))
+ , mIPDLRef(nullptr)
+{
+}
+
+NS_IMETHODIMP
+SimpleChannelChild::ConnectParent(uint32_t aId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mozilla::dom::ContentChild* cc =
+ static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager());
+ if (cc->IsShuttingDown()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!gNeckoChild->SendPSimpleChannelConstructor(this, aId)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // IPC now has a ref to us.
+ mIPDLRef = this;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener,
+ nsISupports* aContext)
+{
+ if (mIPDLRef) {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ nsresult rv;
+ if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
+ MOZ_ASSERT(!aContext, "aContext should be null!");
+ rv = AsyncOpen2(aListener);
+ } else {
+ rv = AsyncOpen(aListener, aContext);
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (mIPDLRef) {
+ Unused << Send__delete__(this);
+ }
+ return NS_OK;
+}
+
+void
+SimpleChannelChild::ActorDestroy(ActorDestroyReason why)
+{
+ MOZ_ASSERT(mIPDLRef);
+ mIPDLRef = nullptr;
+}
+
+
already_AddRefed<nsIChannel>
NS_NewSimpleChannelInternal(nsIURI* aURI, nsILoadInfo* aLoadInfo, UniquePtr<SimpleChannelCallbacks>&& aCallbacks)
{
- RefPtr<SimpleChannel> chan = new SimpleChannel(Move(aCallbacks));
+ RefPtr<SimpleChannel> chan;
+ if (IsNeckoChild()) {
+ chan = new SimpleChannelChild(Move(aCallbacks));
+ } else {
+ chan = new SimpleChannel(Move(aCallbacks));
+ }
chan->SetURI(aURI);
MOZ_ALWAYS_SUCCEEDS(chan->SetLoadInfo(aLoadInfo));
return chan.forget();
}
new file mode 100644
--- /dev/null
+++ b/netwerk/base/SimpleChannelParent.cpp
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 sw=4 sts=4 et tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SimpleChannelParent.h"
+#include "mozilla/Assertions.h"
+#include "nsNetUtil.h"
+#include "nsIChannel.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(SimpleChannelParent, nsIParentChannel, nsIStreamListener)
+
+bool
+SimpleChannelParent::Init(const uint32_t &channelId)
+{
+ nsCOMPtr<nsIChannel> channel;
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel)));
+
+ return true;
+}
+
+NS_IMETHODIMP
+SimpleChannelParent::SetParentListener(HttpChannelParentListener* aListener)
+{
+ // Nothing to do.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleChannelParent::NotifyTrackingProtectionDisabled()
+{
+ // Nothing to do.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleChannelParent::NotifyTrackingResource()
+{
+ // Nothing to do.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix)
+{
+ // nothing to do
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleChannelParent::Delete()
+{
+ // Nothing to do.
+ return NS_OK;
+}
+
+void
+SimpleChannelParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+}
+
+NS_IMETHODIMP
+SimpleChannelParent::OnStartRequest(nsIRequest* aRequest,
+ nsISupports* aContext)
+{
+ // We don't have a way to prevent nsBaseChannel from calling AsyncOpen on
+ // the created nsSimpleChannel. We don't have anywhere to send the data in the
+ // parent, so abort the binding.
+ return NS_BINDING_ABORTED;
+}
+
+NS_IMETHODIMP
+SimpleChannelParent::OnStopRequest(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsresult aStatusCode)
+{
+ // See above.
+ MOZ_ASSERT(NS_FAILED(aStatusCode));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SimpleChannelParent::OnDataAvailable(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsIInputStream* aInputStream,
+ uint64_t aOffset,
+ uint32_t aCount)
+{
+ // See above.
+ MOZ_CRASH("Should never be called");
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/base/SimpleChannelParent.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 sw=4 sts=4 et tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NS_SIMPLECHANNELPARENT_H
+#define NS_SIMPLECHANNELPARENT_H
+
+#include "nsIParentChannel.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/net/PSimpleChannelParent.h"
+
+namespace mozilla {
+namespace net {
+
+// In order to support HTTP redirects, we need to implement the HTTP
+// redirection API, which requires a class that implements nsIParentChannel
+// and which calls NS_LinkRedirectChannels.
+class SimpleChannelParent : public nsIParentChannel
+ , public PSimpleChannelParent
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPARENTCHANNEL
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ MOZ_MUST_USE bool Init(const uint32_t& aArgs);
+
+private:
+ ~SimpleChannelParent() = default;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif /* NS_SIMPLECHANNELPARENT_H */
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -174,16 +174,17 @@ EXPORTS.mozilla.net += [
'CaptivePortalService.h',
'ChannelDiverterChild.h',
'ChannelDiverterParent.h',
'Dashboard.h',
'DashboardTypes.h',
'MemoryDownloader.h',
'Predictor.h',
'ReferrerPolicy.h',
+ 'SimpleChannelParent.h',
'TCPFastOpen.h',
]
UNIFIED_SOURCES += [
'ArrayBufferInputStream.cpp',
'BackgroundFileSaver.cpp',
'CaptivePortalService.cpp',
'ChannelDiverterChild.cpp',
@@ -244,16 +245,17 @@ UNIFIED_SOURCES += [
'nsURLParsers.cpp',
'PollableEvent.cpp',
'Predictor.cpp',
'ProxyAutoConfig.cpp',
'RedirectChannelRegistrar.cpp',
'RequestContextService.cpp',
'SimpleBuffer.cpp',
'SimpleChannel.cpp',
+ 'SimpleChannelParent.cpp',
'StreamingProtocolService.cpp',
'TCPFastOpenLayer.cpp',
'ThrottleQueue.cpp',
'Tickler.cpp',
'TLSServerSocket.cpp',
]
if CONFIG['MOZ_RUST_URLPARSE']:
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -256,16 +256,30 @@ NeckoChild::AllocPFileChannelChild(const
bool
NeckoChild::DeallocPFileChannelChild(PFileChannelChild* child)
{
// NB: See FileChannelChild::ActorDestroy.
return true;
}
+PSimpleChannelChild*
+NeckoChild::AllocPSimpleChannelChild(const uint32_t& channelId)
+{
+ MOZ_ASSERT_UNREACHABLE("Should never get here");
+ return nullptr;
+}
+
+bool
+NeckoChild::DeallocPSimpleChannelChild(PSimpleChannelChild* child)
+{
+ // NB: See SimpleChannelChild::ActorDestroy.
+ return true;
+}
+
PRtspControllerChild*
NeckoChild::AllocPRtspControllerChild()
{
NS_NOTREACHED("AllocPRtspController should not be called");
return nullptr;
}
bool
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -66,16 +66,18 @@ protected:
const OriginAttributes& aOriginAttributes,
const uint32_t& aFlags,
const nsCString& aNetworkInterface) override;
virtual bool DeallocPDNSRequestChild(PDNSRequestChild*) override;
virtual PDataChannelChild* AllocPDataChannelChild(const uint32_t& channelId) override;
virtual bool DeallocPDataChannelChild(PDataChannelChild* child) override;
virtual PFileChannelChild* AllocPFileChannelChild(const uint32_t& channelId) override;
virtual bool DeallocPFileChannelChild(PFileChannelChild* child) override;
+ virtual PSimpleChannelChild* AllocPSimpleChannelChild(const uint32_t& channelId) override;
+ virtual bool DeallocPSimpleChannelChild(PSimpleChannelChild* child) override;
virtual PRtspControllerChild* AllocPRtspControllerChild() override;
virtual bool DeallocPRtspControllerChild(PRtspControllerChild*) override;
virtual PRtspChannelChild*
AllocPRtspChannelChild(const RtspChannelConnectArgs& aArgs)
override;
virtual bool DeallocPRtspChannelChild(PRtspChannelChild*) override;
virtual PChannelDiverterChild*
AllocPChannelDiverterChild(const ChannelDiverterArgs& channel) override;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -13,16 +13,17 @@
#include "mozilla/net/NeckoParent.h"
#include "mozilla/net/HttpChannelParent.h"
#include "mozilla/net/CookieServiceParent.h"
#include "mozilla/net/WyciwygChannelParent.h"
#include "mozilla/net/FTPChannelParent.h"
#include "mozilla/net/WebSocketChannelParent.h"
#include "mozilla/net/WebSocketEventListenerParent.h"
#include "mozilla/net/DataChannelParent.h"
+#include "mozilla/net/SimpleChannelParent.h"
#include "mozilla/net/AltDataOutputStreamParent.h"
#include "mozilla/Unused.h"
#include "mozilla/net/FileChannelParent.h"
#ifdef NECKO_PROTOCOL_rtsp
#include "mozilla/net/RtspControllerParent.h"
#include "mozilla/net/RtspChannelParent.h"
#endif
#include "mozilla/net/DNSRequestParent.h"
@@ -525,16 +526,39 @@ NeckoParent::RecvPDataChannelConstructor
const uint32_t& channelId)
{
DataChannelParent* p = static_cast<DataChannelParent*>(actor);
DebugOnly<bool> rv = p->Init(channelId);
MOZ_ASSERT(rv);
return IPC_OK();
}
+PSimpleChannelParent*
+NeckoParent::AllocPSimpleChannelParent(const uint32_t &channelId)
+{
+ RefPtr<SimpleChannelParent> p = new SimpleChannelParent();
+ return p.forget().take();
+}
+
+bool
+NeckoParent::DeallocPSimpleChannelParent(PSimpleChannelParent* actor)
+{
+ RefPtr<SimpleChannelParent> p = dont_AddRef(actor).downcast<SimpleChannelParent>();
+ return true;
+}
+
+mozilla::ipc::IPCResult
+NeckoParent::RecvPSimpleChannelConstructor(PSimpleChannelParent* actor,
+ const uint32_t& channelId)
+{
+ SimpleChannelParent* p = static_cast<SimpleChannelParent*>(actor);
+ MOZ_ALWAYS_TRUE(p->Init(channelId));
+ return IPC_OK();
+}
+
PFileChannelParent*
NeckoParent::AllocPFileChannelParent(const uint32_t &channelId)
{
RefPtr<FileChannelParent> p = new FileChannelParent();
return p.forget().take();
}
bool
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -177,16 +177,23 @@ protected:
virtual PDataChannelParent*
AllocPDataChannelParent(const uint32_t& channelId) override;
virtual bool DeallocPDataChannelParent(PDataChannelParent* parent) override;
virtual mozilla::ipc::IPCResult RecvPDataChannelConstructor(PDataChannelParent* aActor,
const uint32_t& channelId) override;
+ virtual PSimpleChannelParent*
+ AllocPSimpleChannelParent(const uint32_t& channelId) override;
+ virtual bool DeallocPSimpleChannelParent(PSimpleChannelParent* parent) override;
+
+ virtual mozilla::ipc::IPCResult RecvPSimpleChannelConstructor(PSimpleChannelParent* aActor,
+ const uint32_t& channelId) override;
+
virtual PFileChannelParent*
AllocPFileChannelParent(const uint32_t& channelId) override;
virtual bool DeallocPFileChannelParent(PFileChannelParent* parent) override;
virtual mozilla::ipc::IPCResult RecvPFileChannelConstructor(PFileChannelParent* aActor,
const uint32_t& channelId) override;
virtual PRtspControllerParent* AllocPRtspControllerParent() override;
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -15,16 +15,17 @@ include protocol PWebSocket;
include protocol PWebSocketEventListener;
include protocol PTCPSocket;
include protocol PTCPServerSocket;
include protocol PUDPSocket;
include protocol PDNSRequest;
include protocol PChannelDiverter;
include protocol PFileDescriptorSet;
include protocol PDataChannel;
+include protocol PSimpleChannel;
include protocol PTransportProvider;
include protocol PChildToParentStream; //FIXME: bug #792908
include protocol PParentToChildStream; //FIXME: bug #792908
include protocol PStunAddrsRequest;
include protocol PFileChannel;
include protocol PRtspController;
include protocol PRtspChannel;
@@ -51,16 +52,17 @@ nested(upto inside_cpow) sync protocol P
manages PFTPChannel;
manages PWebSocket;
manages PWebSocketEventListener;
manages PTCPSocket;
manages PTCPServerSocket;
manages PUDPSocket;
manages PDNSRequest;
manages PDataChannel;
+ manages PSimpleChannel;
manages PFileChannel;
manages PRtspController;
manages PRtspChannel;
manages PChannelDiverter;
manages PTransportProvider;
manages PAltDataOutputStream;
manages PStunAddrsRequest;
@@ -99,17 +101,17 @@ parent:
async CancelHTMLDNSPrefetch(nsString hostname, OriginAttributes originAttributes,
uint16_t flags, nsresult reason);
/**
* channelId is used to establish a connection between redirect channels in
* the parent and the child when we're redirecting to a data: URI.
*/
async PDataChannel(uint32_t channelId);
-
+ async PSimpleChannel(uint32_t channelId);
async PFileChannel(uint32_t channelId);
async PRtspController();
async PRtspChannel(RtspChannelConnectArgs args);
async PChannelDiverter(ChannelDiverterArgs channel);
/**
* These are called from the child with the results of the auth prompt.
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/PSimpleChannel.ipdl
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PNecko;
+include URIParams;
+
+namespace mozilla {
+namespace net {
+
+async protocol PSimpleChannel
+{
+ manager PNecko;
+
+parent:
+ // Note: channels are opened during construction, so no open method here:
+ // see PNecko.ipdl
+ async __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
--- a/netwerk/ipc/moz.build
+++ b/netwerk/ipc/moz.build
@@ -24,16 +24,17 @@ UNIFIED_SOURCES += [
IPDL_SOURCES = [
'NeckoChannelParams.ipdlh',
'PChannelDiverter.ipdl',
'PDataChannel.ipdl',
'PFileChannel.ipdl',
'PNecko.ipdl',
'PRtspChannel.ipdl',
'PRtspController.ipdl',
+ 'PSimpleChannel.ipdl',
]
# needed so --disable-webrtc builds work (yes, a bit messy)
if not CONFIG['MOZ_WEBRTC']:
IPDL_SOURCES += [
'../../media/mtransport/ipc/PStunAddrsRequest.ipdl',
]
EXPORTS.mozilla.net += [
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -50,16 +50,22 @@ function extractFromChannel(channel, key
return null;
}
function getData(channel) {
const key = "mozilla.webRequest.data";
return extractFromChannel(channel, key) || attachToChannel(channel, key, {});
}
+function getFinalChannelURI(channel) {
+ let {loadInfo} = channel;
+ // resultPrincipalURI may be null, but originalURI never will be.
+ return (loadInfo && loadInfo.resultPrincipalURI) || channel.originalURI;
+}
+
var RequestId = {
count: 1,
create(channel = null) {
let id = (this.count++).toString();
if (channel) {
getData(channel).requestId = id;
}
return id;
@@ -748,19 +754,20 @@ HttpObserverManager = {
data.suspended = true;
return true;
}
},
getRequestData(channel, loadContext, policyType, extraData) {
let {loadInfo} = channel;
+ let URI = getFinalChannelURI(channel);
let data = {
requestId: RequestId.get(channel),
- url: channel.URI.spec,
+ url: URI.spec,
method: channel.requestMethod,
browser: loadContext && loadContext.topFrameElement,
type: WebRequestCommon.typeForPolicyType(policyType),
fromCache: getData(channel).fromCache,
// Defaults for a top level request
windowId: 0,
parentWindowId: -1,
};
@@ -826,17 +833,20 @@ HttpObserverManager = {
}
return Object.assign(data, extraData);
},
canModify(channel) {
let {isHostPermitted} = AddonManagerPermissions;
- if (isHostPermitted(channel.URI.host)) {
+ // Bug 1334550 introduced the possibility of having a JAR uri here,
+ // use the result uri if possible in that case.
+ let URI = getFinalChannelURI(channel);
+ if (URI && isHostPermitted(URI.host)) {
return false;
}
let {loadInfo} = channel;
if (loadInfo && loadInfo.loadingPrincipal) {
let {loadingPrincipal} = loadInfo;
return loadingPrincipal.URI && !isHostPermitted(loadingPrincipal.URI.host);
@@ -867,17 +877,17 @@ HttpObserverManager = {
let policyType = (loadInfo ? loadInfo.externalContentPolicyType
: Ci.nsIContentPolicy.TYPE_OTHER);
let includeStatus = (["headersReceived", "authRequired", "onRedirect", "onStart", "onStop"].includes(kind) &&
channel instanceof Ci.nsIHttpChannel);
let canModify = this.canModify(channel);
let commonData = null;
- let uri = channel.URI;
+ let uri = getFinalChannelURI(channel);
let requestBody;
for (let [callback, opts] of this.listeners[kind].entries()) {
if (!this.shouldRunListener(policyType, uri, opts.filter)) {
continue;
}
if (!commonData) {
commonData = this.getRequestData(channel, loadContext, policyType, extraData);
@@ -1050,18 +1060,20 @@ HttpObserverManager = {
if (!channelData.hasAuthRequestor && this.shouldHookListener(this.listeners.authRequired, channel)) {
channel.notificationCallbacks = new AuthRequestor(channel, this);
channelData.hasAuthRequestor = true;
}
},
onChannelReplaced(oldChannel, newChannel) {
+ // We want originalURI, this will provide a moz-ext rather than jar or file
+ // uri on redirects.
this.runChannelListener(oldChannel, this.getLoadContext(oldChannel),
- "onRedirect", {redirectUrl: newChannel.URI.spec});
+ "onRedirect", {redirectUrl: newChannel.originalURI.spec});
},
onStartRequest(channel, loadContext) {
this.runChannelListener(channel, loadContext, "onStart");
},
onStopRequest(channel, loadContext) {
this.runChannelListener(channel, loadContext, "onStop");