Backed out changesets f63a2cf3fa11 and 7d9a91ee3d48 (bug 1126089) for mochitest-bc failures.

This commit is contained in:
Ryan VanderMeulen 2015-03-13 13:42:57 -04:00
parent c23816a32e
commit fa73ab5dc5
21 changed files with 186 additions and 502 deletions

View File

@ -25,7 +25,7 @@ const Observer = {
start: function () {
Services.obs.addObserver(this, 'remote-browser-shown', false);
Services.obs.addObserver(this, 'inprocess-browser-shown', false);
Services.obs.addObserver(this, 'message-manager-close', false);
Services.obs.addObserver(this, 'message-manager-disconnect', false);
SystemAppProxy.getFrames().forEach(frame => {
let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
@ -40,7 +40,7 @@ const Observer = {
stop: function () {
Services.obs.removeObserver(this, 'remote-browser-shown');
Services.obs.removeObserver(this, 'inprocess-browser-shown');
Services.obs.removeObserver(this, 'message-manager-close');
Services.obs.removeObserver(this, 'message-manager-disconnect');
this._frames.clear();
this._apps.clear();
},
@ -61,7 +61,7 @@ const Observer = {
break;
// Every time an iframe is destroyed, its message manager also is
case 'message-manager-close':
case 'message-manager-disconnect':
this.onMessageManagerDestroyed(subject);
break;
}

View File

@ -684,7 +684,7 @@ this.UITour = {
}
this.tourBrowsersByWindow.get(window).add(browser);
Services.obs.addObserver(this, "message-manager-close", false);
Services.obs.addObserver(this, "message-manager-disconnect", false);
window.addEventListener("SSWindowClosing", this);
@ -736,7 +736,7 @@ this.UITour = {
switch (aTopic) {
// The browser message manager is disconnected when the <browser> is
// destroyed and we want to teardown at that point.
case "message-manager-close": {
case "message-manager-disconnect": {
let winEnum = Services.wm.getEnumerator("navigator:browser");
while (winEnum.hasMoreElements()) {
let window = winEnum.getNext();

View File

@ -7334,14 +7334,14 @@ nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
}
nsresult
nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer)
nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader)
{
mInitializableFrameLoaders.RemoveElement(aLoader);
if (mInDestructor) {
return NS_ERROR_FAILURE;
}
mFrameLoaderFinalizers.AppendElement(aFinalizer);
mFinalizableFrameLoaders.AppendElement(aLoader);
if (!mFrameLoaderRunner) {
mFrameLoaderRunner =
NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
@ -7366,7 +7366,7 @@ nsDocument::MaybeInitializeFinalizeFrameLoaders()
if (!nsContentUtils::IsSafeToRunScript()) {
if (!mInDestructor && !mFrameLoaderRunner &&
(mInitializableFrameLoaders.Length() ||
mFrameLoaderFinalizers.Length())) {
mFinalizableFrameLoaders.Length())) {
mFrameLoaderRunner =
NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
@ -7385,12 +7385,12 @@ nsDocument::MaybeInitializeFinalizeFrameLoaders()
loader->ReallyStartLoading();
}
uint32_t length = mFrameLoaderFinalizers.Length();
uint32_t length = mFinalizableFrameLoaders.Length();
if (length > 0) {
nsTArray<nsCOMPtr<nsIRunnable> > finalizers;
mFrameLoaderFinalizers.SwapElements(finalizers);
nsTArray<nsRefPtr<nsFrameLoader> > loaders;
mFinalizableFrameLoaders.SwapElements(loaders);
for (uint32_t i = 0; i < length; ++i) {
finalizers[i]->Run();
loaders[i]->Finalize();
}
}
}
@ -7407,6 +7407,20 @@ nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
}
}
bool
nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell)
{
if (aShell) {
uint32_t length = mFinalizableFrameLoaders.Length();
for (uint32_t i = 0; i < length; ++i) {
if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) {
return true;
}
}
}
return false;
}
nsIDocument*
nsDocument::RequestExternalResource(nsIURI* aURI,
nsINode* aRequestingNode,

View File

@ -1033,8 +1033,9 @@ public:
virtual void FlushSkinBindings() MOZ_OVERRIDE;
virtual nsresult InitializeFrameLoader(nsFrameLoader* aLoader) MOZ_OVERRIDE;
virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer) MOZ_OVERRIDE;
virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader) MOZ_OVERRIDE;
virtual void TryCancelFrameLoaderInitialization(nsIDocShell* aShell) MOZ_OVERRIDE;
virtual bool FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell) MOZ_OVERRIDE;
virtual nsIDocument*
RequestExternalResource(nsIURI* aURI,
nsINode* aRequestingNode,
@ -1767,7 +1768,7 @@ private:
nsString mLastStyleSheetSet;
nsTArray<nsRefPtr<nsFrameLoader> > mInitializableFrameLoaders;
nsTArray<nsCOMPtr<nsIRunnable> > mFrameLoaderFinalizers;
nsTArray<nsRefPtr<nsFrameLoader> > mFinalizableFrameLoaders;
nsRefPtr<nsRunnableMethod<nsDocument> > mFrameLoaderRunner;
nsRevocableEventPtr<nsRunnableMethod<nsDocument, void, false> >

View File

@ -107,6 +107,25 @@ using namespace mozilla::layers;
using namespace mozilla::layout;
typedef FrameMetrics::ViewID ViewID;
class nsAsyncDocShellDestroyer : public nsRunnable
{
public:
explicit nsAsyncDocShellDestroyer(nsIDocShell* aDocShell)
: mDocShell(aDocShell)
{
}
NS_IMETHOD Run()
{
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
if (base_win) {
base_win->Destroy();
}
return NS_OK;
}
nsRefPtr<nsIDocShell> mDocShell;
};
// Bug 136580: Limit to the number of nested content frames that can have the
// same URL. This is to stop content that is recursively loading
// itself. Note that "#foo" on the end of URL doesn't affect
@ -162,6 +181,7 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
nsFrameLoader::~nsFrameLoader()
{
mNeedsAsyncDestroy = true;
if (mMessageManager) {
mMessageManager->Disconnect();
}
@ -503,6 +523,16 @@ nsFrameLoader::GetDocShell(nsIDocShell **aDocShell)
return rv;
}
void
nsFrameLoader::Finalize()
{
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
if (base_win) {
base_win->Destroy();
}
mDocShell = nullptr;
}
static void
FirePageHideEvent(nsIDocShellTreeItem* aItem,
EventTarget* aChromeEventHandler)
@ -1328,59 +1358,29 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
return NS_OK;
}
void
nsFrameLoader::DestroyChild()
{
if (mRemoteBrowser) {
mRemoteBrowser->SetOwnerElement(nullptr);
mRemoteBrowser->Destroy();
mRemoteBrowser = nullptr;
}
}
NS_IMETHODIMP
nsFrameLoader::Destroy()
{
StartDestroy();
return NS_OK;
}
class nsFrameLoaderDestroyRunnable : public nsRunnable
{
enum DestroyPhase
{
// See the implementation of Run for an explanation of these phases.
eDestroyDocShell,
eWaitForUnloadMessage,
eDestroyComplete
};
nsRefPtr<nsFrameLoader> mFrameLoader;
DestroyPhase mPhase;
public:
explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader)
: mFrameLoader(aFrameLoader), mPhase(eDestroyDocShell) {}
NS_IMETHODIMP Run() MOZ_OVERRIDE;
};
void
nsFrameLoader::StartDestroy()
{
// nsFrameLoader::StartDestroy is called just before the frameloader is
// detached from the <browser> element. Destruction continues in phases via
// the nsFrameLoaderDestroyRunnable.
if (mDestroyCalled) {
return;
return NS_OK;
}
mDestroyCalled = true;
// After this point, we return an error when trying to send a message using
// the message manager on the frame.
if (mMessageManager) {
mMessageManager->Close();
mMessageManager->Disconnect();
}
// Retain references to the <browser> element and the frameloader in case we
// receive any messages from the message manager on the frame. These
// references are dropped in DestroyComplete.
if (mChildMessageManager || mRemoteBrowser) {
mOwnerContentStrong = mOwnerContent;
if (mRemoteBrowser) {
mRemoteBrowser->CacheFrameLoader(this);
}
if (mChildMessageManager) {
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
}
nsCOMPtr<nsIDocument> doc;
@ -1392,6 +1392,7 @@ nsFrameLoader::StartDestroy()
SetOwnerContent(nullptr);
}
DestroyChild();
// Seems like this is a dynamic frame removal.
if (dynamicSubframeRemoval) {
@ -1411,7 +1412,7 @@ nsFrameLoader::StartDestroy()
}
}
}
// Let our window know that we are gone
if (mDocShell) {
nsCOMPtr<nsPIDOMWindow> win_private(mDocShell->GetWindow());
@ -1420,122 +1421,23 @@ nsFrameLoader::StartDestroy()
}
}
nsCOMPtr<nsIRunnable> destroyRunnable = new nsFrameLoaderDestroyRunnable(this);
if (mNeedsAsyncDestroy || !doc ||
NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) {
NS_DispatchToCurrentThread(destroyRunnable);
if ((mNeedsAsyncDestroy || !doc ||
NS_FAILED(doc->FinalizeFrameLoader(this))) && mDocShell) {
nsCOMPtr<nsIRunnable> event = new nsAsyncDocShellDestroyer(mDocShell);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
NS_DispatchToCurrentThread(event);
// Let go of our docshell now that the async destroyer holds on to
// the docshell.
mDocShell = nullptr;
}
}
nsresult
nsFrameLoaderDestroyRunnable::Run()
{
switch (mPhase) {
case eDestroyDocShell:
mFrameLoader->DestroyDocShell();
// In the out-of-process case, TabParent will eventually call
// DestroyComplete once it receives a __delete__ message from the child. In
// the in-process case, we dispatch a series of runnables to ensure that
// DestroyComplete gets called at the right time. The frame loader is kept
// alive by mFrameLoader during this time.
if (mFrameLoader->mChildMessageManager) {
// When the docshell is destroyed, NotifyWindowIDDestroyed is called to
// asynchronously notify {outer,inner}-window-destroyed via a runnable. We
// don't want DestroyComplete to run until after those runnables have
// run. Since we're enqueueing ourselves after the window-destroyed
// runnables are enqueued, we're guaranteed to run after.
mPhase = eWaitForUnloadMessage;
NS_DispatchToCurrentThread(this);
}
break;
case eWaitForUnloadMessage:
// The *-window-destroyed observers have finished running at this
// point. However, it's possible that a *-window-destroyed observer might
// have sent a message using the message manager. These messages might not
// have been processed yet. So we enqueue ourselves again to ensure that
// DestroyComplete runs after all messages sent by *-window-destroyed
// observers have been processed.
mPhase = eDestroyComplete;
NS_DispatchToCurrentThread(this);
break;
case eDestroyComplete:
// Now that all messages sent by unload listeners and window destroyed
// observers have been processed, we disconnect the message manager and
// finish destruction.
mFrameLoader->DestroyComplete();
break;
}
// NOTE: 'this' may very well be gone by now.
return NS_OK;
}
void
nsFrameLoader::DestroyDocShell()
{
// This code runs after the frameloader has been detached from the <browser>
// element. We postpone this work because we may not be allowed to run
// script at that time.
// Ask the TabChild to fire the frame script "unload" event, destroy its
// docshell, and finally destroy the PBrowser actor. This eventually leads to
// nsFrameLoader::DestroyComplete being called.
if (mRemoteBrowser) {
mRemoteBrowser->Destroy();
}
// Fire the "unload" event if we're in-process.
if (mChildMessageManager) {
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->FireUnloadEvent();
}
// Destroy the docshell.
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
if (base_win) {
base_win->Destroy();
}
mDocShell = nullptr;
if (mChildMessageManager) {
// Stop handling events in the in-process frame script.
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->DisconnectEventListeners();
}
}
void
nsFrameLoader::DestroyComplete()
{
// We get here, as part of StartDestroy, after the docshell has been destroyed
// and all message manager messages sent during docshell destruction have been
// dispatched. We also get here if the child process crashes. In the latter
// case, StartDestroy might not have been called.
// Drop the strong references created in StartDestroy.
if (mChildMessageManager || mRemoteBrowser) {
mOwnerContentStrong = nullptr;
if (mRemoteBrowser) {
mRemoteBrowser->CacheFrameLoader(nullptr);
}
}
// Call TabParent::Destroy if we haven't already (in case of a crash).
if (mRemoteBrowser) {
mRemoteBrowser->SetOwnerElement(nullptr);
mRemoteBrowser->Destroy();
mRemoteBrowser = nullptr;
}
if (mMessageManager) {
mMessageManager->Disconnect();
}
if (mChildMessageManager) {
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
}
}
NS_IMETHODIMP
nsFrameLoader::GetDepthTooGreat(bool* aDepthTooGreat)
{

View File

@ -78,9 +78,7 @@ public:
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
nsresult CheckForRecursiveLoad(nsIURI* aURI);
nsresult ReallyStartLoading();
void StartDestroy();
void DestroyDocShell();
void DestroyComplete();
void Finalize();
nsIDocShell* GetExistingDocShell() { return mDocShell; }
mozilla::dom::EventTarget* GetTabChildGlobalAsEventTarget();
nsresult CreateStaticClone(nsIFrameLoader* aDest);
@ -321,11 +319,6 @@ private:
nsCOMPtr<nsIURI> mURIToLoad;
mozilla::dom::Element* mOwnerContent; // WEAK
// After the frameloader has been removed from the DOM but before all of the
// messages from the frame have been received, we keep a strong reference to
// our <browser> element.
nsRefPtr<mozilla::dom::Element> mOwnerContentStrong;
// Note: this variable must be modified only by ResetPermissionManagerStatus()
uint32_t mAppIdSentToPermissionManager;

View File

@ -301,8 +301,7 @@ SameProcessCpowHolder::ToObject(JSContext* aCx,
NS_IMETHODIMP
nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
nsIMessageListener* aListener,
bool aListenWhenClosed)
nsIMessageListener* aListener)
{
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
mListeners.Get(aMessage);
@ -321,7 +320,6 @@ nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
nsMessageListenerInfo* entry = listeners->AppendElement();
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
entry->mStrongListener = aListener;
entry->mListenWhenClosed = aListenWhenClosed;
return NS_OK;
}
@ -409,7 +407,6 @@ nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage,
nsMessageListenerInfo* entry = listeners->AppendElement();
entry->mWeakListener = weak;
entry->mListenWhenClosed = false;
return NS_OK;
}
@ -986,20 +983,6 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
mozilla::jsipc::CpowHolder* aCpows,
nsIPrincipal* aPrincipal,
InfallibleTArray<nsString>* aJSONRetVal)
{
return ReceiveMessage(aTarget, mClosed, aMessage, aIsSync,
aCloneData, aCpows, aPrincipal, aJSONRetVal);
}
nsresult
nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
bool aTargetClosed,
const nsAString& aMessage,
bool aIsSync,
const StructuredCloneData* aCloneData,
mozilla::jsipc::CpowHolder* aCpows,
nsIPrincipal* aPrincipal,
InfallibleTArray<nsString>* aJSONRetVal)
{
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
mListeners.Get(aMessage);
@ -1021,10 +1004,6 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
}
}
if (!listener.mListenWhenClosed && aTargetClosed) {
continue;
}
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
if (weakListener) {
wrappedJS = do_QueryInterface(weakListener);
@ -1175,7 +1154,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
}
}
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
return mParentManager ? mParentManager->ReceiveMessage(aTarget, aTargetClosed, aMessage,
return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
aIsSync, aCloneData,
aCpows, aPrincipal,
aJSONRetVal) : NS_OK;
@ -1255,27 +1234,9 @@ nsFrameMessageManager::RemoveFromParent()
mOwnedCallback = nullptr;
}
void
nsFrameMessageManager::Close()
{
if (!mClosed) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIContentFrameMessageManager*, this),
"message-manager-close", nullptr);
}
}
mClosed = true;
mCallback = nullptr;
mOwnedCallback = nullptr;
}
void
nsFrameMessageManager::Disconnect(bool aRemoveFromParent)
{
// Notify message-manager-close if we haven't already.
Close();
if (!mDisconnected) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
@ -1288,6 +1249,8 @@ nsFrameMessageManager::Disconnect(bool aRemoveFromParent)
}
mDisconnected = true;
mParentManager = nullptr;
mCallback = nullptr;
mOwnedCallback = nullptr;
if (!mHandlingMessage) {
mListeners.Clear();
}

