Bug 1575744 - P2. Add nsIProcessSwitchRequestor interface. r=mayhemer,nika

Will allow for SessionStore.jsm process switching to be used by other objects than nsHttpChannel.

Differential Revision: https://phabricator.services.mozilla.com/D46015

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jean-Yves Avenard 2019-09-18 23:49:51 +00:00
Родитель 02d8a3d841
Коммит db9dcef811
15 изменённых файлов: 186 добавлений и 124 удалений

Просмотреть файл

@ -2606,8 +2606,8 @@ var SessionStoreInternal = {
},
// Examine the channel response to see if we should change the process
// performing the given load.
onMayChangeProcess(aChannel) {
// performing the given load. aRequestor implements nsIProcessSwitchRequestor
onMayChangeProcess(aRequestor) {
if (
!E10SUtils.useHttpResponseProcessSelection() &&
!E10SUtils.useCrossOriginOpenerPolicy()
@ -2615,19 +2615,26 @@ var SessionStoreInternal = {
return;
}
aChannel.QueryInterface(Ci.nsIHttpChannel);
let switchRequestor;
try {
switchRequestor = aRequestor.QueryInterface(Ci.nsIProcessSwitchRequestor);
} catch (e) {
debug(`[process-switch]: object not compatible with process switching `);
return;
}
if (!aChannel.isDocument || !aChannel.loadInfo) {
const channel = switchRequestor.channel;
if (!channel.isDocument || !channel.loadInfo) {
return; // Not a document load.
}
// Check that the document has a corresponding BrowsingContext.
let browsingContext;
let cp = aChannel.loadInfo.externalContentPolicyType;
let cp = channel.loadInfo.externalContentPolicyType;
if (cp == Ci.nsIContentPolicy.TYPE_DOCUMENT) {
browsingContext = aChannel.loadInfo.browsingContext;
browsingContext = channel.loadInfo.browsingContext;
} else {
browsingContext = aChannel.loadInfo.frameBrowsingContext;
browsingContext = channel.loadInfo.frameBrowsingContext;
}
if (!browsingContext) {
@ -2691,7 +2698,7 @@ var SessionStoreInternal = {
// Determine the process type the load should be performed in.
let resultPrincipal = Services.scriptSecurityManager.getChannelResultPrincipal(
aChannel
channel
);
let remoteType = E10SUtils.getRemoteTypeForPrincipal(
resultPrincipal,
@ -2703,7 +2710,7 @@ var SessionStoreInternal = {
if (
currentRemoteType == remoteType &&
(!E10SUtils.useCrossOriginOpenerPolicy() ||
!aChannel.hasCrossOriginOpenerPolicyMismatch())
!switchRequestor.hasCrossOriginOpenerPolicyMismatch())
) {
debug(`[process-switch]: type (${remoteType}) is compatible - ignoring`);
return;
@ -2719,7 +2726,7 @@ var SessionStoreInternal = {
const isCOOPSwitch =
E10SUtils.useCrossOriginOpenerPolicy() &&
aChannel.hasCrossOriginOpenerPolicyMismatch();
switchRequestor.hasCrossOriginOpenerPolicyMismatch();
// ------------------------------------------------------------------------
// DANGER ZONE: Perform a process switch into the new process. This is
@ -2729,11 +2736,11 @@ var SessionStoreInternal = {
let tabPromise = this._doProcessSwitch(
browsingContext,
remoteType,
aChannel,
channel,
identifier,
isCOOPSwitch
);
aChannel.switchProcessTo(tabPromise, identifier);
switchRequestor.switchProcessTo(tabPromise, identifier);
},
/* ........ nsISessionStore API .............. */

Просмотреть файл

@ -37,3 +37,41 @@ interface nsICrossProcessSwitchChannel : nsISupports
*/
void triggerCrossProcessSwitch(in nsIHttpChannel channel, in uint64_t identifier);
};
/**
* The nsIProcessSwitchRequestor interface allows clients to instruct
* SessionStore.jsm that a channel setup has completed and a process switch
* may be required. This works alongside the on-may-change-process observer
* notification.
* This interface must be used only from the XPCOM main thread.
*/
[scriptable, uuid(fce8497b-c57c-4557-b360-3efefc83eff5)]
interface nsIProcessSwitchRequestor : nsISupports
{
/**
* The underlying channel object that was intercepted and that could trigger
* a process.
*/
readonly attribute nsIChannel channel;
/**
* Instructs the callee to be loaded in a new process. Like 'redirectTo'
* this can only be used on channels that have not yet called their
* listener's OnStartRequest(). Can only be called during the
* http-on-may-change-process observer notification.
*
* @param aTabPromise a promise which resolves to a nsIRemotTab object
* which the load will proceed in.
* @param aIdentifier a 64-bit ID which will be provided to the
* ChildProcessChannelListener.
*/
[must_use] void switchProcessTo(in Promise aTabPromise,
in unsigned long long aIdentifier);
/**
* Used to determine if there is a Cross-Origin-Opener-Policy mismatch
* that would require switching the channel to another process.
* @throws NS_ERROR_NOT_AVAILABLE if we don't have a responseHead
*/
[must_use] boolean hasCrossOriginOpenerPolicyMismatch();
};

Просмотреть файл

@ -1926,17 +1926,6 @@ HttpBaseChannel::RedirectTo(nsIURI* targetURI) {
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SwitchProcessTo(mozilla::dom::Promise* aBrowserParent,
uint64_t aIdentifier) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
HttpBaseChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
HttpBaseChannel::UpgradeToSecure() {
// Upgrades are handled internally between http-on-modify-request and

Просмотреть файл

@ -219,9 +219,6 @@ class HttpBaseChannel : public nsHashPropertyBag,
NS_IMETHOD GetResponseStatusText(nsACString& aValue) override;
NS_IMETHOD GetRequestSucceeded(bool* aValue) override;
NS_IMETHOD RedirectTo(nsIURI* newURI) override;
NS_IMETHOD SwitchProcessTo(mozilla::dom::Promise* aBrowserParent,
uint64_t aIdentifier) override;
NS_IMETHOD HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) override;
NS_IMETHOD UpgradeToSecure() override;
NS_IMETHOD GetRequestContextID(uint64_t* aRCID) override;
NS_IMETHOD GetTransferSize(uint64_t* aTransferSize) override;

Просмотреть файл

@ -273,17 +273,6 @@ NullHttpChannel::RedirectTo(nsIURI* aNewURI) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
NullHttpChannel::SwitchProcessTo(mozilla::dom::Promise* aBrowserParent,
uint64_t aIdentifier) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
NullHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
NullHttpChannel::UpgradeToSecure() { return NS_ERROR_NOT_IMPLEMENTED; }

Просмотреть файл

@ -6139,6 +6139,7 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsIChannelWithDivertableParentListener)
NS_INTERFACE_MAP_ENTRY(nsIRequestTailUnblockCallback)
NS_INTERFACE_MAP_ENTRY(nsIProcessSwitchRequestor)
NS_INTERFACE_MAP_ENTRY_CONCRETE(nsHttpChannel)
NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
@ -7285,6 +7286,15 @@ class DomPromiseListener final : dom::PromiseNativeHandler {
NS_IMPL_ISUPPORTS0(DomPromiseListener)
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIProcessSwitchRequestor
//-----------------------------------------------------------------------------
NS_IMETHODIMP nsHttpChannel::GetChannel(nsIChannel** aChannel) {
*aChannel = do_AddRef(this).take();
return NS_OK;
}
NS_IMETHODIMP nsHttpChannel::SwitchProcessTo(
dom::Promise* aContentProcessIdPromise, uint64_t aIdentifier) {
MOZ_ASSERT(NS_IsMainThread());
@ -7310,6 +7320,19 @@ NS_IMETHODIMP nsHttpChannel::SwitchProcessTo(
return NS_OK;
}
// This method returns the cached result of running the Cross-Origin-Opener
// policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch
NS_IMETHODIMP
nsHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
MOZ_ASSERT(aMismatch);
if (!aMismatch) {
return NS_ERROR_INVALID_ARG;
}
*aMismatch = mHasCrossOriginOpenerPolicyMismatch;
return NS_OK;
}
nsresult nsHttpChannel::StartCrossProcessRedirect() {
nsresult rv;
@ -7373,19 +7396,6 @@ static bool CompareCrossOriginOpenerPolicies(
return false;
}
// This method returns the cached result of running the Cross-Origin-Opener
// policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch
NS_IMETHODIMP
nsHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
MOZ_ASSERT(aMismatch);
if (!aMismatch) {
return NS_ERROR_INVALID_ARG;
}
*aMismatch = mHasCrossOriginOpenerPolicyMismatch;
return NS_OK;
}
// This runs steps 1-5 of the algorithm when navigating a top level document.
// See https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
nsresult nsHttpChannel::ComputeCrossOriginOpenerPolicyMismatch() {

Просмотреть файл

@ -34,6 +34,7 @@
#include "mozilla/extensions/PStreamFilterParent.h"
#include "mozilla/Mutex.h"
#include "nsIRemoteTab.h"
#include "nsICrossProcessSwitchChannel.h"
class nsDNSPrefetch;
class nsICancelable;
@ -78,7 +79,8 @@ class nsHttpChannel final : public HttpBaseChannel,
public nsIChannelWithDivertableParentListener,
public nsIRaceCacheWithNetwork,
public nsIRequestTailUnblockCallback,
public nsITimerCallback {
public nsITimerCallback,
public nsIProcessSwitchRequestor {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIREQUESTOBSERVER
@ -100,6 +102,7 @@ class nsHttpChannel final : public HttpBaseChannel,
NS_DECL_NSIRACECACHEWITHNETWORK
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSIREQUESTTAILUNBLOCKCALLBACK
NS_DECL_NSIPROCESSSWITCHREQUESTOR
// nsIHttpAuthenticableChannel. We can't use
// NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and
@ -150,9 +153,6 @@ class nsHttpChannel final : public HttpBaseChannel,
NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
// nsIHttpChannel
NS_IMETHOD GetEncodedBodySize(uint64_t* aEncodedBodySize) override;
NS_IMETHOD SwitchProcessTo(mozilla::dom::Promise* aBrowserParent,
uint64_t aIdentifier) override;
NS_IMETHOD HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) override;
// nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char* aFallbackKey) override;
NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload) override;

Просмотреть файл

@ -15,6 +15,7 @@
#include "nsHttpChannel.h"
#include "nsHttpAuthCache.h"
#include "nsStandardURL.h"
#include "nsICrossProcessSwitchChannel.h"
#include "nsIDOMWindow.h"
#include "nsIHttpChannel.h"
#include "nsIStandardURL.h"
@ -806,6 +807,14 @@ void nsHttpHandler::NotifyObservers(nsIChannel* chan, const char* event) {
if (obsService) obsService->NotifyObservers(chan, event, nullptr);
}
void nsHttpHandler::NotifyObservers(nsIProcessSwitchRequestor* request,
const char* event) {
LOG(("nsHttpHandler::NotifyObservers [request=%p event=\"%s\"]\n", request,
event));
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
if (obsService) obsService->NotifyObservers(request, event, nullptr);
}
nsresult nsHttpHandler::AsyncOnChannelRedirect(
nsIChannel* oldChan, nsIChannel* newChan, uint32_t flags,
nsIEventTarget* mainThreadEventTarget) {

Просмотреть файл

@ -29,6 +29,7 @@ class nsIHttpChannel;
class nsIPrefBranch;
class nsICancelable;
class nsICookieService;
class nsIProcessSwitchRequestor;
class nsIIOService;
class nsIRequestContextService;
class nsISiteSecurityService;
@ -407,8 +408,8 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
NotifyObservers(chan, NS_HTTP_ON_EXAMINE_CACHED_RESPONSE_TOPIC);
}
void OnMayChangeProcess(nsIHttpChannel* chan) {
NotifyObservers(chan, NS_HTTP_ON_MAY_CHANGE_PROCESS_TOPIC);
void OnMayChangeProcess(nsIProcessSwitchRequestor* request) {
NotifyObservers(request, NS_HTTP_ON_MAY_CHANGE_PROCESS_TOPIC);
}
// Generates the host:port string for use in the Host: header as well as the
@ -479,6 +480,7 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
MOZ_MUST_USE nsresult InitConnectionMgr();
void NotifyObservers(nsIChannel* chan, const char* event);
void NotifyObservers(nsIProcessSwitchRequestor* request, const char* event);
void SetFastOpenOSSupport();

Просмотреть файл

@ -415,26 +415,6 @@ interface nsIHttpChannel : nsIIdentChannel
*/
[must_use] void redirectTo(in nsIURI aTargetURI);
/**
* Instructs the channel to be loaded in a new process. Like 'redirectTo'
* this can only be used on channels that have not yet called their
* listener's OnStartRequest().
*
* @param aTabPromise a promise which resolves to a nsIRemotTab object
* which the load will proceed in.
* @param aIdentifier a 64-bit ID which will be provided to the
* ChildProcessChannelListener.
*/
[must_use] void switchProcessTo(in Promise aTabPromise,
in unsigned long long aIdentifier);
/**
* Used to determine if there is a Cross-Origin-Opener-Policy mismatch
* that would require switching the channel to another process.
* @throws NS_ERROR_NOT_AVAILABLE if we don't have a responseHead
*/
[must_use] boolean hasCrossOriginOpenerPolicyMismatch();
/**
* Flags a channel to be upgraded to HTTPS.
*

Просмотреть файл

@ -175,9 +175,9 @@ interface nsIHttpProtocolHandler : nsIProxiedProtocolHandler
/**
* The observer of this topic is notified before before the response for a HTTP
* load is available. The "subject" of the notification is the nsIHttpChannel
* instance. Observers may call "switchProcessTo" to perform a process switch
* while this is being run.
* load is available. The "subject" of the notification is the
* nsIProcessSwitchRequestor instance. Observers may call "switchProcessTo" to
* perform a process switch while this is being run.
*/
#define NS_HTTP_ON_MAY_CHANGE_PROCESS_TOPIC "http-on-may-change-process"

Просмотреть файл

@ -22,6 +22,8 @@ EXPORTS += [
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/netwerk/base',
# For nsHttpChannel.h
'/netwerk/protocol/http',
]
include('/ipc/chromium/chromium-config.mozbuild')

Просмотреть файл

@ -5,17 +5,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsViewSourceChannel.h"
#include "nsIIOService.h"
#include "nsMimeTypes.h"
#include "nsNetUtil.h"
#include "nsContentUtils.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsContentSecurityManager.h"
#include "nsServiceManagerUtils.h"
#include "nsIInputStreamChannel.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/NullPrincipal.h"
#include "nsContentSecurityManager.h"
#include "nsContentUtils.h"
#include "nsHttpChannel.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsIIOService.h"
#include "nsIInputStreamChannel.h"
#include "nsIReferrerInfo.h"
#include "nsMimeTypes.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
NS_IMPL_ADDREF(nsViewSourceChannel)
NS_IMPL_RELEASE(nsViewSourceChannel)
@ -40,6 +41,8 @@ NS_INTERFACE_MAP_BEGIN(nsViewSourceChannel)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIRequest, nsIViewSourceChannel)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIChannel, nsIViewSourceChannel)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIViewSourceChannel)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIProcessSwitchRequestor,
IsNsHttpChannel())
NS_INTERFACE_MAP_END
nsresult nsViewSourceChannel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo) {
@ -942,24 +945,6 @@ nsViewSourceChannel::RedirectTo(nsIURI* uri) {
return !mHttpChannel ? NS_ERROR_NULL_POINTER : mHttpChannel->RedirectTo(uri);
}
NS_IMETHODIMP
nsViewSourceChannel::SwitchProcessTo(mozilla::dom::Promise* aBrowserParent,
uint64_t aIdentifier) {
return !mHttpChannel
? NS_ERROR_NULL_POINTER
: mHttpChannel->SwitchProcessTo(aBrowserParent, aIdentifier);
}
NS_IMETHODIMP
nsViewSourceChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
MOZ_ASSERT(aMismatch);
if (!aMismatch) {
return NS_ERROR_INVALID_ARG;
}
*aMismatch = false;
return NS_OK;
}
NS_IMETHODIMP
nsViewSourceChannel::UpgradeToSecure() {
return !mHttpChannel ? NS_ERROR_NULL_POINTER
@ -1057,3 +1042,49 @@ void nsViewSourceChannel::SetHasSandboxedAuxiliaryNavigations(
aHasSandboxedAuxiliaryNavigations);
}
}
//-----------------------------------------------------------------------------
// nsViewSourceChannel::nsIProcessSwitchRequestor
//-----------------------------------------------------------------------------
NS_IMETHODIMP nsViewSourceChannel::GetChannel(nsIChannel** aChannel) {
if (!IsNsHttpChannel()) {
return NS_ERROR_NOT_AVAILABLE;
}
*aChannel = mHttpChannel;
NS_ADDREF(*aChannel);
return NS_OK;
}
NS_IMETHODIMP
nsViewSourceChannel::SwitchProcessTo(mozilla::dom::Promise* aBrowserParent,
uint64_t aIdentifier) {
if (!IsNsHttpChannel()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<mozilla::net::nsHttpChannel> httpChannel =
do_QueryInterface(mHttpChannel);
return httpChannel->SwitchProcessTo(aBrowserParent, aIdentifier);
}
NS_IMETHODIMP
nsViewSourceChannel::HasCrossOriginOpenerPolicyMismatch(bool* aMismatch) {
if (!IsNsHttpChannel()) {
return NS_ERROR_NOT_AVAILABLE;
}
MOZ_ASSERT(aMismatch);
if (!aMismatch) {
return NS_ERROR_INVALID_ARG;
}
*aMismatch = false;
return NS_OK;
}
bool nsViewSourceChannel::IsNsHttpChannel() const {
if (!mHttpChannel) {
return false;
}
nsCOMPtr<mozilla::net::nsHttpChannel> httpChannel =
do_QueryInterface(mHttpChannel);
return !!httpChannel;
}

Просмотреть файл

@ -6,18 +6,19 @@
#ifndef nsViewSourceChannel_h___
#define nsViewSourceChannel_h___
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIViewSourceChannel.h"
#include "nsIURI.h"
#include "nsIStreamListener.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsICachingChannel.h"
#include "nsIApplicationCacheChannel.h"
#include "nsIFormPOSTActionChannel.h"
#include "mozilla/Attributes.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "nsCOMPtr.h"
#include "nsIApplicationCacheChannel.h"
#include "nsICachingChannel.h"
#include "nsICrossProcessSwitchChannel.h"
#include "nsIFormPOSTActionChannel.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIStreamListener.h"
#include "nsIURI.h"
#include "nsIViewSourceChannel.h"
#include "nsString.h"
class nsViewSourceChannel final : public nsIViewSourceChannel,
public nsIStreamListener,
@ -25,7 +26,8 @@ class nsViewSourceChannel final : public nsIViewSourceChannel,
public nsIHttpChannelInternal,
public nsICachingChannel,
public nsIApplicationCacheChannel,
public nsIFormPOSTActionChannel {
public nsIFormPOSTActionChannel,
public nsIProcessSwitchRequestor {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUEST
@ -35,6 +37,7 @@ class nsViewSourceChannel final : public nsIViewSourceChannel,
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSIHTTPCHANNEL
NS_DECL_NSIPROCESSSWITCHREQUESTOR
NS_FORWARD_SAFE_NSICACHEINFOCHANNEL(mCacheInfoChannel)
NS_FORWARD_SAFE_NSICACHINGCHANNEL(mCachingChannel)
NS_FORWARD_SAFE_NSIAPPLICATIONCACHECHANNEL(mApplicationCacheChannel)
@ -82,6 +85,9 @@ class nsViewSourceChannel final : public nsIViewSourceChannel,
bool mIsDocument; // keeps track of the LOAD_DOCUMENT_URI flag
bool mOpened;
bool mIsSrcdocChannel;
private:
bool IsNsHttpChannel() const;
};
#endif /* nsViewSourceChannel_h___ */

Просмотреть файл

@ -40,26 +40,28 @@ ProcessChooser.prototype = {
Services.obs.removeObserver(this, "http-on-may-change-process");
},
examine(aChannel) {
if (this.channel && this.channel != aChannel) {
examine(aRequestor) {
const channel = aRequestor.channel;
if (this.channel && this.channel != channel) {
// Hack: this is just so we don't get redirected multiple times.
info("same channel. give null");
return;
}
if (aChannel.URI.host != this.toDomain) {
info("wrong host for channel " + aChannel.URI.host);
if (channel.URI.host != this.toDomain) {
info("wrong host for channel " + channel.URI.host);
return;
}
let redirects = aChannel.loadInfo.redirectChain;
let redirects = channel.loadInfo.redirectChain;
if (redirects[redirects.length - 1].principal.URI.host != this.fromDomain) {
info("didn't find redirect");
return;
}
info("setting channel");
this.channel = aChannel;
this.channel = channel;
let self = this;
info("unregistering");
@ -79,13 +81,13 @@ ProcessChooser.prototype = {
});
info("calling switchprocessto");
aChannel.switchProcessTo(tabPromise, identifier);
aRequestor.switchProcessTo(tabPromise, identifier);
},
observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "http-on-may-change-process":
this.examine(aSubject.QueryInterface(Ci.nsIHttpChannel));
this.examine(aSubject.QueryInterface(Ci.nsIProcessSwitchRequestor));
break;
default:
ok(false, "Unexpected topic observed!");