Bug 1191491 - Do not dispatch an audio-playback notification when swapping browsers; r=smaug

We send a pagehide event during swapping docshell frame loaders, and
before this patch we would not be able to differentiate this event with
the one that we send when navigating away from a page, so we would
incorrectly dispatch an audio-playback notification indicating that
audio playback has stopped.  This patch adds a flag to the root docshell
when the frame loader swapping is in progress and disables the above
behavior when that flag is set.
This commit is contained in:
Ehsan Akhgari
2015-08-06 10:03:24 -04:00
parent e79a496046
commit 9e1ae0a6f8
6 changed files with 68 additions and 6 deletions

View File

@@ -115,12 +115,28 @@ function* test_swapped_browser(oldTab, newBrowser, isPlaying) {
return (event.detail.changed.indexOf("soundplaying") >= 0 || !isPlaying) &&
event.detail.changed.indexOf("muted") >= 0;
});
let AudioPlaybackPromise = new Promise(resolve => {
let observer = (subject, topic, data) => {
ok(false, "Should not see an audio-playback notification");
};
Services.obs.addObserver(observer, "audio-playback", false);
setTimeout(() => {
Services.obs.removeObserver(observer, "audio-playback");
resolve();
}, 100);
});
gBrowser.swapBrowsersAndCloseOther(newTab, oldTab);
yield AttrChangePromise;
ok(newTab.hasAttribute("muted"), "Expected the correct muted attribute on the new tab");
is(newTab.hasAttribute("soundplaying"), isPlaying, "Expected the correct soundplaying attribute on the new tab");
// Wait to see if an audio-playback event is dispatched. This should not happen!
yield AudioPlaybackPromise;
ok(newTab.hasAttribute("muted"), "Expected the correct muted attribute on the new tab");
is(newTab.hasAttribute("soundplaying"), isPlaying, "Expected the correct soundplaying attribute on the new tab");
}
function* test_browser_swapping(tab, browser) {

View File

@@ -900,6 +900,7 @@ nsDocShell::nsDocShell()
, mUseRemoteTabs(false)
, mDeviceSizeIsPageSize(false)
, mWindowDraggingAllowed(false)
, mInFrameSwap(false)
, mCanExecuteScripts(false)
, mFiredUnloadEvent(false)
, mEODForCurrentDocument(false)
@@ -14074,3 +14075,16 @@ nsDocShell::GetPaymentRequestId(nsAString& aPaymentRequestId)
aPaymentRequestId = GetInheritedPaymentRequestId();
return NS_OK;
}
bool
nsDocShell::InFrameSwap()
{
nsRefPtr<nsDocShell> shell = this;
do {
if (shell->mInFrameSwap) {
return true;
}
shell = shell->GetParentDocshell();
} while (shell);
return false;
}

View File

@@ -258,6 +258,12 @@ public:
// is no longer applied
void NotifyAsyncPanZoomStopped();
void SetInFrameSwap(bool aInSwap)
{
mInFrameSwap = aInSwap;
}
bool InFrameSwap();
private:
// An observed docshell wrapper is created when recording markers is enabled.
mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
@@ -900,6 +906,7 @@ protected:
bool mUseRemoteTabs;
bool mDeviceSizeIsPageSize;
bool mWindowDraggingAllowed;
bool mInFrameSwap;
// Because scriptability depends on the mAllowJavascript values of our
// ancestors, we cache the effective scriptability and recompute it when

View File

@@ -987,8 +987,8 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
return NS_ERROR_DOM_SECURITY_ERR;
}
nsCOMPtr<nsIDocShell> ourDocshell = GetExistingDocShell();
nsCOMPtr<nsIDocShell> otherDocshell = aOther->GetExistingDocShell();
nsRefPtr<nsDocShell> ourDocshell = static_cast<nsDocShell*>(GetExistingDocShell());
nsRefPtr<nsDocShell> otherDocshell = static_cast<nsDocShell*>(aOther->GetExistingDocShell());
if (!ourDocshell || !otherDocshell) {
// How odd
return NS_ERROR_NOT_IMPLEMENTED;
@@ -1120,6 +1120,8 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
return NS_ERROR_NOT_IMPLEMENTED;
}
mInSwap = aOther->mInSwap = true;
ourDocshell->SetInFrameSwap(true);
otherDocshell->SetInFrameSwap(true);
// Fire pageshow events on still-loading pages, and then fire pagehide
// events. Note that we do NOT fire these in the normal way, but just fire
@@ -1132,26 +1134,32 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
if (!ourFrame || !otherFrame) {
mInSwap = aOther->mInSwap = false;
nsContentUtils::FirePageShowEvent(ourDocshell, ourEventTarget, true);
nsContentUtils::FirePageShowEvent(otherDocshell, otherEventTarget, true);
mInSwap = aOther->mInSwap = false;
ourDocshell->SetInFrameSwap(false);
otherDocshell->SetInFrameSwap(false);
return NS_ERROR_NOT_IMPLEMENTED;
}
nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
if (!ourFrameFrame) {
mInSwap = aOther->mInSwap = false;
nsContentUtils::FirePageShowEvent(ourDocshell, ourEventTarget, true);
nsContentUtils::FirePageShowEvent(otherDocshell, otherEventTarget, true);
mInSwap = aOther->mInSwap = false;
ourDocshell->SetInFrameSwap(false);
otherDocshell->SetInFrameSwap(false);
return NS_ERROR_NOT_IMPLEMENTED;
}
// OK. First begin to swap the docshells in the two nsIFrames
rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
if (NS_FAILED(rv)) {
mInSwap = aOther->mInSwap = false;
nsContentUtils::FirePageShowEvent(ourDocshell, ourEventTarget, true);
nsContentUtils::FirePageShowEvent(otherDocshell, otherEventTarget, true);
mInSwap = aOther->mInSwap = false;
ourDocshell->SetInFrameSwap(false);
otherDocshell->SetInFrameSwap(false);
return rv;
}
@@ -1258,6 +1266,8 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
nsContentUtils::FirePageShowEvent(otherDocshell, otherEventTarget, true);
mInSwap = aOther->mInSwap = false;
ourDocshell->SetInFrameSwap(false);
otherDocshell->SetInFrameSwap(false);
return NS_OK;
}

View File

@@ -103,6 +103,7 @@ static PRLogModuleInfo* gMediaElementEventsLog;
#include "nsIPermissionManager.h"
#include "nsContentTypeParser.h"
#include "nsDocShell.h"
#include "mozilla/EventStateManager.h"
@@ -4013,7 +4014,14 @@ void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
if (pauseElement && mAudioChannelAgent) {
// If the element is being paused since we are navigating away from the
// document, notify the audio channel agent.
NotifyAudioChannelAgent(false);
// Be careful to ignore this event during a docshell frame swap.
auto docShell = static_cast<nsDocShell*>(OwnerDoc()->GetDocShell());
if (!docShell) {
return;
}
if (!docShell->InFrameSwap()) {
NotifyAudioChannelAgent(false);
}
}
}

View File

@@ -2392,11 +2392,18 @@ TabChild::RecvSwappedWithOtherRemoteLoader()
return true;
}
nsRefPtr<nsDocShell> docShell = static_cast<nsDocShell*>(ourDocShell.get());
nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
docShell->SetInFrameSwap(true);
nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, false);
nsContentUtils::FirePageHideEvent(ourDocShell, ourEventTarget);
nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, true);
docShell->SetInFrameSwap(false);
return true;
}