View File

@ -133,7 +133,6 @@ struct nsMessageListenerInfo
// Exactly one of mStrongListener and mWeakListener must be non-null.
nsCOMPtr<nsIMessageListener> mStrongListener;
nsWeakPtr mWeakListener;
bool mListenWhenClosed;
};
@ -170,7 +169,6 @@ public:
mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)),
mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)),
mHandlingMessage(false),
mClosed(false),
mDisconnected(false),
mCallback(aCallback),
mParentManager(aParentManager)
@ -240,7 +238,6 @@ public:
mChildManagers.RemoveObject(aManager);
}
void Disconnect(bool aRemoveFromParent = true);
void Close();
void InitWithCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
@ -293,11 +290,6 @@ private:
JS::MutableHandle<JS::Value> aRetval,
bool aIsSync);
nsresult ReceiveMessage(nsISupports* aTarget, bool aTargetClosed, const nsAString& aMessage,
bool aIsSync, const StructuredCloneData* aCloneData,
mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
InfallibleTArray<nsString>* aJSONRetVal);
NS_IMETHOD LoadScript(const nsAString& aURL,
bool aAllowDelayedLoad,
bool aRunInGlobalScope);
@ -317,7 +309,6 @@ protected:
bool mIsBroadcaster; // true if the message manager is a broadcaster
bool mOwnsCallback;
bool mHandlingMessage;
bool mClosed; // true if we can no longer send messages
bool mDisconnected;
mozilla::dom::ipc::MessageManagerCallback* mCallback;
nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback;

View File

@ -61,7 +61,6 @@ class nsIObserver;
class nsIPresShell;
class nsIPrincipal;
class nsIRequest;
class nsIRunnable;
class nsIStreamListener;
class nsIStructuredCloneContainer;
class nsIStyleRule;
@ -1653,9 +1652,11 @@ public:
virtual nsresult InitializeFrameLoader(nsFrameLoader* aLoader) = 0;
// In case of failure, the caller must handle the error, for example by
// finalizing frame loader asynchronously.
virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer) = 0;
virtual nsresult FinalizeFrameLoader(nsFrameLoader* aLoader) = 0;
// Removes the frame loader of aShell from the initialization list.
virtual void TryCancelFrameLoaderInitialization(nsIDocShell* aShell) = 0;
// Returns true if the frame loader of aShell is in the finalization list.
virtual bool FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell) = 0;
/**
* Check whether this document is a root document that is not an

View File

@ -201,7 +201,7 @@ interface nsIMessageListener : nsISupports
void receiveMessage();
};
[scriptable, builtinclass, uuid(b949bfec-bb7d-47bc-b387-ac6a9b655072)]
[scriptable, builtinclass, uuid(aae827bd-acf1-45fe-a556-ea545d4c0804)]
interface nsIMessageListenerManager : nsISupports
{
/**
@ -213,16 +213,9 @@ interface nsIMessageListenerManager : nsISupports
*
* If the same listener registers twice for the same message, the
* second registration is ignored.
*
* Pass true for listenWhenClosed if you want to receive messages
* during the short period after a frame has been removed from the
* DOM and before its frame script has finished unloading. This
* parameter only has an effect for frame message managers in
* the main process. Default is false.
*/
void addMessageListener(in AString messageName,
in nsIMessageListener listener,
[optional] in boolean listenWhenClosed);
in nsIMessageListener listener);
/**
* Undo an |addMessageListener| call -- that is, calling this causes us to no
@ -259,7 +252,7 @@ interface nsIMessageListenerManager : nsISupports
* messages that are only delivered to its one parent-process message
* manager.
*/
[scriptable, builtinclass, uuid(bb5d79e4-e73c-45e7-9651-4d718f4b994c)]
[scriptable, builtinclass, uuid(d6b0d851-43e6-426d-9f13-054bc0198175)]
interface nsIMessageSender : nsIMessageListenerManager
{
/**
@ -291,7 +284,7 @@ interface nsIMessageSender : nsIMessageListenerManager
* manager will broadcast the message to all frame message managers
* within its window.
*/
[scriptable, builtinclass, uuid(4d7d62ad-4725-4f39-86cf-8fb22bf9c1d8)]
[scriptable, builtinclass, uuid(d36346b9-5d3b-497d-9c28-ffbc3e4f6d0d)]
interface nsIMessageBroadcaster : nsIMessageListenerManager
{
/**
@ -318,7 +311,7 @@ interface nsIMessageBroadcaster : nsIMessageListenerManager
nsIMessageListenerManager getChildAt(in unsigned long aIndex);
};
[scriptable, builtinclass, uuid(0e602c9e-1977-422a-a8e4-fe0d4a4f78d0)]
[scriptable, builtinclass, uuid(7fda0941-9dcc-448b-bd39-16373c5b4003)]
interface nsISyncMessageSender : nsIMessageSender
{
/**
@ -348,7 +341,7 @@ interface nsISyncMessageSender : nsIMessageSender
[optional] in nsIPrincipal principal);
};
[scriptable, builtinclass, uuid(13f3555f-769e-44ea-b607-5239230c3162)]
[scriptable, builtinclass, uuid(e04a7ade-c61a-46ec-9f13-efeabedd9d3d)]
interface nsIMessageManagerGlobal : nsISyncMessageSender
{
/**
@ -383,13 +376,13 @@ interface nsIContentFrameMessageManager : nsIMessageManagerGlobal
readonly attribute nsIDocShell docShell;
};
[uuid(a9e07e89-7125-48e3-bf73-2cbae7fc5b1c)]
[uuid(a2325927-9c0c-437d-9215-749c79235031)]
interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
{
[notxpcom] nsIContent getOwnerContent();
};
[scriptable, builtinclass, uuid(d0c799a2-d5ff-4a75-acbb-b8c8347944a6)]
[scriptable, builtinclass, uuid(9ca95410-b253-11e4-ab27-0800200c9a66)]
interface nsIContentProcessMessageManager : nsIMessageManagerGlobal
{
};

View File

@ -101,7 +101,6 @@ nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell,
nsIContent* aOwner,
nsFrameMessageManager* aChrome)
: mDocShell(aShell), mInitialized(false), mLoadingScript(false),
mPreventEventsEscaping(false),
mOwner(aOwner), mChromeMessageManager(aChrome)
{
SetIsNotDOMBinding();
@ -208,24 +207,25 @@ nsInProcessTabChildGlobal::GetDocShell(nsIDocShell** aDocShell)
}
void
nsInProcessTabChildGlobal::FireUnloadEvent()
nsInProcessTabChildGlobal::Disconnect()
{
// We're called from nsDocument::MaybeInitializeFinalizeFrameLoaders, so it
// should be safe to run script.
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
// Don't let the unload event propagate to chrome event handlers.
mPreventEventsEscaping = true;
DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
// Allow events fired during docshell destruction (pagehide, unload) to
// propagate to the <browser> element since chrome code depends on this.
mPreventEventsEscaping = false;
// Let the frame scripts know the child is being closed. We do any other
// cleanup after the event has been fired. See DelayedDisconnect
nsContentUtils::AddScriptRunner(
NS_NewRunnableMethod(this, &nsInProcessTabChildGlobal::DelayedDisconnect)
);
}
void
nsInProcessTabChildGlobal::DisconnectEventListeners()
nsInProcessTabChildGlobal::DelayedDisconnect()
{
// Don't let the event escape
mOwner = nullptr;
// Fire the "unload" event
DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
// Continue with the Disconnect cleanup
if (mDocShell) {
nsCOMPtr<nsPIDOMWindow> win = mDocShell->GetWindow();
if (win) {
@ -233,22 +233,15 @@ nsInProcessTabChildGlobal::DisconnectEventListeners()
win->SetChromeEventHandler(win->GetChromeEventHandler());
}
}
if (mListenerManager) {
mListenerManager->Disconnect();
}
mDocShell = nullptr;
}
void
nsInProcessTabChildGlobal::Disconnect()
{
mChromeMessageManager = nullptr;
mOwner = nullptr;
if (mMessageManager) {
static_cast<nsFrameMessageManager*>(mMessageManager.get())->Disconnect();
mMessageManager = nullptr;
}
if (mListenerManager) {
mListenerManager->Disconnect();
}
}
NS_IMETHODIMP_(nsIContent *)
@ -262,6 +255,18 @@ nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor)
{
aVisitor.mCanHandle = true;
if (mIsBrowserOrAppFrame &&
(!mOwner || !nsContentUtils::IsInChromeDocshell(mOwner->OwnerDoc()))) {
if (mOwner) {
nsPIDOMWindow* innerWindow = mOwner->OwnerDoc()->GetInnerWindow();
if (innerWindow) {
aVisitor.mParentTarget = innerWindow->GetParentTarget();
}
}
} else {
aVisitor.mParentTarget = mOwner;
}
#ifdef DEBUG
if (mOwner) {
nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(mOwner);
@ -275,23 +280,6 @@ nsInProcessTabChildGlobal::PreHandleEvent(EventChainPreVisitor& aVisitor)
}
#endif
if (mPreventEventsEscaping) {
aVisitor.mParentTarget = nullptr;
return NS_OK;
}
if (mIsBrowserOrAppFrame &&
(!mOwner || !nsContentUtils::IsInChromeDocshell(mOwner->OwnerDoc()))) {
if (mOwner) {
nsPIDOMWindow* innerWindow = mOwner->OwnerDoc()->GetInnerWindow();
if (innerWindow) {
aVisitor.mParentTarget = innerWindow->GetParentTarget();
}
}
} else {
aVisitor.mParentTarget = mOwner;
}
return NS_OK;
}

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
/* vim: set sw=4 ts=8 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
@ -118,8 +118,6 @@ public:
virtual JSContext* GetJSContextForEventHandlers() MOZ_OVERRIDE { return nsContentUtils::GetSafeJSContext(); }
virtual nsIPrincipal* GetPrincipal() MOZ_OVERRIDE { return mPrincipal; }
void LoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope);
void FireUnloadEvent();
void DisconnectEventListeners();
void Disconnect();
void SendMessageToParent(const nsString& aMessage, bool aSync,
const nsString& aJSON,
@ -139,6 +137,8 @@ public:
mChromeMessageManager = aParent;
}
void DelayedDisconnect();
virtual JSObject* GetGlobalJSObject() MOZ_OVERRIDE {
if (!mGlobal) {
return nullptr;
@ -164,7 +164,6 @@ protected:
// <iframe mozapp>? This affects where events get sent, so it affects
// PreHandleEvent.
bool mIsBrowserOrAppFrame;
bool mPreventEventsEscaping;
public:
nsIContent* mOwner;
nsFrameMessageManager* mChromeMessageManager;

View File

@ -1,15 +1,10 @@
[DEFAULT]
support-files =
file_messagemanager_unload.html
[browser_bug593387.js]
skip-if = e10s # Bug ?????? - test directly touches content (contentWindow.iframe.addEventListener)
[browser_bug902350.js]
skip-if = e10s # Bug ?????? - test e10s utils don't support load events from iframe etc, which this test relies on.
[browser_messagemanager_loadprocessscript.js]
[browser_pagehide_on_tab_close.js]
skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
[browser_messagemanager_unload.js]
[browser_state_notifications.js]
# skip-if = e10s # Bug ?????? - content-document-* notifications come while document's URI is still about:blank, but test expects real URL.
skip-if = true # Intermittent failures - bug 987493. Restore the skip-if above once fixed

View File

@ -1,98 +0,0 @@
function frameScript()
{
Components.utils.import("resource://gre/modules/Services.jsm");
function eventHandler(e) {
if (!docShell) {
sendAsyncMessage("Test:Fail", "docShell is null");
}
sendAsyncMessage("Test:Event", [e.type, e.target === content.document, e.eventPhase]);
}
let outerID = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils).outerWindowID;
function onOuterWindowDestroyed(subject, topic, data) {
if (docShell) {
sendAsyncMessage("Test:Fail", "docShell is non-null");
}
let id = subject.QueryInterface(Components.interfaces.nsISupportsPRUint64).data;
sendAsyncMessage("Test:Event", ["outer-window-destroyed", id == outerID]);
if (id == outerID) {
Services.obs.removeObserver(onOuterWindowDestroyed, "outer-window-destroyed");
}
}
let url = "https://example.com/browser/dom/base/test/file_messagemanager_unload.html";
content.location = url;
addEventListener("load", (e) => {
if (e.target.location != url) {
return;
}
addEventListener("unload", eventHandler, false);
addEventListener("unload", eventHandler, true);
addEventListener("pagehide", eventHandler, false);
addEventListener("pagehide", eventHandler, true);
Services.obs.addObserver(onOuterWindowDestroyed, "outer-window-destroyed", false);
sendAsyncMessage("Test:Ready");
}, true);
}
const EXPECTED = [
// Unload events on the TabChildGlobal. These come first so that the
// docshell is available.
["unload", false, 2],
["unload", false, 2],
// pagehide and unload events for the top-level page.
["pagehide", true, 1],
["pagehide", true, 3],
["unload", true, 1],
// pagehide and unload events for the iframe.
["pagehide", false, 1],
["pagehide", false, 3],
["unload", false, 1],
// outer-window-destroyed for both pages.
["outer-window-destroyed", false],
["outer-window-destroyed", true],
];
function test() {
waitForExplicitFinish();
var newTab = gBrowser.addTab("about:blank");
gBrowser.selectedTab = newTab;
let browser = newTab.linkedBrowser;
browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")()", false);
browser.messageManager.addMessageListener("Test:Fail", (msg) => {
ok(false, msg.data);
}, true);
let index = 0;
browser.messageManager.addMessageListener("Test:Event", (msg) => {
ok(msg.target === browser, "<browser> is correct");
info(JSON.stringify(msg.data));
is(JSON.stringify(msg.data), JSON.stringify(EXPECTED[index]), "results match");
index++;
if (index == EXPECTED.length) {
finish();
}
}, true);
browser.messageManager.addMessageListener("Test:Ready", () => {
info("Got ready message");
gBrowser.removeCurrentTab();
});
}

View File

@ -1,17 +0,0 @@
function test() {
waitForExplicitFinish();
var tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
tab.linkedBrowser.addEventListener("load", function onload() {
tab.linkedBrowser.removeEventListener("load", onload);
tab.linkedBrowser.addEventListener("pagehide", function() {
ok(true, "got page hide event");
finish();
});
executeSoon(() => { gBrowser.removeTab(tab); });
}, true);
}

View File

@ -1,6 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<iframe id="frame" src="about:robots"></iframe>
</body>
</html>

View File

@ -2650,6 +2650,32 @@ TabChild::RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline)
return true;
}
class UnloadScriptEvent : public nsRunnable
{
public:
UnloadScriptEvent(TabChild* aTabChild, TabChildGlobal* aTabChildGlobal)
: mTabChild(aTabChild), mTabChildGlobal(aTabChildGlobal)
{ }
NS_IMETHOD Run()
{
nsCOMPtr<nsIDOMEvent> event;
NS_NewDOMEvent(getter_AddRefs(event), mTabChildGlobal, nullptr, nullptr);
if (event) {
event->InitEvent(NS_LITERAL_STRING("unload"), false, false);
event->SetTrusted(true);
bool dummy;
mTabChildGlobal->DispatchEvent(event, &dummy);
}
return NS_OK;
}
nsRefPtr<TabChild> mTabChild;
TabChildGlobal* mTabChildGlobal;
};
bool
TabChild::RecvDestroy()
{
@ -2657,10 +2683,10 @@ TabChild::RecvDestroy()
mDestroyed = true;
if (mTabChildGlobal) {
// Message handlers are called from the event loop, so it better be safe to
// run script.
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
mTabChildGlobal->DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
// Let the frame scripts know the child is being closed
nsContentUtils::AddScriptRunner(
new UnloadScriptEvent(this, mTabChildGlobal)
);
}
nsCOMPtr<nsIObserverService> observerService =

View File

@ -318,12 +318,6 @@ TabParent::RemoveTabParentFromTable(uint64_t aLayersId)
}
}
void
TabParent::CacheFrameLoader(nsFrameLoader* aFrameLoader)
{
mFrameLoader = aFrameLoader;
}
void
TabParent::SetOwnerElement(Element* aElement)
{
@ -413,13 +407,15 @@ TabParent::ActorDestroy(ActorDestroyReason why)
if (mIMETabParent == this) {
mIMETabParent = nullptr;
}
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
nsRefPtr<nsFrameMessageManager> fmm;
if (frameLoader) {
fmm = frameLoader->GetFrameMessageManager();
nsCOMPtr<Element> frameElement(mFrameElement);
ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr,
nullptr);
frameLoader->DestroyComplete();
frameLoader->DestroyChild();
if (why == AbnormalShutdown && os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader),
@ -428,13 +424,14 @@ TabParent::ActorDestroy(ActorDestroyReason why)
NS_LITERAL_STRING("oop-browser-crashed"),
true, true);
}
mFrameLoader = nullptr;
}
if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this), "ipc:browser-destroyed", nullptr);
}
if (fmm) {
fmm->Disconnect();
}
}
bool
@ -2288,7 +2285,7 @@ TabParent::ReceiveMessage(const nsString& aMessage,
nsIPrincipal* aPrincipal,
InfallibleTArray<nsString>* aJSONRetVal)
{
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
if (frameLoader && frameLoader->GetFrameMessageManager()) {
nsRefPtr<nsFrameMessageManager> manager =
frameLoader->GetFrameMessageManager();
@ -2406,16 +2403,8 @@ TabParent::AllowContentIME()
}
already_AddRefed<nsFrameLoader>
TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
TabParent::GetFrameLoader() const
{
if (mIsDestroyed && !aUseCachedFrameLoaderAfterDestroy) {
return nullptr;
}
if (mFrameLoader) {
nsRefPtr<nsFrameLoader> fl = mFrameLoader;
return fl.forget();
}
nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(mFrameElement);
return frameLoaderOwner ? frameLoaderOwner->GetFrameLoader() : nullptr;
}

View File

@ -80,8 +80,6 @@ public:
Element* GetOwnerElement() const { return mFrameElement; }
void SetOwnerElement(Element* aElement);
void CacheFrameLoader(nsFrameLoader* aFrameLoader);
/**
* Get the mozapptype attribute from this TabParent's owner DOM element.
*/
@ -454,7 +452,7 @@ protected:
bool mUpdatedDimensions;
private:
already_AddRefed<nsFrameLoader> GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;
already_AddRefed<nsFrameLoader> GetFrameLoader() const;
layout::RenderFrameParent* GetRenderFrame();
nsRefPtr<nsIContentParent> mManager;
void TryCacheDPIAndScale();
@ -496,11 +494,6 @@ private:
nsCOMPtr<nsILoadContext> mLoadContext;
// We keep a strong reference to the frameloader after we've sent the
// Destroy message and before we've received __delete__. This allows us to
// dispatch message manager messages during this time.
nsRefPtr<nsFrameLoader> mFrameLoader;
TabId mTabId;
// Helper class for RecvCreateWindow.

View File

@ -48,42 +48,6 @@ function b2gStart() {
webNav.loadURI(url, null, null, null, null);
}
let TabDestroyObserver = {
outstanding: new Set(),
promiseResolver: null,
init: function() {
Services.obs.addObserver(this, "message-manager-close", false);
Services.obs.addObserver(this, "message-manager-disconnect", false);
},
destroy: function() {
Services.obs.removeObserver(this, "message-manager-close");
Services.obs.removeObserver(this, "message-manager-disconnect");
},
observe: function(subject, topic, data) {
if (topic == "message-manager-close") {
this.outstanding.add(subject);
} else if (topic == "message-manager-disconnect") {
this.outstanding.delete(subject);
if (!this.outstanding.size && this.promiseResolver) {
this.promiseResolver();
}
}
},
wait: function() {
if (!this.outstanding.size) {
return Promise.resolve();
}
return new Promise((resolve) => {
this.promiseResolver = resolve;
});
},
};
function testInit() {
gConfig = readConfig();
if (gConfig.testRoot == "browser" ||
@ -221,8 +185,6 @@ Tester.prototype = {
},
start: function Tester_start() {
TabDestroyObserver.init();
//if testOnLoad was not called, then gConfig is not defined
if (!gConfig)
gConfig = readConfig();
@ -312,8 +274,6 @@ Tester.prototype = {
},
finish: function Tester_finish(aSkipSummary) {
TabDestroyObserver.destroy();
this.Promise.Debugging.flushUncaughtErrors();
var passCount = this.tests.reduce(function(a, f) a + f.passCount, 0);
@ -623,9 +583,6 @@ Tester.prototype = {
Services.obs.notifyObservers({wrappedJSObject: barrier},
"shutdown-leaks-before-check", null);
barrier.client.addBlocker("ShutdownLeaks: Wait for tabs to finish closing",
TabDestroyObserver.wait());
barrier.wait().then(() => {
// Simulate memory pressure so that we're forced to free more resources
// and thus get rid of more false leaks like already terminated workers.

View File

@ -713,9 +713,9 @@ var DebuggerServer = {
prefix: prefix
});
function onClose() {
Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
events.off(aConnection, "closed", onClose);
function onDisconnect() {
Services.obs.removeObserver(onMessageManagerDisconnect, "message-manager-disconnect");
events.off(aConnection, "closed", onDisconnect);
if (childTransport) {
// If we have a child transport, the actor has already
// been created. We need to stop using this message manager.
@ -730,16 +730,16 @@ var DebuggerServer = {
}
}
let onMessageManagerClose = DevToolsUtils.makeInfallible(function (subject, topic, data) {
let onMessageManagerDisconnect = DevToolsUtils.makeInfallible(function (subject, topic, data) {
if (subject == aMm) {
onClose();
onDisconnect();
aConnection.send({ from: actor.actor, type: "tabDetached" });
}
}).bind(this);
Services.obs.addObserver(onMessageManagerClose,
"message-manager-close", false);
Services.obs.addObserver(onMessageManagerDisconnect,
"message-manager-disconnect", false);
events.on(aConnection, "closed", onClose);
events.on(aConnection, "closed", onDisconnect);
return deferred.promise;
},
@ -882,9 +882,9 @@ var DebuggerServer = {
}).bind(this);
mm.addMessageListener("debug:actor", onActorCreated);
let onMessageManagerClose = DevToolsUtils.makeInfallible(function (subject, topic, data) {
let onMessageManagerDisconnect = DevToolsUtils.makeInfallible(function (subject, topic, data) {
if (subject == mm) {
Services.obs.removeObserver(onMessageManagerClose, topic);
Services.obs.removeObserver(onMessageManagerDisconnect, topic);
// provides hook to actor modules that need to exchange messages
// between e10s parent and child processes
@ -925,8 +925,8 @@ var DebuggerServer = {
}
}
}).bind(this);
Services.obs.addObserver(onMessageManagerClose,
"message-manager-close", false);
Services.obs.addObserver(onMessageManagerDisconnect,
"message-manager-disconnect", false);
events.once(aConnection, "closed", () => {
if (childTransport) {