Merge autoland to mozilla-central. a=merge

This commit is contained in:
Csoregi Natalia 2020-11-19 11:56:07 +02:00
Родитель 511fc14c29 4d3ab47735
Коммит 1d34bd022d
261 изменённых файлов: 2884 добавлений и 10114 удалений

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

@ -80,7 +80,7 @@ rev = "477d8fc53a64705a9d3fbcce9de92f4988558525"
[source."https://github.com/ChunMinChang/cubeb-coreaudio-rs"]
git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs"
replace-with = "vendored-sources"
rev = "87667052a5ffaf47fbeac4ae67f1ecdefa8d7b09"
rev = "1e1222d6213e28eabd7f4302d00765ae1153a31e"
[source.crates-io]
replace-with = "vendored-sources"

4
Cargo.lock сгенерированный
Просмотреть файл

@ -764,7 +764,7 @@ dependencies = [
[[package]]
name = "coreaudio-sys-utils"
version = "0.1.0"
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=87667052a5ffaf47fbeac4ae67f1ecdefa8d7b09#87667052a5ffaf47fbeac4ae67f1ecdefa8d7b09"
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=1e1222d6213e28eabd7f4302d00765ae1153a31e#1e1222d6213e28eabd7f4302d00765ae1153a31e"
dependencies = [
"core-foundation-sys",
"coreaudio-sys",
@ -1013,7 +1013,7 @@ dependencies = [
[[package]]
name = "cubeb-coreaudio"
version = "0.1.0"
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=87667052a5ffaf47fbeac4ae67f1ecdefa8d7b09#87667052a5ffaf47fbeac4ae67f1ecdefa8d7b09"
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=1e1222d6213e28eabd7f4302d00765ae1153a31e#1e1222d6213e28eabd7f4302d00765ae1153a31e"
dependencies = [
"atomic",
"audio-mixer",

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

@ -22,9 +22,6 @@
// CSS display
testCSSAttrs("display_mozbox");
testCSSAttrs("display_mozinlinebox");
testCSSAttrs("display_mozgrid");
testCSSAttrs("display_mozgridgroup");
testCSSAttrs("display_mozgridline");
testCSSAttrs("display_mozdeck");
testCSSAttrs("display_mozpopup");
@ -53,9 +50,6 @@
<vbox id="display_mozbox" style="display: -moz-box;" role="img"/>
<vbox id="display_mozinlinebox" style="display: -moz-inline-box;" role="img"/>
<vbox id="display_mozgrid" style="display: -moz-grid;" role="img"/>
<vbox id="display_mozgridgroup" style="display: -moz-grid-group;" role="img"/>
<vbox id="display_mozgridline" style="display: -moz-grid-line;" role="img"/>
<vbox id="display_mozdeck" style="display: -moz-deck;" role="img"/>
<vbox id="display_mozpopup" style="display: -moz-popup;" role="img"/>

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

@ -1884,12 +1884,7 @@ pref("dom.ipc.processPrelaunch.enabled", true);
// See comments in bug 1340115 on how we got to these numbers.
pref("browser.migrate.chrome.history.limit", 2000);
pref("browser.migrate.chrome.history.maxAgeInDays", 180);
#ifdef EARLY_BETA_OR_EARLIER
pref("browser.migrate.showBookmarksToolbarAfterMigration", true);
#else
pref("browser.migrate.showBookmarksToolbarAfterMigration", false);
#endif
pref("extensions.pocket.api", "api.getpocket.com");
pref("extensions.pocket.enabled", true);

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

@ -360,6 +360,7 @@ export class _DiscoveryStreamBase extends React.PureComponent {
privacyNoticeURL={topStories.privacyNoticeURL}
showPrefName={topStories.pref.feed}
title={message.header.title}
eventSource="CARDGRID"
>
{this.renderLayout(layoutRender)}
</CollapsibleSection>

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

@ -3916,7 +3916,8 @@ class _DiscoveryStreamBase extends react__WEBPACK_IMPORTED_MODULE_14___default.a
},
privacyNoticeURL: topStories.privacyNoticeURL,
showPrefName: topStories.pref.feed,
title: message.header.title
title: message.header.title,
eventSource: "CARDGRID"
}, this.renderLayout(layoutRender)), this.renderLayout([{
width: 12,
components: [{

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

@ -277,6 +277,13 @@ describe("<DiscoveryStreamBase>", () => {
.type(),
CollapsibleSection
);
assert.equal(
wrapper
.children()
.at(0)
.props().eventSource,
"CARDGRID"
);
});
it("should render a Message component", () => {

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

@ -1024,7 +1024,9 @@ var SessionStoreInternal = {
},
OnHistoryNewEntry(newURI, oldIndex) {
this.notifySHistoryChanges(oldIndex);
// We use oldIndex - 1 to collect the current entry as well. This makes sure to
// collect any changes that were made to the entry while the document was active.
this.notifySHistoryChanges(oldIndex == -1 ? oldIndex : oldIndex - 1);
},
OnHistoryGotoIndex() {

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

@ -17,6 +17,10 @@ add_task(async function() {
// Forces the Browser Toolbox to open on the inspector by default
await pushPref("devtools.browsertoolbox.panel", "inspector");
// Force the modal print preview, otherwise the printPreview command will not
// open the expected UI.
await pushPref("print.tab_modal.enabled", true);
// Open the tab *after* opening the Browser Toolbox in order to force creating the remote frames
// late and exercise frame target watching code.
await addTab(`data:text/html,<div id="test-div">PRINT PREVIEW TEST</div>`);

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

@ -664,6 +664,11 @@ void BrowsingContext::Attach(bool aFromIPC, ContentParent* aOriginProcess) {
Canonical()->mWebProgress = new BrowsingContextWebProgress();
}
}
if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
obs->NotifyWhenScriptSafe(ToSupports(this), "browsing-context-attached",
nullptr);
}
}
void BrowsingContext::Detach(bool aFromIPC) {
@ -2860,15 +2865,17 @@ void BrowsingContext::RemoveFromSessionHistory() {
}
void BrowsingContext::HistoryGo(int32_t aOffset, uint64_t aHistoryEpoch,
bool aRequireUserInteraction,
std::function<void(int32_t&&)>&& aResolver) {
if (XRE_IsContentProcess()) {
ContentChild::GetSingleton()->SendHistoryGo(
this, aOffset, aHistoryEpoch, std::move(aResolver),
this, aOffset, aHistoryEpoch, aRequireUserInteraction,
std::move(aResolver),
[](mozilla::ipc::
ResponseRejectReason) { /* FIXME Is ignoring this fine? */ });
} else {
Canonical()->HistoryGo(
aOffset, aHistoryEpoch,
aOffset, aHistoryEpoch, aRequireUserInteraction,
Canonical()->GetContentParent()
? Some(Canonical()->GetContentParent()->ChildID())
: Nothing(),

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

@ -733,6 +733,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
GetTriggeringAndInheritPrincipalsForCurrentLoad();
void HistoryGo(int32_t aOffset, uint64_t aHistoryEpoch,
bool aRequireUserInteraction,
std::function<void(int32_t&&)>&& aResolver);
bool ShouldUpdateSessionHistory(uint32_t aLoadType);

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

@ -535,6 +535,8 @@ void CanonicalBrowsingContext::SessionHistoryCommit(uint64_t aLoadId,
}
}
ResetSHEntryHasUserInteractionCache();
HistoryCommitIndexAndLength(aChangeID, caller);
return;
@ -650,6 +652,9 @@ void CanonicalBrowsingContext::SetActiveSessionHistoryEntry(
UseRemoteSubframes());
}
}
ResetSHEntryHasUserInteractionCache();
// FIXME Need to do the equivalent of EvictContentViewersOrReplaceEntry.
HistoryCommitIndexAndLength(aChangeID, caller);
}
@ -667,6 +672,9 @@ void CanonicalBrowsingContext::ReplaceActiveSessionHistoryEntry(
shistory->NotifyOnHistoryReplaceEntry();
shistory->UpdateRootBrowsingContextState();
}
ResetSHEntryHasUserInteractionCache();
// FIXME Need to do the equivalent of EvictContentViewersOrReplaceEntry.
}
@ -702,8 +710,15 @@ void CanonicalBrowsingContext::RemoveFromSessionHistory() {
}
void CanonicalBrowsingContext::HistoryGo(
int32_t aOffset, uint64_t aHistoryEpoch, Maybe<ContentParentId> aContentId,
int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction,
Maybe<ContentParentId> aContentId,
std::function<void(int32_t&&)>&& aResolver) {
if (aRequireUserInteraction && aOffset != -1 && aOffset != 1) {
NS_ERROR(
"aRequireUserInteraction may only be used with an offset of -1 or 1");
return;
}
nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
if (!shistory) {
return;
@ -716,13 +731,25 @@ void CanonicalBrowsingContext::HistoryGo(
("HistoryGo(%d->%d) epoch %" PRIu64 "/id %" PRIu64, aOffset,
(index + aOffset).value(), aHistoryEpoch,
(uint64_t)(aContentId.isSome() ? aContentId.value() : 0)));
index += aOffset;
if (!index.isValid()) {
MOZ_LOG(gSHLog, LogLevel::Debug, ("Invalid index"));
return;
}
// FIXME userinteraction bits may needs tweaks here.
while (true) {
index += aOffset;
if (!index.isValid()) {
MOZ_LOG(gSHLog, LogLevel::Debug, ("Invalid index"));
return;
}
// Check for user interaction if desired, except for the first and last
// history entries. We compare with >= to account for the case where
// aOffset >= length.
if (!aRequireUserInteraction || index.value() >= shistory->Length() - 1 ||
index.value() <= 0) {
break;
}
if (shistory->HasUserInteractionAtIndex(index.value())) {
break;
}
}
// Implement aborting additional history navigations from within the same
// event spin of the content process.
@ -1539,6 +1566,13 @@ void CanonicalBrowsingContext::EndDocumentLoad(bool aForProcessSwitch) {
}
}
void CanonicalBrowsingContext::ResetSHEntryHasUserInteractionCache() {
WindowContext* topWc = GetTopWindowContext();
if (topWc && !topWc->IsDiscarded()) {
MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
}
}
void CanonicalBrowsingContext::HistoryCommitIndexAndLength() {
nsID changeID = {};
CallerWillNotifyHistoryIndexAndLengthChanges caller(nullptr);

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

@ -140,6 +140,7 @@ class CanonicalBrowsingContext final : public BrowsingContext {
void RemoveFromSessionHistory();
void HistoryGo(int32_t aIndex, uint64_t aHistoryEpoch,
bool aRequireUserInteraction,
Maybe<ContentParentId> aContentId,
std::function<void(int32_t&&)>&& aResolver);
@ -324,6 +325,12 @@ class CanonicalBrowsingContext final : public BrowsingContext {
uint64_t mCrossGroupOpenerId = 0;
// This function will make the top window context reset its
// "SHEntryHasUserInteraction" cache that prevents documents from repeatedly
// setting user interaction on SH entries. Should be called anytime SH
// entries are added or replaced.
void ResetSHEntryHasUserInteractionCache();
// The current remoteness change which is in a pending state.
RefPtr<PendingRemotenessChange> mPendingRemotenessChange;

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

@ -242,6 +242,22 @@ bool WindowContext::CanSet(
return CheckOnlyOwningProcessCanSet(aSource);
}
void WindowContext::DidSet(FieldIndex<IDX_SHEntryHasUserInteraction>,
bool aOldValue) {
MOZ_ASSERT(
TopWindowContext() == this,
"SHEntryHasUserInteraction can only be set on the top window context");
// This field is set when the child notifies us of new user interaction, so we
// also set the currently active shentry in the parent as having interaction.
if (XRE_IsParentProcess() && mBrowsingContext) {
SessionHistoryEntry* activeEntry =
mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
if (activeEntry && GetSHEntryHasUserInteraction()) {
activeEntry->SetHasUserInteraction(true);
}
}
}
void WindowContext::DidSet(FieldIndex<IDX_UserActivationState>) {
MOZ_ASSERT_IF(!mInProcess, mUserGestureStart.IsNull());
USER_ACTIVATION_LOG("Set user gesture activation %" PRIu8

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

@ -251,6 +251,8 @@ class WindowContext : public nsISupports, public nsWrapperCache {
void DidSet(FieldIndex<IDX_HasReportedShadowDOMUsage>, bool aOldValue);
void DidSet(FieldIndex<IDX_SHEntryHasUserInteraction>, bool aOldValue);
// Overload `DidSet` to get notifications for a particular field being set.
//
// You can also overload the variant that gets the old value if you need it.

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

@ -244,10 +244,6 @@
# include "nsIWebBrowserPrint.h"
#endif
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::net;
@ -3331,7 +3327,7 @@ nsDocShell::GotoIndex(int32_t aIndex) {
NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
ErrorResult rv;
rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), rv);
rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), false, rv);
return rv.StealNSResult();
}

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

@ -154,17 +154,6 @@ void ChildSHistory::Go(int32_t aOffset, bool aRequireUserInteraction,
return;
}
// See Bug 1650095.
if (mozilla::SessionHistoryInParent() && !mPendingEpoch) {
mPendingEpoch = true;
RefPtr<ChildSHistory> self(this);
NS_DispatchToCurrentThread(
NS_NewRunnableFunction("UpdateEpochRunnable", [self] {
self->mHistoryEpoch++;
self->mPendingEpoch = false;
}));
}
// Check for user interaction if desired, except for the first and last
// history entries. We compare with >= to account for the case where
// aOffset >= Count().
@ -177,7 +166,17 @@ void ChildSHistory::Go(int32_t aOffset, bool aRequireUserInteraction,
}
}
GotoIndex(index.value(), aOffset, aRv);
if (mozilla::SessionHistoryInParent() && !mPendingEpoch) {
mPendingEpoch = true;
RefPtr<ChildSHistory> self(this);
NS_DispatchToCurrentThread(
NS_NewRunnableFunction("UpdateEpochRunnable", [self] {
self->mHistoryEpoch++;
self->mPendingEpoch = false;
}));
}
GotoIndex(index.value(), aOffset, aRequireUserInteraction, aRv);
}
void ChildSHistory::AsyncGo(int32_t aOffset, bool aRequireUserInteraction,
@ -200,14 +199,15 @@ void ChildSHistory::AsyncGo(int32_t aOffset, bool aRequireUserInteraction,
}
void ChildSHistory::GotoIndex(int32_t aIndex, int32_t aOffset,
ErrorResult& aRv) {
bool aRequireUserInteraction, ErrorResult& aRv) {
MOZ_LOG(gSHLog, LogLevel::Debug,
("ChildSHistory::GotoIndex(%d, %d), epoch %" PRIu64, aIndex, aOffset,
mHistoryEpoch));
if (mozilla::SessionHistoryInParent()) {
nsCOMPtr<nsISHistory> shistory = mHistory;
mBrowsingContext->HistoryGo(
aOffset, mHistoryEpoch, [shistory](int32_t&& aRequestedIndex) {
aOffset, mHistoryEpoch, aRequireUserInteraction,
[shistory](int32_t&& aRequestedIndex) {
// FIXME Should probably only do this for non-fission.
if (shistory) {
shistory->InternalSetRequestedIndex(aRequestedIndex);

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

@ -71,7 +71,8 @@ class ChildSHistory : public nsISupports, public nsWrapperCache {
CallerType aCallerType, ErrorResult& aRv);
// aIndex is the new index, and aOffset is the offset between new and current.
void GotoIndex(int32_t aIndex, int32_t aOffset, ErrorResult& aRv);
void GotoIndex(int32_t aIndex, int32_t aOffset, bool aRequireUserInteraction,
ErrorResult& aRv);
void RemovePendingHistoryNavigations();

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

@ -31,6 +31,7 @@ SessionHistoryInfo::SessionHistoryInfo(nsDocShellLoadState* aLoadState,
mLoadReplace(aLoadState->LoadReplace()),
/* FIXME Should this be aLoadState->IsSrcdocLoad()? */
mIsSrcdocEntry(!aLoadState->SrcdocData().IsEmpty()),
mHasUserInteraction(false),
mSharedState(SharedState::Create(
aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(),
aLoadState->PartitionedPrincipalToInherit(), aLoadState->Csp(),
@ -129,6 +130,7 @@ void SessionHistoryInfo::Reset(nsIURI* aURI, const nsID& aDocShellID,
mIsSrcdocEntry = false;
mScrollRestorationIsManual = false;
mPersist = false;
mHasUserInteraction = false;
mSharedState.Get()->mTriggeringPrincipal = aTriggeringPrincipal;
mSharedState.Get()->mPrincipalToInherit = aPrincipalToInherit;
@ -503,14 +505,27 @@ SessionHistoryEntry::SetIsSubFrame(bool aIsSubFrame) {
NS_IMETHODIMP
SessionHistoryEntry::GetHasUserInteraction(bool* aFlag) {
NS_WARNING("Not implemented in the parent process!");
*aFlag = true;
// The back button and menulist deal with root/top-level
// session history entries, thus we annotate only the root entry.
if (!mParent) {
*aFlag = mInfo->mHasUserInteraction;
} else {
nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(this);
root->GetHasUserInteraction(aFlag);
}
return NS_OK;
}
NS_IMETHODIMP
SessionHistoryEntry::SetHasUserInteraction(bool aFlag) {
NS_WARNING("Not implemented in the parent process!");
// The back button and menulist deal with root/top-level
// session history entries, thus we annotate only the root entry.
if (!mParent) {
mInfo->mHasUserInteraction = aFlag;
} else {
nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(this);
root->SetHasUserInteraction(aFlag);
}
return NS_OK;
}
@ -957,7 +972,7 @@ SessionHistoryEntry::Clone(nsISHEntry** aEntry) {
entry->mInfo->mScrollPositionY = 0;
entry->mInfo->mScrollRestorationIsManual = false;
// entry->mInfo->mHasUserInteraction = false;
entry->mInfo->mHasUserInteraction = false;
entry.forget(aEntry);
@ -1364,6 +1379,7 @@ void IPDLParamTraits<dom::SessionHistoryInfo>::Write(
WriteIPDLParam(aMsg, aActor, aParam.mIsSrcdocEntry);
WriteIPDLParam(aMsg, aActor, aParam.mScrollRestorationIsManual);
WriteIPDLParam(aMsg, aActor, aParam.mPersist);
WriteIPDLParam(aMsg, aActor, aParam.mHasUserInteraction);
WriteIPDLParam(aMsg, aActor, aParam.mSharedState.Get()->mId);
WriteIPDLParam(aMsg, aActor, aParam.mSharedState.Get()->mTriggeringPrincipal);
WriteIPDLParam(aMsg, aActor, aParam.mSharedState.Get()->mPrincipalToInherit);
@ -1400,6 +1416,7 @@ bool IPDLParamTraits<dom::SessionHistoryInfo>::Read(
!ReadIPDLParam(aMsg, aIter, aActor,
&aResult->mScrollRestorationIsManual) ||
!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mPersist) ||
!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mHasUserInteraction) ||
!ReadIPDLParam(aMsg, aIter, aActor, &sharedId)) {
aActor->FatalError("Error reading fields for SessionHistoryInfo");
return false;

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

@ -101,6 +101,11 @@ class SessionHistoryInfo {
}
bool GetURIWasModified() const { return mURIWasModified; }
void SetHasUserInteraction(bool aHasUserInteraction) {
mHasUserInteraction = aHasUserInteraction;
}
bool GetHasUserInteraction() const { return mHasUserInteraction; }
uint64_t SharedId() const;
nsILayoutHistoryState* GetLayoutHistoryState();
@ -152,6 +157,7 @@ class SessionHistoryInfo {
bool mIsSrcdocEntry = false;
bool mScrollRestorationIsManual = false;
bool mPersist = true;
bool mHasUserInteraction = false;
union SharedState {
SharedState();

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

@ -300,13 +300,14 @@ nsSHEntry::SetIsSubFrame(bool aFlag) {
NS_IMETHODIMP
nsSHEntry::GetHasUserInteraction(bool* aFlag) {
// We can't assert that this getter isn't accessed only on root
// entries because there's JS code that will iterate over entries
// for serialization etc., so let's assert the next best thing.
MOZ_ASSERT(!mParent || !mHasUserInteraction,
"User interaction can only be set on root entries");
*aFlag = mHasUserInteraction;
// The back button and menulist deal with root/top-level
// session history entries, thus we annotate only the root entry.
if (!mParent) {
*aFlag = mHasUserInteraction;
} else {
nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(this);
root->GetHasUserInteraction(aFlag);
}
return NS_OK;
}

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

@ -72,9 +72,7 @@ support-files =
[browser_backforward_userinteraction.js]
support-files =
dummy_iframe_page.html
skip-if = fission # bug 1666515
[browser_backforward_userinteraction_about.js]
skip-if = fission
[browser_bug1543077-1.js]
[browser_bug1543077-2.js]
[browser_bug1543077-3.js]
@ -167,6 +165,7 @@ support-files =
skip-if = !e10s # e10s specific test.
[browser_tab_replace_while_loading.js]
skip-if = (os == 'linux' && bits == 64 && os_version == '18.04') || (os == "win") # Bug 1604237, Bug 1671794
[browser_browsing_context_attached.js]
[browser_browsing_context_discarded.js]
[browser_fall_back_to_https.js]
skip-if = (os == 'mac')

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

@ -0,0 +1,139 @@
"use strict";
const TEST_PATH =
getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"http://example.com"
) + "dummy_page.html";
const TOPIC = "browsing-context-attached";
async function observeAttached(callback) {
let attached = [];
function observer(subject, topic) {
is(topic, TOPIC, "observing correct topic");
ok(subject instanceof BrowsingContext, "subject to be a BrowsingContext");
info(`*** bc id: ${subject.id}`);
attached.push(subject);
}
Services.obs.addObserver(observer, TOPIC);
try {
await callback();
return attached;
} finally {
Services.obs.removeObserver(observer, TOPIC);
}
}
add_task(async function toplevelForNewWindow() {
let win;
let attached = await observeAttached(async () => {
win = await BrowserTestUtils.openNewBrowserWindow();
});
ok(
attached.includes(win.browsingContext),
"got notification for window's chrome browsing context"
);
ok(
attached.includes(win.gBrowser.selectedBrowser.browsingContext),
"got notification for toplevel browsing context"
);
await BrowserTestUtils.closeWindow(win);
});
add_task(async function toplevelForNewTab() {
let tab;
let attached = await observeAttached(async () => {
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
});
ok(
!attached.includes(window.browsingContext),
"no notification for the current window's chrome browsing context"
);
ok(
attached.includes(tab.linkedBrowser.browsingContext),
"got notification for toplevel browsing context"
);
BrowserTestUtils.removeTab(tab);
});
add_task(async function subframe() {
let browsingContext;
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
let attached = await observeAttached(async () => {
browsingContext = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
let iframe = content.document.createElement("iframe");
content.document.body.appendChild(iframe);
iframe.contentWindow.location = "https://example.com/";
return iframe.browsingContext;
});
});
ok(
!attached.includes(window.browsingContext),
"no notification for the current window's chrome browsing context"
);
ok(
!attached.includes(tab.linkedBrowser.browsingContext),
"no notification for toplevel browsing context"
);
ok(
attached.includes(browsingContext),
"got notification for frame's browsing context"
);
BrowserTestUtils.removeTab(tab);
});
add_task(async function toplevelReplacedBy() {
let tab;
let attached = await observeAttached(async () => {
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
});
const firstContext = tab.linkedBrowser.browsingContext;
ok(
attached.includes(firstContext),
"got notification for initial toplevel browsing context"
);
attached = await observeAttached(async () => {
await loadURI(TEST_PATH);
});
const secondContext = tab.linkedBrowser.browsingContext;
ok(
attached.includes(secondContext),
"got notification for replaced toplevel browsing context"
);
isnot(secondContext, firstContext, "browsing context to be replaced");
is(
secondContext.browserId,
firstContext.browserId,
"browserId has been kept"
);
attached = await observeAttached(async () => {
await loadURI("about:robots");
});
const thirdContext = tab.linkedBrowser.browsingContext;
ok(
attached.includes(thirdContext),
"got notification for replaced toplevel browsing context"
);
isnot(thirdContext, secondContext, "browsing context to be replaced");
is(
thirdContext.browserId,
secondContext.browserId,
"browserId has been kept"
);
BrowserTestUtils.removeTab(tab);
});

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

@ -2,35 +2,64 @@
const TOPIC = "browsing-context-discarded";
add_task(async function toplevel() {
let win = await BrowserTestUtils.openNewBrowserWindow();
let bc = await SpecialPowers.spawn(
win.gBrowser.selectedBrowser,
[],
() => content.docShell.browsingContext
);
async function observeDiscarded(browsingContexts, callback) {
let discarded = [];
let promise = BrowserUtils.promiseObserved(TOPIC, subject => subject === bc);
await BrowserTestUtils.closeWindow(win);
let promise = BrowserUtils.promiseObserved(TOPIC, subject => {
ok(subject instanceof BrowsingContext, "subject to be a BrowsingContext");
discarded.push(subject);
return browsingContexts.every(item => discarded.includes(item));
});
await callback();
await promise;
// If we make it here, then we've received the notification.
ok(true, "got top-level notification");
return discarded;
}
add_task(async function toplevelForNewWindow() {
let win = await BrowserTestUtils.openNewBrowserWindow();
let browsingContext = win.gBrowser.selectedBrowser.browsingContext;
await observeDiscarded([win.browsingContext, browsingContext], async () => {
await BrowserTestUtils.closeWindow(win);
});
});
add_task(async function toplevelForNewTab() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
let browsingContext = tab.linkedBrowser.browsingContext;
let discarded = await observeDiscarded([browsingContext], () => {
BrowserTestUtils.removeTab(tab);
});
ok(
!discarded.includes(window.browsingContext),
"no notification for the current window's chrome browsing context"
);
});
add_task(async function subframe() {
let win = await BrowserTestUtils.openNewBrowserWindow();
let bc = await SpecialPowers.spawn(win.gBrowser.selectedBrowser, [], () => {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
let browsingContext = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
let iframe = content.document.createElement("iframe");
content.document.body.appendChild(iframe);
iframe.contentWindow.location = "https://example.com/";
return iframe.browsingContext;
});
let promise = BrowserUtils.promiseObserved(TOPIC, subject => subject === bc);
await BrowserTestUtils.closeWindow(win);
await promise;
let discarded = await observeDiscarded([browsingContext], async () => {
await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
let iframe = content.document.querySelector("iframe");
iframe.remove();
});
});
// If we make it here, then we've received the notification.
ok(true, "got subframe notification");
ok(
!discarded.includes(tab.browsingContext),
"no notification for toplevel browsing context"
);
BrowserTestUtils.removeTab(tab);
});

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

@ -51,9 +51,6 @@
#include "nsThreadUtils.h"
#include "mozJSComponentLoader.h"
#include "GeckoProfiler.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
#include "nsIException.h"
namespace mozilla::dom {

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

@ -22,9 +22,6 @@
#include "TimeoutBudgetManager.h"
#include "mozilla/net/WebSocketEventService.h"
#include "mozilla/MediaManager.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
@ -154,10 +151,13 @@ void TimeoutManager::MoveIdleToActive() {
int(elapsed.ToMilliseconds()), int(target.ToMilliseconds()),
int(delta.ToMilliseconds()));
// don't have end before start...
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"setTimeout deferred release", DOM, TextMarkerPayload,
(marker, delta.ToMilliseconds() >= 0 ? timeout->When() : now, now,
Some(mWindow.WindowID())));
PROFILER_MARKER_TEXT(
"setTimeout deferred release", DOM,
MarkerOptions(
MarkerTiming::Interval(
delta.ToMilliseconds() >= 0 ? timeout->When() : now, now),
MarkerInnerWindowId(mWindow.WindowID())),
marker);
}
#endif
num++;
@ -906,10 +906,13 @@ void TimeoutManager::RunTimeout(const TimeStamp& aNow,
int(elapsed.ToMilliseconds()), int(target.ToMilliseconds()),
int(delta.ToMilliseconds()), int(runtime.ToMilliseconds()));
// don't have end before start...
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"setTimeout", DOM, TextMarkerPayload,
(marker, delta.ToMilliseconds() >= 0 ? timeout->When() : now, now,
Some(mWindow.WindowID())));
PROFILER_MARKER_TEXT(
"setTimeout", DOM,
MarkerOptions(
MarkerTiming::Interval(
delta.ToMilliseconds() >= 0 ? timeout->When() : now, now),
MarkerInnerWindowId(mWindow.WindowID())),
marker);
}
#endif

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

@ -25,7 +25,7 @@
#include "nsPrintfCString.h"
#include "prtime.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# include "mozilla/ProfilerMarkerTypes.h"
#endif
using namespace mozilla;
@ -116,14 +116,18 @@ void nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI) {
void nsDOMNavigationTiming::NotifyUnloadEventStart() {
mUnloadStart = TimeStamp::Now();
PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Unload", NETWORK,
TRACING_INTERVAL_START, mDocShell);
PROFILER_MARKER("Unload", NETWORK,
MarkerOptions(MarkerTiming::IntervalStart(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
}
void nsDOMNavigationTiming::NotifyUnloadEventEnd() {
mUnloadEnd = TimeStamp::Now();
PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Unload", NETWORK,
TRACING_INTERVAL_END, mDocShell);
PROFILER_MARKER("Unload", NETWORK,
MarkerOptions(MarkerTiming::IntervalEnd(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
}
void nsDOMNavigationTiming::NotifyLoadEventStart() {
@ -132,8 +136,10 @@ void nsDOMNavigationTiming::NotifyLoadEventStart() {
}
mLoadEventStart = TimeStamp::Now();
PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Load", NETWORK,
TRACING_INTERVAL_START, mDocShell);
PROFILER_MARKER("Load", NETWORK,
MarkerOptions(MarkerTiming::IntervalStart(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
if (IsTopLevelContentDocumentInContentProcess()) {
mLoadEventStartForTelemetry = TimeStamp::Now();
@ -163,8 +169,10 @@ void nsDOMNavigationTiming::NotifyLoadEventEnd() {
}
mLoadEventEnd = TimeStamp::Now();
PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "Load", NETWORK,
TRACING_INTERVAL_END, mDocShell);
PROFILER_MARKER("Load", NETWORK,
MarkerOptions(MarkerTiming::IntervalEnd(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
if (IsTopLevelContentDocumentInContentProcess()) {
#ifdef MOZ_GECKO_PROFILER
@ -179,10 +187,13 @@ void nsDOMNavigationTiming::NotifyLoadEventEnd() {
"Document %s loaded after %dms, load event duration %dms", spec.get(),
int(elapsed.ToMilliseconds()), int(duration.ToMilliseconds()));
PAGELOAD_LOG(("%s", marker.get()));
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"DocumentLoad", DOM, TextMarkerPayload,
(marker, mNavigationStart, mLoadEventEnd,
profiler_get_inner_window_id_from_docshell(mDocShell)));
PROFILER_MARKER_TEXT(
"DocumentLoad", DOM,
MarkerOptions(
MarkerTiming::Interval(mNavigationStart, mLoadEventEnd),
MarkerInnerWindowId(
profiler_get_inner_window_id_from_docshell(mDocShell))),
marker);
}
#endif
TimeStamp loadEventEnd = TimeStamp::Now();
@ -241,8 +252,10 @@ void nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI) {
mLoadedURI = aURI;
mDOMContentLoadedEventStart = TimeStamp::Now();
PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "DOMContentLoaded", NETWORK,
TRACING_INTERVAL_START, mDocShell);
PROFILER_MARKER("DOMContentLoaded", NETWORK,
MarkerOptions(MarkerTiming::IntervalStart(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
if (IsTopLevelContentDocumentInContentProcess()) {
TimeStamp now = TimeStamp::Now();
@ -273,8 +286,10 @@ void nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI) {
mLoadedURI = aURI;
mDOMContentLoadedEventEnd = TimeStamp::Now();
PROFILER_TRACING_MARKER_DOCSHELL("Navigation", "DOMContentLoaded", NETWORK,
TRACING_INTERVAL_END, mDocShell);
PROFILER_MARKER("DOMContentLoaded", NETWORK,
MarkerOptions(MarkerTiming::IntervalEnd(),
MarkerInnerWindowIdFromDocShell(mDocShell)),
Tracing, "Navigation");
if (IsTopLevelContentDocumentInContentProcess()) {
Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_CONTENT_LOADED_END_MS,
@ -377,10 +392,13 @@ void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) {
int(elapsed.ToMilliseconds()),
int(elapsedLongTask.ToMilliseconds()), spec.get());
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"TimeToFirstInteractive (TTFI)", DOM, TextMarkerPayload,
(marker, mNavigationStart, mTTFI,
profiler_get_inner_window_id_from_docshell(mDocShell)));
PROFILER_MARKER_TEXT(
"TimeToFirstInteractive (TTFI)", DOM,
MarkerOptions(
MarkerTiming::Interval(mNavigationStart, mTTFI),
MarkerInnerWindowId(
profiler_get_inner_window_id_from_docshell(mDocShell))),
marker);
}
#endif
}
@ -410,10 +428,13 @@ void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() {
: "this tab was inactive some of the time between navigation start "
"and first non-blank paint");
PAGELOAD_LOG(("%s", marker.get()));
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"FirstNonBlankPaint", DOM, TextMarkerPayload,
(marker, mNavigationStart, mNonBlankPaint,
profiler_get_inner_window_id_from_docshell(mDocShell)));
PROFILER_MARKER_TEXT(
"FirstNonBlankPaint", DOM,
MarkerOptions(
MarkerTiming::Interval(mNavigationStart, mNonBlankPaint),
MarkerInnerWindowId(
profiler_get_inner_window_id_from_docshell(mDocShell))),
marker);
}
#endif
@ -459,10 +480,13 @@ void nsDOMNavigationTiming::NotifyContentfulPaintForRootContentDocument(
: "this tab was inactive some of the time between navigation start "
"and first non-blank paint");
PAGELOAD_LOG(("%s", marker.get()));
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"FirstContentfulPaint", DOM, TextMarkerPayload,
(marker, mNavigationStart, mContentfulPaint,
profiler_get_inner_window_id_from_docshell(mDocShell)));
PROFILER_MARKER_TEXT(
"FirstContentfulPaint", DOM,
MarkerOptions(
MarkerTiming::Interval(mNavigationStart, mContentfulPaint),
MarkerInnerWindowId(
profiler_get_inner_window_id_from_docshell(mDocShell))),
marker);
}
#endif
@ -508,10 +532,13 @@ void nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument() {
: "this tab was inactive some of the time between navigation start "
"and DOMContentFlushed");
PAGELOAD_LOG(("%s", marker.get()));
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"DOMContentFlushed", DOM, TextMarkerPayload,
(marker, mNavigationStart, mDOMContentFlushed,
profiler_get_inner_window_id_from_docshell(mDocShell)));
PROFILER_MARKER_TEXT(
"DOMContentFlushed", DOM,
MarkerOptions(
MarkerTiming::Interval(mNavigationStart, mDOMContentFlushed),
MarkerInnerWindowId(
profiler_get_inner_window_id_from_docshell(mDocShell))),
marker);
}
#endif
}

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

@ -1039,10 +1039,6 @@ nsresult EventDispatcher::Dispatch(nsISupports* aTarget,
struct DOMEventMarker {
static constexpr Span<const char> MarkerTypeName() {
// Note: DOMEventMarkerPayload was originally a sub-class of
// TracingMarkerPayload, so it uses the same payload type.
// TODO: Change to its own distinct type, but this will require
// front-end changes.
return MakeStringSpan("DOMEvent");
}
static void StreamJSONMarkerData(
@ -1099,14 +1095,10 @@ nsresult EventDispatcher::Dispatch(nsISupports* aTarget,
if (aPresContext && aPresContext->GetRootPresContext()) {
nsRefreshDriver* driver =
aPresContext->GetRootPresContext()->RefreshDriver();
if (driver && driver->ViewManagerFlushIsPending()) {
nsIWidget* widget = aPresContext->GetRootWidget();
layers::LayerManager* lm =
widget ? widget->GetLayerManager() : nullptr;
if (lm) {
lm->RegisterPayload({layers::CompositionPayloadType::eKeyPress,
aEvent->mTimeStamp});
}
if (driver && driver->HasPendingTick()) {
driver->RegisterCompositionPayload(
{layers::CompositionPayloadType::eKeyPress,
aEvent->mTimeStamp});
}
}
}

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

@ -4923,6 +4923,7 @@ void HTMLMediaElement::UnbindFromTree(bool aNullParent) {
[self = RefPtr<HTMLMediaElement>(this)]() {
if (!self->IsInComposedDoc()) {
self->PauseInternal();
self->mMediaControlKeyListener->StopIfNeeded();
}
});
RunInStableState(task);

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

@ -21,9 +21,6 @@
# include "mozilla/a11y/PDocAccessible.h"
#endif
#include "GeckoProfiler.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
#include "GMPServiceParent.h"
#include "HandlerServiceParent.h"
#include "IHistory.h"
@ -918,9 +915,7 @@ already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
if (profiler_thread_is_being_profiled()) {
nsPrintfCString marker("Reused process %u",
(unsigned int)retval->ChildID());
TimeStamp now = TimeStamp::Now();
PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload,
(marker, now, now));
PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
}
#endif
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
@ -959,9 +954,7 @@ already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
if (profiler_thread_is_being_profiled()) {
nsPrintfCString marker("Recycled process %u (%p)",
(unsigned int)recycled->ChildID(), recycled.get());
TimeStamp now = TimeStamp::Now();
PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload,
(marker, now, now));
PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
}
#endif
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
@ -984,9 +977,7 @@ already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
if (profiler_thread_is_being_profiled()) {
nsPrintfCString marker("Assigned preallocated process %u",
(unsigned int)preallocated->ChildID());
TimeStamp now = TimeStamp::Now();
PROFILER_ADD_MARKER_WITH_PAYLOAD("Process", DOM, TextMarkerPayload,
(marker, now, now));
PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
}
#endif
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
@ -2423,9 +2414,10 @@ bool ContentParent::LaunchSubprocessResolve(bool aIsSync,
nsPrintfCString marker("Process start%s for %u",
mIsAPreallocBlocker ? " (immediate)" : "",
(unsigned int)ChildID());
PROFILER_ADD_MARKER_WITH_PAYLOAD(
mIsAPreallocBlocker ? "Process Immediate Launch" : "Process Launch",
DOM, TextMarkerPayload, (marker, mLaunchTS, launchResumeTS));
PROFILER_MARKER_TEXT(
mIsAPreallocBlocker ? ProfilerString8View("Process Immediate Launch")
: ProfilerString8View("Process Launch"),
DOM, MarkerTiming::Interval(mLaunchTS, launchResumeTS), marker);
}
#endif
@ -7026,10 +7018,12 @@ mozilla::ipc::IPCResult ContentParent::RecvHistoryCommit(
mozilla::ipc::IPCResult ContentParent::RecvHistoryGo(
const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
uint64_t aHistoryEpoch, HistoryGoResolver&& aResolveRequestedIndex) {
uint64_t aHistoryEpoch, bool aRequireUserInteraction,
HistoryGoResolver&& aResolveRequestedIndex) {
if (!aContext.IsDiscarded()) {
aContext.get_canonical()->HistoryGo(aOffset, aHistoryEpoch, Some(ChildID()),
std::move(aResolveRequestedIndex));
aContext.get_canonical()->HistoryGo(
aOffset, aHistoryEpoch, aRequireUserInteraction, Some(ChildID()),
std::move(aResolveRequestedIndex));
}
return IPC_OK();
}

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

@ -1343,7 +1343,8 @@ class ContentParent final
mozilla::ipc::IPCResult RecvHistoryGo(
const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
uint64_t aHistoryEpoch, HistoryGoResolver&& aResolveRequestedIndex);
uint64_t aHistoryEpoch, bool aRequireUserInteraction,
HistoryGoResolver&& aResolveRequestedIndex);
mozilla::ipc::IPCResult RecvSessionHistoryUpdate(
const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aIndex,

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

@ -1681,7 +1681,7 @@ parent:
uint64_t aLoadID, nsID aChangeID, uint32_t aLoadType);
async HistoryGo(MaybeDiscardedBrowsingContext aContext,
int32_t aOffset, uint64_t aHistoryEpoch) returns(int32_t requestedIndex);
int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction) returns(int32_t requestedIndex);
async BlobURLDataRequest(nsCString aBlobURL,
nsIPrincipal aTriggeringPrincipal,

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

@ -16,9 +16,6 @@
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Sprintf.h"
#include "GeckoProfiler.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
#include "MPSCQueue.h"
#if defined(_WIN32)
@ -199,24 +196,41 @@ class AsyncLogger {
}
#ifdef MOZ_GECKO_PROFILER
{
struct Budget {
static constexpr Span<const char> MarkerTypeName() {
return MakeStringSpan("Budget");
}
static void StreamJSONMarkerData(
baseprofiler::SpliceableJSONWriter& aWriter) {}
static MarkerSchema MarkerTypeDisplay() {
using MS = MarkerSchema;
MS schema{MS::Location::markerChart, MS::Location::markerTable};
// Nothing outside the defaults.
return schema;
}
};
TracePayload message;
while (mMessageQueueProfiler.Pop(&message) && mRunning) {
if (message.mPhase != TracingPhase::COMPLETE) {
TracingKind kind = message.mPhase == TracingPhase::BEGIN
? TracingKind::TRACING_INTERVAL_START
: TracingKind::TRACING_INTERVAL_END;
TracingMarkerPayload payload("media", kind, message.mTimestamp);
profiler_add_marker_for_thread(
message.mTID, JS::ProfilingCategoryPair::MEDIA_RT,
message.mName, payload);
profiler_add_marker(
ProfilerString8View::WrapNullTerminatedString(message.mName),
geckoprofiler::category::MEDIA_RT,
{MarkerThreadId(message.mTID),
(message.mPhase == TracingPhase::BEGIN)
? MarkerTiming::IntervalStart(message.mTimestamp)
: MarkerTiming::IntervalEnd(message.mTimestamp)},
Budget{});
} else {
mozilla::TimeStamp end =
message.mTimestamp +
TimeDuration::FromMicroseconds(message.mDurationUs);
BudgetMarkerPayload payload(message.mTimestamp, end);
profiler_add_marker_for_thread(
message.mTID, JS::ProfilingCategoryPair::MEDIA_RT,
message.mName, payload);
profiler_add_marker(
ProfilerString8View::WrapNullTerminatedString(message.mName),
geckoprofiler::category::MEDIA_RT,
{MarkerThreadId(message.mTID),
MarkerTiming::Interval(
message.mTimestamp,
message.mTimestamp + TimeDuration::FromMicroseconds(
message.mDurationUs))},
Budget{});
}
}
}

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

@ -27,6 +27,7 @@
#include "AudioSegment.h"
#include "DOMMediaStream.h"
#include "ImageContainer.h"
#include "GeckoProfiler.h"
#include "MediaDecoder.h"
#include "MediaDecoderStateMachine.h"
#include "MediaShutdownManager.h"
@ -38,17 +39,8 @@
#include "VideoUtils.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# define MDSM_ERROR_MARKER(tag, error, markerTime) \
PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
(error, markerTime))
# define MDSM_SAMPLE_MARKER(tag, startTime, endTime) \
PROFILER_ADD_MARKER_WITH_PAYLOAD( \
tag, MEDIA_PLAYBACK, MediaSampleMarkerPayload, (startTime, endTime))
#else
# define MDSM_ERROR_MARKER(tag, error, markerTime)
# define MDSM_SAMPLE_MARKER(tag, startTime, endTime)
#endif
# include "mozilla/ProfilerMarkerTypes.h"
#endif // MOZ_GECKO_PROFILER
namespace mozilla {
@ -2859,8 +2851,9 @@ void MediaDecoderStateMachine::PushAudio(AudioData* aSample) {
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(aSample);
AudioQueue().Push(aSample);
MDSM_SAMPLE_MARKER("MDSM::PushAudio", aSample->mTime.ToMicroseconds(),
aSample->GetEndTime().ToMicroseconds());
PROFILER_MARKER("MDSM::PushAudio", MEDIA_PLAYBACK, {}, MediaSampleMarker,
aSample->mTime.ToMicroseconds(),
aSample->GetEndTime().ToMicroseconds());
}
void MediaDecoderStateMachine::PushVideo(VideoData* aSample) {
@ -2868,8 +2861,9 @@ void MediaDecoderStateMachine::PushVideo(VideoData* aSample) {
MOZ_ASSERT(aSample);
aSample->mFrameID = ++mCurrentFrameID;
VideoQueue().Push(aSample);
MDSM_SAMPLE_MARKER("MDSM::PushVideo", aSample->mTime.ToMicroseconds(),
aSample->GetEndTime().ToMicroseconds());
PROFILER_MARKER("MDSM::PushVideo", MEDIA_PLAYBACK, {}, MediaSampleMarker,
aSample->mTime.ToMicroseconds(),
aSample->GetEndTime().ToMicroseconds());
}
void MediaDecoderStateMachine::OnAudioPopped(const RefPtr<AudioData>& aSample) {
@ -3469,8 +3463,8 @@ bool MediaDecoderStateMachine::HasLowBufferedData(const TimeUnit& aThreshold) {
void MediaDecoderStateMachine::DecodeError(const MediaResult& aError) {
MOZ_ASSERT(OnTaskQueue());
LOGE("Decode error: %s", aError.Description().get());
MDSM_ERROR_MARKER("MDSM::DecodeError", aError.Description(),
TimeStamp::NowUnfuzzed());
PROFILER_MARKER_TEXT("MDSM::DecodeError", MEDIA_PLAYBACK, {},
aError.Description());
// Notify the decode error and MediaDecoder will shut down MDSM.
mOnPlaybackErrorEvent.Notify(aError);
}

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

@ -42,15 +42,6 @@ mozilla::LazyLogModule gMediaDemuxerLog("MediaDemuxer");
DDMOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Verbose, "::%s: " arg, \
__func__, ##__VA_ARGS__)
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# define MEDIA_FORMAT_READER_STATUS_MARKER(tag, text, markerTime) \
PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
(text, markerTime))
#else
# define MEDIA_FORMAT_READER_STATUS_MARKER(tag, text, markerTime)
#endif
#define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
namespace mozilla {
@ -1931,8 +1922,7 @@ void MediaFormatReader::HandleDemuxedSamples(
nsPrintfCString markerString(
"%s stream id changed from:%" PRIu32 " to:%" PRIu32,
TrackTypeToStr(aTrack), decoder.mLastStreamSourceID, info->GetID());
MEDIA_FORMAT_READER_STATUS_MARKER("StreamID Change", markerString,
TimeStamp::NowUnfuzzed());
PROFILER_MARKER_TEXT("StreamID Change", MEDIA_PLAYBACK, {}, markerString);
LOG("%s", markerString.get());
if (aTrack == TrackInfo::kVideoTrack) {
@ -3150,6 +3140,5 @@ void MediaFormatReader::OnFirstDemuxFailed(TrackInfo::TrackType aType,
} // namespace mozilla
#undef NS_DispatchToMainThread
#undef MEDIA_FORMAT_READER_STATUS_MARKER
#undef LOGV
#undef LOG

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

@ -5,13 +5,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RDDProcessHost.h"
#include "ProcessUtils.h"
#include "RDDChild.h"
#include "chrome/common/process_watcher.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_media.h"
#include "ProcessUtils.h"
#include "RDDChild.h"
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
# include "mozilla/Sandbox.h"
#endif
@ -27,11 +26,7 @@ bool RDDProcessHost::sLaunchWithMacSandbox = false;
RDDProcessHost::RDDProcessHost(Listener* aListener)
: GeckoChildProcessHost(GeckoProcessType_RDD),
mListener(aListener),
mTaskFactory(this),
mLaunchPhase(LaunchPhase::Unlaunched),
mProcessToken(0),
mShutdownRequested(false),
mChannelClosed(false) {
mLiveToken(new media::Refcountable<bool>(true)) {
MOZ_COUNT_CTOR(RDDProcessHost);
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
@ -45,6 +40,8 @@ RDDProcessHost::RDDProcessHost(Listener* aListener)
RDDProcessHost::~RDDProcessHost() { MOZ_COUNT_DTOR(RDDProcessHost); }
bool RDDProcessHost::Launch(StringVector aExtraOpts) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
MOZ_ASSERT(!mRDDChild);
@ -61,19 +58,6 @@ bool RDDProcessHost::Launch(StringVector aExtraOpts) {
mLaunchPhase = LaunchPhase::Waiting;
mLaunchTime = TimeStamp::Now();
if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts)) {
mLaunchPhase = LaunchPhase::Complete;
mPrefSerializer = nullptr;
return false;
}
return true;
}
bool RDDProcessHost::WaitForLaunch() {
if (mLaunchPhase == LaunchPhase::Complete) {
return !!mRDDChild;
}
int32_t timeoutMs = StaticPrefs::media_rdd_process_startup_timeout_ms();
// If one of the following environment variables are set we can
@ -83,13 +67,62 @@ bool RDDProcessHost::WaitForLaunch() {
PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
timeoutMs = 0;
}
if (timeoutMs) {
// We queue a delayed task. If that task runs before the
// WhenProcessHandleReady promise gets resolved, we will abort the launch.
GetMainThreadSerialEventTarget()->DelayedDispatch(
NS_NewRunnableFunction(
"RDDProcessHost::Launchtimeout",
[this, liveToken = mLiveToken]() {
if (!*liveToken || mTimerChecked) {
// We have been deleted or the runnable has already started, we
// can abort.
return;
}
InitAfterConnect(false);
MOZ_ASSERT(mTimerChecked,
"InitAfterConnect must have acted on the promise");
}),
timeoutMs);
}
// Our caller expects the connection to be finished after we return, so we
// immediately set up the IPDL actor and fire callbacks. The IO thread will
// still dispatch a notification to the main thread - we'll just ignore it.
bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs);
InitAfterConnect(result);
return result;
if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts)) {
mLaunchPhase = LaunchPhase::Complete;
mPrefSerializer = nullptr;
return false;
}
return true;
}
RefPtr<GenericNonExclusivePromise> RDDProcessHost::LaunchPromise() {
MOZ_ASSERT(NS_IsMainThread());
if (mLaunchPromise) {
return mLaunchPromise;
}
mLaunchPromise = MakeRefPtr<GenericNonExclusivePromise::Private>(__func__);
WhenProcessHandleReady()->Then(
GetCurrentSerialEventTarget(), __func__,
[this, liveToken = mLiveToken](
const ipc::ProcessHandlePromise::ResolveOrRejectValue& aResult) {
if (!*liveToken) {
// The RDDProcessHost got deleted. Abort. The promise would have
// already been rejected.
return;
}
if (mTimerChecked) {
// We hit the timeout earlier, abort.
return;
}
mTimerChecked = true;
if (aResult.IsReject()) {
RejectPromise();
}
// If aResult.IsResolve() then we have succeeded in launching the
// RDD process. The promise will be resolved once the channel has
// connected (or failed to) later.
});
return mLaunchPromise;
}
void RDDProcessHost::OnChannelConnected(int32_t peer_pid) {
@ -97,15 +130,12 @@ void RDDProcessHost::OnChannelConnected(int32_t peer_pid) {
GeckoChildProcessHost::OnChannelConnected(peer_pid);
// Post a task to the main thread. Take the lock because mTaskFactory is not
// thread-safe.
RefPtr<Runnable> runnable;
{
MonitorAutoLock lock(mMonitor);
runnable =
mTaskFactory.NewRunnableMethod(&RDDProcessHost::OnChannelConnectedTask);
}
NS_DispatchToMainThread(runnable);
NS_DispatchToMainThread(NS_NewRunnableFunction(
"RDDProcessHost::OnChannelConnected", [this, liveToken = mLiveToken]() {
if (*liveToken && mLaunchPhase == LaunchPhase::Waiting) {
InitAfterConnect(true);
}
}));
}
void RDDProcessHost::OnChannelError() {
@ -113,71 +143,59 @@ void RDDProcessHost::OnChannelError() {
GeckoChildProcessHost::OnChannelError();
// Post a task to the main thread. Take the lock because mTaskFactory is not
// thread-safe.
RefPtr<Runnable> runnable;
{
MonitorAutoLock lock(mMonitor);
runnable =
mTaskFactory.NewRunnableMethod(&RDDProcessHost::OnChannelErrorTask);
}
NS_DispatchToMainThread(runnable);
}
void RDDProcessHost::OnChannelConnectedTask() {
if (mLaunchPhase == LaunchPhase::Waiting) {
InitAfterConnect(true);
}
}
void RDDProcessHost::OnChannelErrorTask() {
if (mLaunchPhase == LaunchPhase::Waiting) {
InitAfterConnect(false);
}
NS_DispatchToMainThread(NS_NewRunnableFunction(
"RDDProcessHost::OnChannelError", [this, liveToken = mLiveToken]() {
if (*liveToken && mLaunchPhase == LaunchPhase::Waiting) {
InitAfterConnect(false);
}
}));
}
static uint64_t sRDDProcessTokenCounter = 0;
void RDDProcessHost::InitAfterConnect(bool aSucceeded) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
MOZ_ASSERT(!mRDDChild);
mLaunchPhase = LaunchPhase::Complete;
if (aSucceeded) {
mProcessToken = ++sRDDProcessTokenCounter;
mRDDChild = MakeUnique<RDDChild>(this);
DebugOnly<bool> rv = mRDDChild->Open(
TakeChannel(), base::GetProcId(GetChildProcessHandle()));
MOZ_ASSERT(rv);
// Only clear mPrefSerializer in the success case to avoid a
// possible race in the case case of a timeout on Windows launch.
// See Bug 1555076 comment 7:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1555076#c7
mPrefSerializer = nullptr;
if (!mRDDChild->Init()) {
// Can't just kill here because it will create a timing race that
// will crash the tab. We don't really want to crash the tab just
// because RDD linux sandbox failed to initialize. In this case,
// we'll close the child channel which will cause the RDD process
// to shutdown nicely avoiding the tab crash (which manifests as
// Bug 1535335).
mRDDChild->Close();
return;
}
if (!aSucceeded) {
RejectPromise();
return;
}
mProcessToken = ++sRDDProcessTokenCounter;
mRDDChild = MakeUnique<RDDChild>(this);
DebugOnly<bool> rv =
mRDDChild->Open(TakeChannel(), base::GetProcId(GetChildProcessHandle()));
MOZ_ASSERT(rv);
if (mListener) {
mListener->OnProcessLaunchComplete(this);
// Only clear mPrefSerializer in the success case to avoid a
// possible race in the case case of a timeout on Windows launch.
// See Bug 1555076 comment 7:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1555076#c7
mPrefSerializer = nullptr;
if (!mRDDChild->Init()) {
// Can't just kill here because it will create a timing race that
// will crash the tab. We don't really want to crash the tab just
// because RDD linux sandbox failed to initialize. In this case,
// we'll close the child channel which will cause the RDD process
// to shutdown nicely avoiding the tab crash (which manifests as
// Bug 1535335).
mRDDChild->Close();
RejectPromise();
} else {
ResolvePromise();
}
}
void RDDProcessHost::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mShutdownRequested);
mListener = nullptr;
RejectPromise();
if (mRDDChild) {
// OnChannelClosed uses this to check if the shutdown was expected or
@ -208,7 +226,10 @@ void RDDProcessHost::Shutdown() {
}
void RDDProcessHost::OnChannelClosed() {
MOZ_ASSERT(NS_IsMainThread());
mChannelClosed = true;
RejectPromise();
if (!mShutdownRequested && mListener) {
// This is an unclean shutdown. Notify our listener that we're going away.
@ -219,10 +240,11 @@ void RDDProcessHost::OnChannelClosed() {
// Release the actor.
RDDChild::Destroy(std::move(mRDDChild));
MOZ_ASSERT(!mRDDChild);
}
void RDDProcessHost::KillHard(const char* aReason) {
MOZ_ASSERT(NS_IsMainThread());
ProcessHandle handle = GetChildProcessHandle();
if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) {
NS_WARNING("failed to kill subprocess!");
@ -231,22 +253,46 @@ void RDDProcessHost::KillHard(const char* aReason) {
SetAlreadyDead();
}
uint64_t RDDProcessHost::GetProcessToken() const { return mProcessToken; }
void RDDProcessHost::KillProcess() { KillHard("DiagnosticKill"); }
uint64_t RDDProcessHost::GetProcessToken() const {
MOZ_ASSERT(NS_IsMainThread());
return mProcessToken;
}
void RDDProcessHost::DestroyProcess() {
// Cancel all tasks. We don't want anything triggering after our caller
// expects this to go away.
{
MonitorAutoLock lock(mMonitor);
mTaskFactory.RevokeAll();
}
MOZ_ASSERT(NS_IsMainThread());
RejectPromise();
GetCurrentSerialEventTarget()->Dispatch(
// Any pending tasks will be cancelled from now on.
*mLiveToken = false;
NS_DispatchToMainThread(
NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); }));
}
void RDDProcessHost::ResolvePromise() {
MOZ_ASSERT(NS_IsMainThread());
if (!mLaunchPromiseSettled) {
mLaunchPromise->Resolve(true, __func__);
mLaunchPromiseSettled = true;
}
// We have already acted on the promise; the timeout runnable no longer needs
// to interrupt anything.
mTimerChecked = true;
}
void RDDProcessHost::RejectPromise() {
MOZ_ASSERT(NS_IsMainThread());
if (!mLaunchPromiseSettled) {
mLaunchPromise->Reject(NS_ERROR_FAILURE, __func__);
mLaunchPromiseSettled = true;
}
// We have already acted on the promise; the timeout runnable no longer needs
// to interrupt anything.
mTimerChecked = true;
}
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
bool RDDProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) {
GeckoChildProcessHost::FillMacSandboxInfo(aInfo);

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

@ -6,12 +6,10 @@
#ifndef _include_dom_media_ipc_RDDProcessHost_h_
#define _include_dom_media_ipc_RDDProcessHost_h_
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/ipc/TaskFactory.h"
#include "mozilla/media/MediaUtils.h"
namespace mozilla {
namespace ipc {
@ -37,8 +35,6 @@ class RDDProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
public:
class Listener {
public:
virtual void OnProcessLaunchComplete(RDDProcessHost* aHost) {}
// The RDDProcessHost has unexpectedly shutdown or had its connection
// severed. This is not called if an error occurs after calling
// Shutdown().
@ -48,19 +44,18 @@ class RDDProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
explicit RDDProcessHost(Listener* listener);
// Launch the subprocess asynchronously. On failure, false is returned.
// Otherwise, true is returned, and the OnProcessLaunchComplete listener
// callback will be invoked either when a connection has been established, or
// if a connection could not be established due to an asynchronous error.
// Otherwise, true is returned. If succeeded, a follow-up call should be made
// to LaunchPromise() which will return a promise that will be resolved once
// the RDD process has launched and a channel has been established.
//
// @param aExtraOpts (StringVector)
// Extra options to pass to the subprocess.
bool Launch(StringVector aExtraOpts);
// If the process is being launched, block until it has launched and
// connected. If a launch task is pending, it will fire immediately.
//
// Returns true if the process is successfully connected; false otherwise.
bool WaitForLaunch();
// Return a promise that will be resolved once the process has completed its
// launch. The promise will be immediately resolved if the launch has already
// succeeded.
RefPtr<GenericNonExclusivePromise> LaunchPromise();
// Inform the process that it should clean up its resources and shut
// down. This initiates an asynchronous shutdown sequence. After this
@ -72,13 +67,19 @@ class RDDProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
// Return the actor for the top-level actor of the process. If the process
// has not connected yet, this returns null.
RDDChild* GetActor() const { return mRDDChild.get(); }
RDDChild* GetActor() const {
MOZ_ASSERT(NS_IsMainThread());
return mRDDChild.get();
}
// Return a unique id for this process, guaranteed not to be shared with any
// past or future instance of RDDProcessHost.
uint64_t GetProcessToken() const;
bool IsConnected() const { return !!mRDDChild; }
bool IsConnected() const {
MOZ_ASSERT(NS_IsMainThread());
return !!mRDDChild;
}
// Return the time stamp for when we tried to launch the RDD process.
// This is currently used for Telemetry so that we can determine how
@ -92,9 +93,6 @@ class RDDProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
void SetListener(Listener* aListener);
// Used for tests and diagnostics
void KillProcess();
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
// Return the sandbox type to be used with this process type.
static MacSandboxType GetMacSandboxType();
@ -103,11 +101,8 @@ class RDDProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
private:
~RDDProcessHost();
// Called on the main thread.
void OnChannelConnectedTask();
void OnChannelErrorTask();
// Called on the main thread after a connection has been established.
// Called on the main thread with true after a connection has been established
// or false if it failed (including if it failed before the timeout kicked in)
void InitAfterConnect(bool aSucceeded);
// Called on the main thread when the mRDDChild actor is shutting down.
@ -130,21 +125,37 @@ class RDDProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
DISALLOW_COPY_AND_ASSIGN(RDDProcessHost);
Listener* mListener;
mozilla::ipc::TaskFactory<RDDProcessHost> mTaskFactory;
Listener* const mListener;
// All members below are only ever accessed on the main thread.
enum class LaunchPhase { Unlaunched, Waiting, Complete };
LaunchPhase mLaunchPhase;
LaunchPhase mLaunchPhase = LaunchPhase::Unlaunched;
UniquePtr<RDDChild> mRDDChild;
uint64_t mProcessToken;
uint64_t mProcessToken = 0;
UniquePtr<ipc::SharedPreferenceSerializer> mPrefSerializer;
bool mShutdownRequested;
bool mChannelClosed;
bool mShutdownRequested = false;
bool mChannelClosed = false;
TimeStamp mLaunchTime;
void RejectPromise();
void ResolvePromise();
// Set to true on construction and to false just prior deletion.
// The RDDProcessHost isn't refcounted; so we can capture this by value in
// lambdas along with a strong reference to mLiveToken and check if that value
// is true before accessing "this".
// While a reference to mLiveToken can be taken on any thread; its value can
// only be read on the main thread.
const RefPtr<media::Refcountable<bool>> mLiveToken;
RefPtr<GenericNonExclusivePromise::Private> mLaunchPromise;
bool mLaunchPromiseSettled = false;
// Will be set to true if we've exceeded the allowed startup time or if the
// RDD process as successfully started. This is used to determine if the
// timeout runnable needs to execute code or not.
bool mTimerChecked = false;
};
} // namespace mozilla

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

@ -101,47 +101,86 @@ void RDDProcessManager::OnPreferenceChange(const char16_t* aData) {
auto RDDProcessManager::EnsureRDDProcessAndCreateBridge(
base::ProcessId aOtherProcess) -> RefPtr<EnsureRDDPromise> {
return InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
[aOtherProcess, this]() -> RefPtr<EnsureRDDPromise> {
if (!Get()) {
// Shutdown?
return EnsureRDDPromise::CreateAndReject(
NS_ERROR_NOT_AVAILABLE, __func__);
}
if (mProcess) {
ipc::Endpoint<PRemoteDecoderManagerChild> endpoint;
if (!CreateContentBridge(aOtherProcess, &endpoint)) {
return EnsureRDDPromise::CreateAndReject(
NS_ERROR_NOT_AVAILABLE, __func__);
}
return EnsureRDDPromise::CreateAndResolve(
std::move(endpoint), __func__);
}
return InvokeAsync(
GetMainThreadSerialEventTarget(), __func__,
[aOtherProcess, this]() -> RefPtr<EnsureRDDPromise> {
if (!Get()) {
// Shutdown?
return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
__func__);
}
if (mProcess) {
return mProcess->LaunchPromise()->Then(
GetMainThreadSerialEventTarget(), __func__,
[aOtherProcess, this](bool) {
ipc::Endpoint<PRemoteDecoderManagerChild> endpoint;
if (!CreateContentBridge(aOtherProcess, &endpoint)) {
return EnsureRDDPromise::CreateAndReject(
NS_ERROR_NOT_AVAILABLE, __func__);
}
return EnsureRDDPromise::CreateAndResolve(std::move(endpoint),
__func__);
},
[](nsresult aError) {
return EnsureRDDPromise::CreateAndReject(aError, __func__);
});
}
mNumProcessAttempts++;
if (mNumProcessAttempts &&
!StaticPrefs::media_rdd_retryonfailure_enabled()) {
// We failed to start the RDD process earlier, abort now.
return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
__func__);
}
// Launch the RDD process.
std::vector<std::string> extraArgs;
nsCString parentBuildID(mozilla::PlatformBuildID());
extraArgs.push_back("-parentBuildID");
extraArgs.push_back(parentBuildID.get());
std::vector<std::string> extraArgs;
nsCString parentBuildID(mozilla::PlatformBuildID());
extraArgs.push_back("-parentBuildID");
extraArgs.push_back(parentBuildID.get());
// The subprocess is launched asynchronously, so we
// wait for the promise to be resolved to acquire the IPDL actor.
mProcess = new RDDProcessHost(this);
if (!mProcess->Launch(extraArgs)) {
DestroyProcess();
return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
__func__);
}
return mProcess->LaunchPromise()->Then(
GetMainThreadSerialEventTarget(), __func__,
[aOtherProcess, this](bool) {
mRDDChild = mProcess->GetActor();
mProcessToken = mProcess->GetProcessToken();
// The subprocess is launched asynchronously, so we wait
// for a callback to acquire the IPDL actor.
mProcess = new RDDProcessHost(this);
if (!mProcess->Launch(extraArgs)) {
DestroyProcess();
return EnsureRDDPromise::CreateAndReject(
NS_ERROR_NOT_AVAILABLE, __func__);
}
ipc::Endpoint<PRemoteDecoderManagerChild> endpoint;
if (!EnsureRDDReady() || !CreateVideoBridge() ||
!CreateContentBridge(aOtherProcess, &endpoint)) {
return EnsureRDDPromise::CreateAndReject(
NS_ERROR_NOT_AVAILABLE, __func__);
}
return EnsureRDDPromise::CreateAndResolve(
std::move(endpoint), __func__);
});
// Flush any pref updates that happened during
// launch and weren't included in the blobs set
// up in LaunchRDDProcess.
for (const mozilla::dom::Pref& pref : mQueuedPrefs) {
Unused << NS_WARN_IF(!mRDDChild->SendPreferenceUpdate(pref));
}
mQueuedPrefs.Clear();
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::RDDProcessStatus, "Running"_ns);
ipc::Endpoint<PRemoteDecoderManagerChild> endpoint;
if (!CreateVideoBridge() ||
!CreateContentBridge(aOtherProcess, &endpoint)) {
mNumProcessAttempts++;
return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
__func__);
}
mNumProcessAttempts = 0;
return EnsureRDDPromise::CreateAndResolve(std::move(endpoint),
__func__);
},
[this](nsresult aError) {
mNumProcessAttempts++;
DestroyProcess();
return EnsureRDDPromise::CreateAndReject(aError, __func__);
});
});
}
bool RDDProcessManager::IsRDDProcessLaunching() {
@ -149,41 +188,6 @@ bool RDDProcessManager::IsRDDProcessLaunching() {
return !!mProcess && !mRDDChild;
}
bool RDDProcessManager::EnsureRDDReady() {
MOZ_ASSERT(NS_IsMainThread());
if (mProcess && !mProcess->IsConnected() && !mProcess->WaitForLaunch()) {
// If this fails, we should have fired OnProcessLaunchComplete and
// removed the process.
MOZ_ASSERT(!mProcess && !mRDDChild);
return false;
}
return true;
}
void RDDProcessManager::OnProcessLaunchComplete(RDDProcessHost* aHost) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mProcess && mProcess == aHost);
if (!mProcess->IsConnected()) {
DestroyProcess();
return;
}
mRDDChild = mProcess->GetActor();
mProcessToken = mProcess->GetProcessToken();
// Flush any pref updates that happened during launch and weren't
// included in the blobs set up in LaunchRDDProcess.
for (const mozilla::dom::Pref& pref : mQueuedPrefs) {
Unused << NS_WARN_IF(!mRDDChild->SendPreferenceUpdate(pref));
}
mQueuedPrefs.Clear();
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::RDDProcessStatus, "Running"_ns);
}
void RDDProcessManager::OnProcessUnexpectedShutdown(RDDProcessHost* aHost) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mProcess && mProcess == aHost);
@ -214,15 +218,6 @@ void RDDProcessManager::NotifyRemoteActorDestroyed(
void RDDProcessManager::CleanShutdown() { DestroyProcess(); }
void RDDProcessManager::KillProcess() {
MOZ_ASSERT(NS_IsMainThread());
if (!mProcess) {
return;
}
mProcess->KillProcess();
}
void RDDProcessManager::DestroyProcess() {
MOZ_ASSERT(NS_IsMainThread());
if (!mProcess) {
@ -345,7 +340,7 @@ class RDDMemoryReporter : public MemoryReportingProcess {
};
RefPtr<MemoryReportingProcess> RDDProcessManager::GetProcessMemoryReporter() {
if (!EnsureRDDReady()) {
if (!mProcess || !mProcess->IsConnected()) {
return nullptr;
}
return new RDDMemoryReporter();

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

@ -35,16 +35,12 @@ class RDDProcessManager final : public RDDProcessHost::Listener {
RefPtr<EnsureRDDPromise> EnsureRDDProcessAndCreateBridge(
base::ProcessId aOtherProcess);
void OnProcessLaunchComplete(RDDProcessHost* aHost) override;
void OnProcessUnexpectedShutdown(RDDProcessHost* aHost) override;
// Notify the RDDProcessManager that a top-level PRDD protocol has been
// terminated. This may be called from any thread.
void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken);
// Used for tests and diagnostics
void KillProcess();
// Returns -1 if there is no RDD process, or the platform pid for it.
base::ProcessId RDDProcessPid();
@ -63,10 +59,6 @@ class RDDProcessManager final : public RDDProcessHost::Listener {
private:
bool IsRDDProcessLaunching();
// Ensure that RDD-bound methods can be used. If no RDD process is being
// used, or one is launched and ready, this function returns immediately.
// Otherwise it blocks until the RDD process has finished launching.
bool EnsureRDDReady();
bool CreateVideoBridge();
// Called from our xpcom-shutdown observer.

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

@ -93,6 +93,9 @@ MediaController::MediaController(uint64_t aBrowsingContextId)
mSupportedActionsChangedListener = SupportedActionsChangedEvent().Connect(
AbstractThread::MainThread(), this,
&MediaController::HandleSupportedMediaSessionActionsChanged);
mPlaybackChangedListener = PlaybackChangedEvent().Connect(
AbstractThread::MainThread(), this,
&MediaController::HandleActualPlaybackStateChanged);
mPositionStateChangedListener = PositionChangedEvent().Connect(
AbstractThread::MainThread(), this,
&MediaController::HandlePositionStateChanged);
@ -231,6 +234,7 @@ void MediaController::Shutdown() {
Deactivate();
mShutdown = true;
mSupportedActionsChangedListener.DisconnectIfExists();
mPlaybackChangedListener.DisconnectIfExists();
mPositionStateChangedListener.DisconnectIfExists();
mMetadataChangedListener.DisconnectIfExists();
}
@ -517,16 +521,22 @@ void MediaController::HandleMetadataChanged(
}
void MediaController::DispatchAsyncEvent(const nsAString& aName) {
LOG("Dispatch event %s", NS_ConvertUTF16toUTF8(aName).get());
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, aName, CanBubble::eYes);
asyncDispatcher->PostDOMEvent();
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
event->InitEvent(aName, false, false);
event->SetTrusted(true);
DispatchAsyncEvent(event);
}
void MediaController::DispatchAsyncEvent(Event* aEvent) {
MOZ_ASSERT(aEvent);
nsAutoString eventType;
aEvent->GetType(eventType);
if (!mIsActive && !eventType.EqualsLiteral("deactivated")) {
LOG("Only 'deactivated' can be dispatched on a deactivated controller, not "
"'%s'",
NS_ConvertUTF16toUTF8(eventType).get());
return;
}
LOG("Dispatch event %s", NS_ConvertUTF16toUTF8(eventType).get());
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, aEvent);

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

@ -152,7 +152,7 @@ class MediaController final : public DOMEventTargetHelper,
private:
~MediaController();
void HandleActualPlaybackStateChanged() override;
void HandleActualPlaybackStateChanged();
void UpdateMediaControlActionToContentMediaIfNeeded(
const MediaControlAction& aAction);
void HandleSupportedMediaSessionActionsChanged(
@ -194,6 +194,7 @@ class MediaController final : public DOMEventTargetHelper,
MediaEventListener mSupportedActionsChangedListener;
MediaEventProducer<nsTArray<MediaControlKey>> mSupportedKeysChangedEvent;
MediaEventListener mPlaybackChangedListener;
MediaEventListener mPositionStateChangedListener;
MediaEventListener mMetadataChangedListener;

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

@ -318,7 +318,6 @@ void MediaStatusManager::UpdateActualPlaybackState() {
mActualPlaybackState = newState;
LOG("UpdateActualPlaybackState : '%s'",
ToMediaSessionPlaybackStateStr(mActualPlaybackState));
HandleActualPlaybackStateChanged();
mPlaybackStateChangedEvent.Notify(mActualPlaybackState);
}

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

@ -198,7 +198,6 @@ class MediaStatusManager : public IMediaInfoUpdater {
protected:
~MediaStatusManager() = default;
virtual void HandleActualPlaybackStateChanged() = 0;
// This event would be notified when the active media session changes its
// supported actions.

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

@ -31,12 +31,14 @@ support-files =
[browser_media_control_keys_event.js]
[browser_media_control_main_controller.js]
[browser_media_control_non_eligible_media.js]
skip-if = verify && (os == 'mac') # bug 1673509
[browser_media_control_playback_state.js]
[browser_media_control_position_state.js]
[browser_media_control_seekto.js]
[browser_media_control_supported_keys.js]
[browser_media_control_stop_timer.js]
[browser_nosrc_and_error_media.js]
skip-if = verify && (os == 'mac') # bug 1673509
[browser_seek_captured_audio.js]
[browser_stop_control_after_media_reaches_to_end.js]
[browser_suspend_inactive_tab.js]

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

@ -18,11 +18,11 @@ add_task(async function testDisableAudioFocusManagement() {
await switchAudioFocusManagerment(false);
info(`open audible autoplay media in tab1`);
const tab1 = await createTabAndLoad(PAGE_AUDIBLE);
const tab1 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab1, testVideoId);
info(`open same page on another tab, which shouldn't cause audio competing`);
const tab2 = await createTabAndLoad(PAGE_AUDIBLE);
const tab2 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab2, testVideoId);
info(`media in tab1 should be playing still`);
@ -36,11 +36,11 @@ add_task(async function testEnableAudioFocusManagement() {
await switchAudioFocusManagerment(true);
info(`open audible autoplay media in tab1`);
const tab1 = await createTabAndLoad(PAGE_AUDIBLE);
const tab1 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab1, testVideoId);
info(`open same page on another tab, which should cause audio competing`);
const tab2 = await createTabAndLoad(PAGE_AUDIBLE);
const tab2 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab2, testVideoId);
info(`media in tab1 should be stopped`);
@ -54,11 +54,11 @@ add_task(async function testCheckAudioCompetingMultipleTimes() {
await switchAudioFocusManagerment(true);
info(`open audible autoplay media in tab1`);
const tab1 = await createTabAndLoad(PAGE_AUDIBLE);
const tab1 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab1, testVideoId);
info(`open same page on another tab, which should cause audio competing`);
const tab2 = await createTabAndLoad(PAGE_AUDIBLE);
const tab2 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab2, testVideoId);
info(`media in tab1 should be stopped`);
@ -84,13 +84,15 @@ add_task(async function testMutedMediaWontInvolveAudioCompeting() {
await switchAudioFocusManagerment(true);
info(`open audible autoplay media in tab1`);
const tab1 = await createTabAndLoad(PAGE_AUDIBLE);
const tab1 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab1, testVideoId);
info(
`open inaudible media page on another tab, which shouldn't cause audio competing`
);
const tab2 = await createTabAndLoad(PAGE_INAUDIBLE);
const tab2 = await createLoadedTabWrapper(PAGE_INAUDIBLE, {
needCheck: false,
});
await checkOrWaitUntilMediaStartedPlaying(tab2, testVideoId);
info(`media in tab1 should be playing still`);
@ -99,7 +101,7 @@ add_task(async function testMutedMediaWontInvolveAudioCompeting() {
info(
`open audible media page on the third tab, which should cause audio competing`
);
const tab3 = await createTabAndLoad(PAGE_AUDIBLE);
const tab3 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab3, testVideoId);
info(`media in tab1 should be stopped`);
@ -116,17 +118,17 @@ add_task(async function testStopMultipleTabsWhenSwitchingPrefDynamically() {
await switchAudioFocusManagerment(false);
info(`open audible autoplay media in tab1`);
const tab1 = await createTabAndLoad(PAGE_AUDIBLE);
const tab1 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab1, testVideoId);
info(`open same page on another tab, which shouldn't cause audio competing`);
const tab2 = await createTabAndLoad(PAGE_AUDIBLE);
const tab2 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab2, testVideoId);
await switchAudioFocusManagerment(true);
info(`open same page on the third tab, which should cause audio competing`);
const tab3 = await createTabAndLoad(PAGE_AUDIBLE);
const tab3 = await createLoadedTabWrapper(PAGE_AUDIBLE, { needCheck: false });
await checkOrWaitUntilMediaStartedPlaying(tab3, testVideoId);
info(`media in tab1 and tab2 should be stopped`);
@ -171,7 +173,7 @@ async function playMedia(tab) {
async function clearTabsAndResetPref(tabs) {
info(`clear tabs and reset pref`);
for (let tab of tabs) {
BrowserTestUtils.removeTab(tab);
await tab.close();
}
await switchAudioFocusManagerment(false);
}

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

@ -14,7 +14,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testSetPositionState() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
info(`play video1 (audible) and video2 (inaudible)`);
await playBothAudibleAndInaudibleMedia(tab);
@ -37,7 +37,7 @@ add_task(async function testSetPositionState() {
});
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -30,53 +30,50 @@ add_task(async function triggerDefaultActionHandler() {
// Default handler should be triggered no matter if media session exists or not.
const kCreateMediaSession = [true, false];
for (const shouldCreateSession of kCreateMediaSession) {
const kActions = ["play", "pause", "stop"];
for (const action of kActions) {
info(`test for '${action}', shouldCreateSession=${shouldCreateSession}`);
info(`open page and start media`);
const tab = await createTabAndLoad(PAGE_URL);
await playMedia(tab, videoId);
info(`open page and start media`);
const tab = await createLoadedTabWrapper(PAGE_URL);
await playMedia(tab, videoId);
if (shouldCreateSession) {
info(
`media has started, so created session should become active session`
);
await Promise.all([
waitUntilActiveMediaSessionChanged(),
createMediaSession(tab),
]);
}
if (action == "play") {
info(`pause media first in order to test 'play'`);
await pauseMedia(tab, videoId);
info(`press '${action}' should trigger default action handler`);
await simulateMediaAction(tab, action);
info(`default action handler should resume media`);
await checkOrWaitUntilMediaPlays(tab, { videoId });
} else {
info(`press '${action}' should trigger default action handler`);
await simulateMediaAction(tab, action);
info(`default action handler should pause media`);
await checkOrWaitUntilMediaPauses(tab, { videoId });
}
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
if (shouldCreateSession) {
info(
`media has started, so created session should become active session`
);
await Promise.all([
waitUntilActiveMediaSessionChanged(),
createMediaSession(tab),
]);
}
info(`test 'pause' action`);
await simulateMediaAction(tab, "pause");
info(`default action handler should pause media`);
await checkOrWaitUntilMediaPauses(tab, { videoId });
info(`test 'play' action`);
await simulateMediaAction(tab, "play");
info(`default action handler should resume media`);
await checkOrWaitUntilMediaPlays(tab, { videoId });
info(`test 'stop' action`);
await simulateMediaAction(tab, "stop");
info(`default action handler should pause media`);
await checkOrWaitUntilMediaPauses(tab, { videoId });
info(`remove tab`);
await tab.close();
}
});
add_task(async function triggerNonDefaultHandlerWhenSetCustomizedHandler() {
info(`open page and start media`);
const tab = await createLoadedTabWrapper(PAGE_URL);
await startMedia(tab, { videoId });
const kActions = ["play", "pause", "stop"];
for (const action of kActions) {
info(`open page and start media`);
const tab = await createTabAndLoad(PAGE_URL);
await startMedia(tab, { videoId });
info(`set action handler for '${action}'`);
await setActionHandler(tab, action);
@ -86,10 +83,10 @@ add_task(async function triggerNonDefaultHandlerWhenSetCustomizedHandler() {
info(`action handler doesn't pause media, media should keep playing`);
await checkOrWaitUntilMediaPlays(tab, { videoId });
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
}
info(`remove tab`);
await tab.close();
});
add_task(
@ -99,7 +96,7 @@ add_task(
const kActions = ["play", "pause", "stop"];
for (const action of kActions) {
info(`open page and load iframe`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
const frameId = "iframe";
await loadIframe(tab, frameId, url);
@ -145,7 +142,7 @@ add_task(
}
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
}
}
}
@ -153,7 +150,7 @@ add_task(
add_task(async function onlyResumeActiveMediaSession() {
info(`open page and load iframes`);
const tab = await createTabAndLoad(PAGE2_URL);
const tab = await createLoadedTabWrapper(PAGE2_URL);
const frame1Id = "frame1";
const frame2Id = "frame2";
await loadIframe(tab, frame1Id, CORS_IFRAME_URL);
@ -186,7 +183,7 @@ add_task(async function onlyResumeActiveMediaSession() {
await checkOrWaitUntilMediaPlays(tab, { frameId: frame2Id });
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -30,7 +30,7 @@ add_task(async function testAudioFocusChangesAmongMultipleFrames() {
* frame doesn't use media session, the current metadata would be the default
* metadata.
*/
const tab = await createTabAndLoad(mainPageURL);
const tab = await createLoadedTabWrapper(mainPageURL);
await playAndWaitUntilMetadataChanged(tab);
await isGivenTabUsingDefaultMetadata(tab);
@ -55,7 +55,7 @@ add_task(async function testAudioFocusChangesAmongMultipleFrames() {
/**
* Remove tab and end test.
*/
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testAudioFocusChangesAfterPausingAudioFocusOwner() {
@ -65,7 +65,7 @@ add_task(async function testAudioFocusChangesAfterPausingAudioFocusOwner() {
* frame doesn't use media session, the current metadata would be the default
* metadata.
*/
const tab = await createTabAndLoad(mainPageURL);
const tab = await createLoadedTabWrapper(mainPageURL);
await playAndWaitUntilMetadataChanged(tab);
await isGivenTabUsingDefaultMetadata(tab);
@ -88,7 +88,7 @@ add_task(async function testAudioFocusChangesAfterPausingAudioFocusOwner() {
/**
* Remove tab and end test.
*/
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testAudioFocusUnchangesAfterPausingAudioFocusOwner() {
@ -98,7 +98,7 @@ add_task(async function testAudioFocusUnchangesAfterPausingAudioFocusOwner() {
* frame doesn't use media session, the current metadata would be the default
* metadata.
*/
const tab = await createTabAndLoad(mainPageURL);
const tab = await createLoadedTabWrapper(mainPageURL);
await playAndWaitUntilMetadataChanged(tab);
await isGivenTabUsingDefaultMetadata(tab);
@ -122,7 +122,7 @@ add_task(async function testAudioFocusUnchangesAfterPausingAudioFocusOwner() {
/**
* Remove tab and end test.
*/
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(
@ -133,7 +133,7 @@ add_task(
* main frame doesn't use media session, the current metadata would be the
* default metadata.
*/
const tab = await createTabAndLoad(mainPageURL);
const tab = await createLoadedTabWrapper(mainPageURL);
await playAndWaitUntilMetadataChanged(tab);
await isGivenTabUsingDefaultMetadata(tab);
@ -159,7 +159,7 @@ add_task(
/**
* Remove tab and end test.
*/
await BrowserTestUtils.removeTab(tab);
await tab.close();
}
);
@ -169,7 +169,7 @@ add_task(
* Play media for frame1, so frame1 owns the audio focus and frame1's metadata
* should be displayed.
*/
const tab = await createTabAndLoad(mainPageURL);
const tab = await createLoadedTabWrapper(mainPageURL);
await loadPageForFrame(tab, frame1, frameURL);
let metadataFrame1 = await setMetadataAndGetReturnResult(tab, frame1);
await playAndWaitUntilMetadataChanged(tab, frame1);
@ -197,7 +197,7 @@ add_task(
/**
* Remove tab and end test.
*/
await BrowserTestUtils.removeTab(tab);
await tab.close();
}
);
@ -208,7 +208,7 @@ add_task(async function testNoAudioFocusAfterRemovingAudioFocusOwner() {
* frame doesn't use media session, the current metadata would be the default
* metadata.
*/
const tab = await createTabAndLoad(mainPageURL);
const tab = await createLoadedTabWrapper(mainPageURL);
await playAndWaitUntilMetadataChanged(tab);
await isGivenTabUsingDefaultMetadata(tab);
@ -236,7 +236,7 @@ add_task(async function testNoAudioFocusAfterRemovingAudioFocusOwner() {
/**
* Remove tab and end test.
*/
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -27,7 +27,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testMediaEntersPIPMode() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`trigger PIP mode`);
const winPIP = await triggerPictureInPicture(tab.linkedBrowser, testVideoId);
@ -38,12 +38,12 @@ add_task(async function testMediaEntersPIPMode() {
info(`remove tab`);
await BrowserTestUtils.closeWindow(winPIP);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testMutedMediaEntersPIPMode() {
info(`open media page and mute video`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
await muteMedia(tab, testVideoId);
info(`trigger PIP mode`);
@ -55,12 +55,12 @@ add_task(async function testMutedMediaEntersPIPMode() {
info(`remove tab`);
await BrowserTestUtils.closeWindow(winPIP);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testMediaEntersFullScreen() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`make video fullscreen`);
await enableFullScreen(tab, testVideoId);
@ -70,12 +70,12 @@ add_task(async function testMediaEntersFullScreen() {
await checkOrWaitUntilMediaStartedPlaying(tab, testVideoId);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testMutedMediaEntersFullScreen() {
info(`open media page and mute video`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
await muteMedia(tab, testVideoId);
info(`make video fullscreen`);
@ -86,12 +86,16 @@ add_task(async function testMutedMediaEntersFullScreen() {
await checkOrWaitUntilMediaStartedPlaying(tab, testVideoId);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testNonMediaEntersFullScreen() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
info(`open media page which won't have an activated controller`);
// As we won't activate controller in this test case, not need to
// check controller's status.
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY, {
needCheck: false,
});
info(`make non-media element fullscreen`);
const nonMediaElementId = "image";
@ -104,12 +108,12 @@ add_task(async function testNonMediaEntersFullScreen() {
await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testMediaInIframeEntersFullScreen() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`make video in iframe fullscreen`);
await enableMediaFullScreenInIframe(tab, testVideoId);
@ -127,7 +131,7 @@ add_task(async function testMediaInIframeEntersFullScreen() {
await removeIframeFromDocument(tab);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -15,7 +15,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testAudibleCapturedMedia() {
info(`open new non autoplay media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY_MEDIA);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY_MEDIA);
info(`capture audio and start playing`);
await captureAudio(tab, testVideoId);
@ -26,7 +26,7 @@ add_task(async function testAudibleCapturedMedia() {
await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -15,52 +15,48 @@ add_task(async function setupTestingPref() {
add_task(async function testPlayPauseAndStop() {
info(`open page and start media`);
const tab = await createTabAndLoad(PAGE);
const tab = await createLoadedTabWrapper(PAGE);
await playMedia(tab, testVideoId);
info(`pressing 'pause' key`);
await generateMediaControlKey("pause");
MediaControlService.generateMediaControlKey("pause");
await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId);
info(`pressing 'play' key`);
await generateMediaControlKey("play");
MediaControlService.generateMediaControlKey("play");
await checkOrWaitUntilMediaStartedPlaying(tab, testVideoId);
info(`pressing 'stop' key`);
await generateMediaControlKey("stop");
MediaControlService.generateMediaControlKey("stop");
await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId);
info(`we have stop controlling media, pressing 'play' won't resume media`);
// Not expect playback state change, so using ChromeUtils's method directly.
info(`Has stopped controlling media, pressing 'play' won't resume it`);
MediaControlService.generateMediaControlKey("play");
await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testPlayPause() {
info(`open page and start media`);
const tab = await createTabAndLoad(PAGE);
const tab = await createLoadedTabWrapper(PAGE);
await playMedia(tab, testVideoId);
info(`pressing 'playPause' key, media should stop`);
await generateMediaControlKey("playpause");
await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId);
MediaControlService.generateMediaControlKey("playpause");
await Promise.all([
new Promise(r => (tab.controller.onplaybackstatechange = r)),
checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId),
]);
info(`pressing 'playPause' key, media should start`);
await generateMediaControlKey("playpause");
await checkOrWaitUntilMediaStartedPlaying(tab, testVideoId);
MediaControlService.generateMediaControlKey("playpause");
await Promise.all([
new Promise(r => (tab.controller.onplaybackstatechange = r)),
checkOrWaitUntilMediaStartedPlaying(tab, testVideoId),
]);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**
* The following are helper functions.
*/
function generateMediaControlKey(event) {
const playbackStateChanged = waitUntilDisplayedPlaybackChanged();
MediaControlService.generateMediaControlKey(event);
return playbackStateChanged;
}

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

@ -39,9 +39,9 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testDeterminingMainController() {
info(`open three different tabs`);
const tab0 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab1 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab2 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab0 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
const tab1 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
const tab2 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
/**
* part1 : [] -> [tab0] -> [tab0, tab1] -> [tab0, tab1, tab2]
@ -89,35 +89,26 @@ add_task(async function testDeterminingMainController() {
*/
info(`# [tab2, tab1, tab0] -> [tab2, tab1] -> [tab2] -> [] #`);
info(`remove tab0 and wait until main controller changes`);
await Promise.all([
waitUntilMainMediaControllerChanged(),
BrowserTestUtils.removeTab(tab0),
]);
await Promise.all([waitUntilMainMediaControllerChanged(), tab0.close()]);
info(`currrent metadata should be equal to tab1's metadata`);
await isCurrentMetadataEqualTo(tab1.metadata);
info(`remove tab1 and wait until main controller changes`);
await Promise.all([
waitUntilMainMediaControllerChanged(),
BrowserTestUtils.removeTab(tab1),
]);
await Promise.all([waitUntilMainMediaControllerChanged(), tab1.close()]);
info(`currrent metadata should be equal to tab2's metadata`);
await isCurrentMetadataEqualTo(tab2.metadata);
info(`remove tab2 and wait until main controller changes`);
await Promise.all([
waitUntilMainMediaControllerChanged(),
BrowserTestUtils.removeTab(tab2),
]);
await Promise.all([waitUntilMainMediaControllerChanged(), tab2.close()]);
isCurrentMetadataEmpty();
});
add_task(async function testPIPControllerWontBeReplacedByNormalController() {
info(`open two different tabs`);
const tab0 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab1 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab0 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
const tab1 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`set different metadata for each tab`);
await setMediaMetadataForTabs([tab0, tab1]);
@ -139,27 +130,21 @@ add_task(async function testPIPControllerWontBeReplacedByNormalController() {
info(`remove tab0 and wait until main controller changes`);
await BrowserTestUtils.closeWindow(winPIP);
await Promise.all([
waitUntilMainMediaControllerChanged(),
BrowserTestUtils.removeTab(tab0),
]);
await Promise.all([waitUntilMainMediaControllerChanged(), tab0.close()]);
info(`currrent metadata should be equal to tab1's metadata`);
await isCurrentMetadataEqualTo(tab1.metadata);
info(`remove tab1 and wait until main controller changes`);
await Promise.all([
waitUntilMainMediaControllerChanged(),
BrowserTestUtils.removeTab(tab1),
]);
await Promise.all([waitUntilMainMediaControllerChanged(), tab1.close()]);
isCurrentMetadataEmpty();
});
add_task(
async function testFullscreenControllerWontBeReplacedByNormalController() {
info(`open two different tabs`);
const tab0 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab1 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab0 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
const tab1 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`set different metadata for each tab`);
await setMediaMetadataForTabs([tab0, tab1]);
@ -167,7 +152,7 @@ add_task(
info(`start media for tab0, main controller should become tab0`);
await makeTabBecomeMainController(tab0);
info(`currrent metadata should be equal to tab0's metadata`);
info(`current metadata should be equal to tab0's metadata`);
await isCurrentMetadataEqualTo(tab0.metadata);
info(`video in tab0 enters fullscreen`);
@ -183,16 +168,15 @@ add_task(
await isCurrentMetadataEqualTo(tab0.metadata);
info(`remove tabs`);
await BrowserTestUtils.removeTab(tab0);
await BrowserTestUtils.removeTab(tab1);
await Promise.all([tab0.close(), tab1.close()]);
}
);
add_task(async function testFullscreenAndPIPControllers() {
info(`open three different tabs`);
const tab0 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab1 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab2 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab0 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
const tab1 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
const tab2 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`set different metadata for each tab`);
await setMediaMetadataForTabs([tab0, tab1, tab2]);
@ -240,10 +224,7 @@ add_task(async function testFullscreenAndPIPControllers() {
*/
info(`remove tab1 and wait until main controller changes`);
await BrowserTestUtils.closeWindow(winPIP);
await Promise.all([
waitUntilMainMediaControllerChanged(),
BrowserTestUtils.removeTab(tab1),
]);
await Promise.all([waitUntilMainMediaControllerChanged(), tab1.close()]);
info(`currrent metadata should be equal to tab0's metadata`);
await isCurrentMetadataEqualTo(tab0.metadata);
@ -252,10 +233,7 @@ add_task(async function testFullscreenAndPIPControllers() {
* Current controller list : [tab2]
*/
info(`remove tab0 and wait until main controller changes`);
await Promise.all([
waitUntilMainMediaControllerChanged(),
BrowserTestUtils.removeTab(tab0),
]);
await Promise.all([waitUntilMainMediaControllerChanged(), tab0.close()]);
info(`currrent metadata should be equal to tab0's metadata`);
await isCurrentMetadataEqualTo(tab2.metadata);
@ -264,10 +242,7 @@ add_task(async function testFullscreenAndPIPControllers() {
* Current controller list : []
*/
info(`remove tab2 and wait until main controller changes`);
await Promise.all([
waitUntilMainMediaControllerChanged(),
BrowserTestUtils.removeTab(tab2),
]);
await Promise.all([waitUntilMainMediaControllerChanged(), tab2.close()]);
isCurrentMetadataEmpty();
});
@ -341,7 +316,7 @@ function playMediaAndWaitUntilRegisteringController(tab, elementId) {
async function switchTabToForegroundAndEnableFullScreen(tab, elementId) {
// Fullscreen can only be allowed to enter from a focus tab.
await BrowserTestUtils.switchTab(gBrowser, tab);
await BrowserTestUtils.switchTab(gBrowser, tab.tabElement);
await SpecialPowers.spawn(tab.linkedBrowser, [elementId], elementId => {
return new Promise(r => {
const element = content.document.getElementById(elementId);

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

@ -40,7 +40,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testDefaultMetadataForPageWithoutMediaSession() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -49,13 +49,13 @@ add_task(async function testDefaultMetadataForPageWithoutMediaSession() {
await isGivenTabUsingDefaultMetadata(tab);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(
async function testDefaultMetadataForEmptyTitlePageWithoutMediaSession() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_EMPTY_TITLE_URL);
const tab = await createLoadedTabWrapper(PAGE_EMPTY_TITLE_URL);
info(`start media`);
await playMedia(tab, testVideoId);
@ -64,13 +64,13 @@ add_task(
await isGivenTabUsingDefaultMetadata(tab);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
}
);
add_task(async function testDefaultMetadataForPageUsingEmptyMetadata() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -87,12 +87,12 @@ add_task(async function testDefaultMetadataForPageUsingEmptyMetadata() {
await isGivenTabUsingDefaultMetadata(tab);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testDefaultMetadataForPageUsingNullMetadata() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -104,12 +104,12 @@ add_task(async function testDefaultMetadataForPageUsingNullMetadata() {
await isGivenTabUsingDefaultMetadata(tab);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testMetadataWithEmptyTitleAndArtwork() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -126,12 +126,12 @@ add_task(async function testMetadataWithEmptyTitleAndArtwork() {
await isGivenTabUsingDefaultMetadata(tab);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testMetadataWithoutTitleAndArtwork() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -146,17 +146,17 @@ add_task(async function testMetadataWithoutTitleAndArtwork() {
await isGivenTabUsingDefaultMetadata(tab);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testMetadataInPrivateBrowsing() {
info(`create a private window`);
const privateWindow = await BrowserTestUtils.openNewBrowserWindow({
const inputWindow = await BrowserTestUtils.openNewBrowserWindow({
private: true,
});
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY, privateWindow);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY, { inputWindow });
info(`start media`);
await playMedia(tab, testVideoId);
@ -174,15 +174,15 @@ add_task(async function testMetadataInPrivateBrowsing() {
await isGivenTabUsingDefaultMetadata(tab, { isPrivateBrowsing: true });
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
info(`close private window`);
await BrowserTestUtils.closeWindow(privateWindow);
await BrowserTestUtils.closeWindow(inputWindow);
});
add_task(async function testSetMetadataFromMediaSessionAPI() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -212,12 +212,14 @@ add_task(async function testSetMetadataFromMediaSessionAPI() {
await isCurrentMetadataEqualTo(metadata);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testSetMetadataBeforeMediaStarts() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY, {
needCheck: false,
});
info(`set metadata`);
let metadata = {
@ -226,18 +228,18 @@ add_task(async function testSetMetadataBeforeMediaStarts() {
album: "foo",
artwork: [{ src: "bar.jpg", sizes: "128x128", type: "image/jpeg" }],
};
await setMediaMetadata(tab, metadata);
await setMediaMetadata(tab, metadata, { notExpectChange: true });
info(`current media metadata should be empty before media starts`);
isCurrentMetadataEmpty();
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testSetMetadataAfterMediaPaused() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media in order to let this tab be controlled`);
await playMedia(tab, testVideoId);
@ -258,12 +260,12 @@ add_task(async function testSetMetadataAfterMediaPaused() {
await isCurrentMetadataEqualTo(metadata);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testSetMetadataAmongMultipleTabs() {
info(`open media page in tab1`);
const tab1 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab1 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media in tab1`);
await playMedia(tab1, testVideoId);
@ -281,7 +283,10 @@ add_task(async function testSetMetadataAmongMultipleTabs() {
await isCurrentMetadataEqualTo(metadata);
info(`open another page in tab2`);
const tab2 = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab2 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media in tab2`);
await playMedia(tab2, testVideoId);
info(`set metadata for tab2`);
metadata = {
@ -292,9 +297,6 @@ add_task(async function testSetMetadataAmongMultipleTabs() {
};
await setMediaMetadata(tab2, metadata);
info(`start media in tab2`);
await playMedia(tab2, testVideoId);
info(`current active metadata should become metadata from tab2`);
await isCurrentMetadataEqualTo(metadata);
@ -313,13 +315,12 @@ add_task(async function testSetMetadataAmongMultipleTabs() {
await isCurrentMetadataEqualTo(metadata);
info(`remove tabs`);
await BrowserTestUtils.removeTab(tab1);
await BrowserTestUtils.removeTab(tab2);
await Promise.all([tab1.close(), tab2.close()]);
});
add_task(async function testMetadataAfterTabNavigation() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -338,20 +339,21 @@ add_task(async function testMetadataAfterTabNavigation() {
info(`navigate tab to blank page`);
await Promise.all([
new Promise(r => (tab.controller.ondeactivated = r)),
BrowserTestUtils.loadURI(tab.linkedBrowser, "about:blank"),
waitUntilMainMediaControllerChanged(),
waitUntilDisplayedMetadataChanged(),
]);
info(`current media metadata should be reset`);
isCurrentMetadataEmpty();
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testUpdateDefaultMetadataWhenPageTitleChanges() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -375,20 +377,22 @@ add_task(async function testUpdateDefaultMetadataWhenPageTitleChanges() {
await isCurrentMetadataEqualTo(metadata);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**
* The following are helper functions.
*/
function setMediaMetadata(tab, metadata) {
function setMediaMetadata(tab, metadata, { notExpectChange } = {}) {
const controller = tab.linkedBrowser.browsingContext.mediaController;
const promise = SpecialPowers.spawn(tab.linkedBrowser, [metadata], data => {
content.navigator.mediaSession.metadata = new content.MediaMetadata(data);
});
const metadatachangePromise = notExpectChange
? Promise.resolve()
: new Promise(r => (controller.onmetadatachange = r));
return Promise.all([
promise,
new Promise(r => (controller.onmetadatachange = r)),
metadatachangePromise,
SpecialPowers.spawn(tab.linkedBrowser, [metadata], data => {
content.navigator.mediaSession.metadata = new content.MediaMetadata(data);
}),
]);
}

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

@ -8,8 +8,8 @@ Services.scriptloader.loadSubScript(
this
);
// Bug 1620686 - This test requests a lot of fullscreen and picture-in-picture
// for media elements which needs longer time to run.
// Bug 1673509 - This test requests a lot of fullscreen for media elements,
// which sometime Gecko would take longer time to fulfill.
requestLongerTimeout(2);
// This array contains the elements' id in `file_non_eligible_media.html`.
@ -36,63 +36,63 @@ add_task(async function setupTestingPref() {
});
});
add_task(async function testPlayPauseAndStop() {
for (const elementId of gNonEligibleElementIds) {
info(`- open new tab and start non eligible media ${elementId} -`);
const tab = await createTabAndLoad(PAGE_NON_ELIGIBLE_MEDIA);
await startNonEligibleMedia(tab, elementId);
add_task(
async function testNonAudibleMediaCantActivateControllerButAudibleMediaCan() {
for (const elementId of gNonEligibleElementIds) {
info(`open new tab with non eligible media elements`);
const tab = await createLoadedTabWrapper(PAGE_NON_ELIGIBLE_MEDIA, {
needCheck: couldElementBecomeEligible(elementId),
});
// Generate media control event should be postponed for a while to ensure
// that we didn't create any controller.
info(`- let media play for a while -`);
await checkIfMediaIsStillPlaying(tab, elementId);
info(`although media is playing but it won't activate controller`);
await Promise.all([
startNonEligibleMedia(tab, elementId),
checkIfMediaIsStillPlaying(tab, elementId),
]);
ok(!tab.controller.isActive, "controller is still inactive");
info(`- simulate pressing 'pause' media control key -`);
MediaControlService.generateMediaControlKey("pause");
if (couldElementBecomeEligible(elementId)) {
info(`make element ${elementId} audible would activate controller`);
await Promise.all([
makeElementEligible(tab, elementId),
checkOrWaitUntilControllerBecomeActive(tab),
]);
}
info(`- non eligible media won't be controlled by media control -`);
await checkIfMediaIsStillPlaying(tab, elementId);
if (couldElementBecomeEligible(elementId)) {
info(`- make element ${elementId} audible -`);
await makeElementEligible(tab, elementId);
info(`- simulate pressing 'pause' media control key -`);
MediaControlService.generateMediaControlKey("pause");
info(`- audible media should be controlled by media control -`);
await waitUntilMediaPaused(tab, elementId);
info(`remove tab`);
await tab.close();
}
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
}
});
);
/**
* Normally those media are not able to being controlled, however, once they
* enter fullsceen or Picture-in-Picture mode, then they can be controlled.
*/
add_task(async function testNonEligibleMediaEnterFullscreen() {
info(`open new tab with non eligible media elements`);
const tab = await createLoadedTabWrapper(PAGE_NON_ELIGIBLE_MEDIA);
for (const elementId of gNonEligibleElementIds) {
info(`- open new tab and start non eligible media ${elementId} -`);
const tab = await createTabAndLoad(PAGE_NON_ELIGIBLE_MEDIA);
await startNonEligibleMedia(tab, elementId);
info(`entering fullscreen should activate the media controller`);
await enableFullScreen(tab, elementId);
await enterFullScreen(tab, elementId);
await checkOrWaitUntilControllerBecomeActive(tab);
ok(true, `fullscreen ${elementId} media is able to being controlled`);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
info(`leave fullscreen`);
await leaveFullScreen(tab);
}
info(`remove tab`);
await tab.close();
});
add_task(async function testNonEligibleMediaEnterPIPMode() {
info(`open new tab with non eligible media elements`);
const tab = await createLoadedTabWrapper(PAGE_NON_ELIGIBLE_MEDIA);
for (const elementId of gNonEligibleElementIds) {
info(`- open new tab and start non eligible media ${elementId} -`);
const tab = await createTabAndLoad(PAGE_NON_ELIGIBLE_MEDIA);
await startNonEligibleMedia(tab, elementId);
info(`media entering PIP mode should activate the media controller`);
@ -100,10 +100,11 @@ add_task(async function testNonEligibleMediaEnterPIPMode() {
await checkOrWaitUntilControllerBecomeActive(tab);
ok(true, `PIP ${elementId} media is able to being controlled`);
info(`remove tab`);
info(`stop PIP mode`);
await BrowserTestUtils.closeWindow(winPIP);
await BrowserTestUtils.removeTab(tab);
}
info(`remove tab`);
await tab.close();
});
/**
@ -122,6 +123,7 @@ function startNonEligibleMedia(tab, elementId) {
const context = new content.AudioContext();
context.createMediaElementSource(video);
}
info(`start non eligible media ${Id}`);
return video.play();
});
}
@ -178,10 +180,10 @@ function waitUntilMediaPaused(tab, elementId) {
});
}
function enableFullScreen(tab, elementId) {
return SpecialPowers.spawn(tab.linkedBrowser, [elementId], elementId => {
function enterFullScreen(tab, elementId) {
return SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => {
return new Promise(r => {
const element = content.document.getElementById(elementId);
const element = content.document.getElementById(Id);
element.requestFullscreen();
element.onfullscreenchange = () => {
element.onfullscreenchange = null;
@ -195,3 +197,9 @@ function enableFullScreen(tab, elementId) {
});
});
}
function leaveFullScreen(tab) {
return SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
return content.document.exitFullscreen();
});
}

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

@ -23,18 +23,20 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testDefaultPlaybackStateBeforeAnyMediaStart() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY, {
needCheck: false,
});
info(`before media starts, playback state should be 'none'`);
await isActualPlaybackStateEqualTo(tab, "none");
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testGuessedPlaybackState() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(
`Now declared='none', guessed='playing', so actual playback state should be 'playing'`
@ -49,12 +51,12 @@ add_task(async function testGuessedPlaybackState() {
await isActualPlaybackStateEqualTo(tab, "paused");
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testBothGuessedAndDeclaredPlaybackState() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(
`Now declared='paused', guessed='playing', so actual playback state should be 'playing'`
@ -76,7 +78,7 @@ add_task(async function testBothGuessedAndDeclaredPlaybackState() {
await isActualPlaybackStateEqualTo(tab, "playing");
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -20,7 +20,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testSetPositionState() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
info(`start media`);
await playMedia(tab, testVideoId);
@ -44,12 +44,12 @@ add_task(async function testSetPositionState() {
});
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testSetPositionStateFromInactiveMediaSession() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
info(`start media`);
await playMedia(tab, testVideoId);
@ -81,7 +81,7 @@ add_task(async function testSetPositionStateFromInactiveMediaSession() {
);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -20,7 +20,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testSetPositionState() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
info(`start media`);
await playMedia(tab, testVideoId);
@ -44,7 +44,7 @@ add_task(async function testSetPositionState() {
});
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -31,7 +31,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testStopMediaControlAfterPausingMedia() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -40,12 +40,12 @@ add_task(async function testStopMediaControlAfterPausingMedia() {
await pauseMediaAndMediaControlShouldBeStopped(tab, testVideoId);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testNotToStopMediaControlForPIPVideo() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -62,15 +62,19 @@ add_task(async function testNotToStopMediaControlForPIPVideo() {
info(`remove tab`);
await BrowserTestUtils.closeWindow(winPIP);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**
* The following is helper function.
*/
function pauseMediaAndMediaControlShouldBeStopped(tab, testVideoId) {
function pauseMediaAndMediaControlShouldBeStopped(tab, elementId) {
// After pausing media, the stop timer would be triggered and stop the media
// control, which would reset the current main media controller.
const controllerChangedPromise = waitUntilMainMediaControllerChanged();
return Promise.all([pauseMedia(tab, testVideoId), controllerChangedPromise]);
// control.
return Promise.all([
new Promise(r => (tab.controller.ondeactivated = r)),
SpecialPowers.spawn(tab.linkedBrowser, [elementId], Id => {
content.document.getElementById(Id).pause();
}),
]);
}

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

@ -23,7 +23,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testDefaultSupportedKeys() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -32,12 +32,12 @@ add_task(async function testDefaultSupportedKeys() {
await supportedKeysShouldEqualTo(tab, sDefaultSupportedKeys);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testNoActionHandlerBeingSet() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -52,12 +52,12 @@ add_task(async function testNoActionHandlerBeingSet() {
await supportedKeysShouldEqualTo(tab, sDefaultSupportedKeys);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testSettingActionsWhichAreAlreadyDefaultKeys() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -72,12 +72,12 @@ add_task(async function testSettingActionsWhichAreAlreadyDefaultKeys() {
await supportedKeysShouldEqualTo(tab, sDefaultSupportedKeys);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testSettingActionsWhichAreNotDefaultKeys() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
info(`start media`);
await playMedia(tab, testVideoId);
@ -98,7 +98,7 @@ add_task(async function testSettingActionsWhichAreNotDefaultKeys() {
await supportedKeysShouldEqualTo(tab, expectedKeys);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -25,7 +25,7 @@ add_task(async function setupTestingPref() {
add_task(async function testNoSrcOrErrorMediaEntersPIPMode() {
for (let page of PAGES) {
info(`open media page ${page}`);
const tab = await createTabAndLoad(page);
const tab = await createLoadedTabWrapper(page, { needCheck: false });
info(`controller should always inactive`);
const controller = tab.linkedBrowser.browsingContext.mediaController;
@ -33,32 +33,28 @@ add_task(async function testNoSrcOrErrorMediaEntersPIPMode() {
ok(false, "should not get activated!");
};
info(`enter PIP mode several times and controller should keep inactive`);
for (let idx = 0; idx < 3; idx++) {
const winPIP = await triggerPictureInPicture(
tab.linkedBrowser,
testVideoId
);
await ensureMessageAndClosePiP(
tab.linkedBrowser,
testVideoId,
winPIP,
false
);
}
info(`enter and leave PIP mode which would not affect controller`);
const winPIP = await triggerPictureInPicture(
tab.linkedBrowser,
testVideoId
);
await ensureMessageAndClosePiP(
tab.linkedBrowser,
testVideoId,
winPIP,
false
);
ok(!controller.isActive, "controller is still inactive");
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await SimpleTest.promiseFocus(window);
await tab.close();
}
});
add_task(async function testNoSrcOrErrorMediaEntersFullscreen() {
for (let page of PAGES) {
info(`open media page ${page}`);
const tab = await createTabAndLoad(page);
const tab = await createLoadedTabWrapper(page, { needCheck: false });
info(`controller should always inactive`);
const controller = tab.linkedBrowser.browsingContext.mediaController;
@ -66,15 +62,13 @@ add_task(async function testNoSrcOrErrorMediaEntersFullscreen() {
ok(false, "should not get activated!");
};
info(`enter fullscreen several times and controller should keep inactive`);
info(`enter and leave fullscreen which would not affect controller`);
await ensureTabIsAlreadyFocused(tab);
for (let idx = 0; idx < 3; idx++) {
await enterAndLeaveFullScreen(tab, testVideoId);
}
await enterAndLeaveFullScreen(tab, testVideoId);
ok(!controller.isActive, "controller is still inactive");
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
}
});

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

@ -20,7 +20,7 @@ add_task(async function setupTestingPref() {
add_task(
async function testControllerWithActiveMediaSessionShouldStillBeActiveWhenNoControllableMediaPresents() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
info(`play media would activate controller and media session`);
await setupMediaSession(tab);
@ -28,7 +28,10 @@ add_task(
await checkOrWaitControllerBecomesActive(tab);
info(`remove playing media so we don't have any controllable media now`);
await removePlayingMedia(tab);
await Promise.all([
new Promise(r => (tab.controller.onplaybackstatechange = r)),
removePlayingMedia(tab),
]);
info(`despite that, controller should still be active`);
await checkOrWaitControllerBecomesActive(tab);
@ -37,27 +40,29 @@ add_task(
await ensureActiveMediaSessionReceivedMediaKey(tab);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
}
);
add_task(
async function testControllerWithoutActiveMediaSessionShouldBecomeInactiveWhenNoControllableMediaPresents() {
info(`open media page`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
info(`play media would activate controller`);
await playMedia(tab, testVideoId);
await checkOrWaitControllerBecomesActive(tab);
info(`remove playing media so we don't have any controllable media now`);
await removePlayingMedia(tab);
info(`without having media session, controller should be deactivated`);
await checkOrWaitControllerBecomesInactive(tab);
info(
`remove playing media so we don't have any controllable media, which would deactivate controller`
);
await Promise.all([
new Promise(r => (tab.controller.ondeactivated = r)),
removePlayingMedia(tab),
]);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
}
);
@ -88,13 +93,9 @@ async function ensureActiveMediaSessionReceivedMediaKey(tab) {
}
function removePlayingMedia(tab) {
const controller = tab.linkedBrowser.browsingContext.mediaController;
return Promise.all([
new Promise(r => (controller.onplaybackstatechange = r)),
SpecialPowers.spawn(tab.linkedBrowser, [testVideoId], Id => {
content.document.getElementById(Id).remove();
}),
]);
return SpecialPowers.spawn(tab.linkedBrowser, [testVideoId], Id => {
content.document.getElementById(Id).remove();
});
}
async function checkOrWaitControllerBecomesActive(tab) {
@ -104,11 +105,3 @@ async function checkOrWaitControllerBecomesActive(tab) {
}
ok(controller.isActive, `controller is active`);
}
async function checkOrWaitControllerBecomesInactive(tab) {
const controller = tab.linkedBrowser.browsingContext.mediaController;
if (controller.isActive) {
await new Promise(r => (controller.ondeactivated = r));
}
ok(!controller.isActive, `controller is inacitve`);
}

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

@ -13,7 +13,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testResumingLatestPausedMedias() {
info(`open media page and play all media`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
await playAllMedia(tab);
/**
@ -118,7 +118,7 @@ add_task(async function testResumingLatestPausedMedias() {
});
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -15,7 +15,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testSeekAudibleCapturedMedia() {
info(`open new non autoplay media page`);
const tab = await createTabAndLoad(PAGE_NON_AUTOPLAY_MEDIA);
const tab = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY_MEDIA);
info(`perform seek on the captured media before it starts`);
await captureAudio(tab, testVideoId);
@ -29,7 +29,7 @@ add_task(async function testSeekAudibleCapturedMedia() {
await checkOrWaitUntilMediaStoppedPlaying(tab, testVideoId);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -15,19 +15,19 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testControllerShouldStopAfterMediaReachesToTheEnd() {
info(`open media page and play media until the end`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
await Promise.all([
checkIfMediaControllerBecomeInactiveAfterMediaEnds(tab),
playMediaUntilItReachesToTheEnd(tab),
]);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testControllerWontStopAfterMediaReachesToTheEnd() {
info(`open media page and create media session`);
const tab = await createTabAndLoad(PAGE_URL);
const tab = await createLoadedTabWrapper(PAGE_URL);
await createMediaSession(tab);
info(`play media until the end`);
@ -37,7 +37,7 @@ add_task(async function testControllerWontStopAfterMediaReachesToTheEnd() {
await checkControllerIsActive(tab);
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
/**

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

@ -35,12 +35,12 @@ add_task(async function testInactiveTabWouldBeSuspended() {
await shouldTabStateEqualTo(tab, "suspended");
info(`remove tab`);
await BrowserTestUtils.removeTab(tab);
await tab.close();
});
add_task(async function testInactiveTabEverStartPlayingWontBeSuspended() {
info(`open tab1 and play media`);
const tab1 = await createTab(PAGE_NON_AUTOPLAY);
const tab1 = await createTab(PAGE_NON_AUTOPLAY, { needCheck: true });
await shouldTabStateEqualTo(tab1, "running");
await playMedia(tab1, VIDEO_ID);
@ -55,7 +55,7 @@ add_task(async function testInactiveTabEverStartPlayingWontBeSuspended() {
await shouldTabStateEqualTo(tab1, "running");
info(`open tab2 and play media`);
const tab2 = await createTab(PAGE_NON_AUTOPLAY);
const tab2 = await createTab(PAGE_NON_AUTOPLAY, { needCheck: true });
await shouldTabStateEqualTo(tab2, "running");
await playMedia(tab2, VIDEO_ID);
@ -65,15 +65,14 @@ add_task(async function testInactiveTabEverStartPlayingWontBeSuspended() {
await shouldTabStateEqualTo(tab1, "suspended");
info(`remove tabs`);
await BrowserTestUtils.removeTab(tab1);
await BrowserTestUtils.removeTab(tab2);
await Promise.all([tab1.close(), tab2.close()]);
});
/**
* The following are helper functions.
*/
async function createTab(url) {
const tab = await createTabAndLoad(url);
async function createTab(url, needCheck = false) {
const tab = await createLoadedTabWrapper(url, { needCheck });
await createStateObserver(tab);
return tab;
}

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

@ -1,19 +1,90 @@
/**
* Create a new tab and load the content from a given window (optional), if
* caller doesn't provide any window, then we would create tab in the current
* window.
*
* @param {string} url
* The URL that tab is going to load
* @param {window} inputWindow [optional]
* The window that uses to create a tab
* @return {tab}
* Return a loaded tab created from the given window
* This function would create a new foreround tab and load the url for it. In
* addition, instead of returning a tab element, we return a tab wrapper that
* helps us to automatically detect if the media controller of that tab
* dispatches the first (activated) and the last event (deactivated) correctly.
* @ param url
* the page url which tab would load
* @ param input window (optional)
* if it exists, the tab would be created from the input window. If not,
* then the tab would be created in current window.
* @ param needCheck (optional)
* it decides if we would perform the check for the first and last event
* on the media controller. It's default true.
*/
async function createTabAndLoad(url, inputWindow = null) {
async function createLoadedTabWrapper(
url,
{ inputWindow = window, needCheck = true } = {}
) {
class tabWrapper {
constructor(tab, needCheck) {
this._tab = tab;
this._controller = tab.linkedBrowser.browsingContext.mediaController;
this._firstEvent = "";
this._lastEvent = "";
this._events = [
"activated",
"deactivated",
"metadatachange",
"playbackstatechange",
"positionstatechange",
"supportedkeyschange",
];
this._needCheck = needCheck;
if (this._needCheck) {
this._registerAllEvents();
}
}
_registerAllEvents() {
for (let event of this._events) {
this._controller.addEventListener(event, this._handleEvent.bind(this));
}
}
_unregisterAllEvents() {
for (let event of this._events) {
this._controller.removeEventListener(
event,
this._handleEvent.bind(this)
);
}
}
_handleEvent(event) {
info(`handle event=${event.type}`);
if (this._firstEvent === "") {
this._firstEvent = event.type;
}
this._lastEvent = event.type;
}
get linkedBrowser() {
return this._tab.linkedBrowser;
}
get controller() {
return this._controller;
}
get tabElement() {
return this._tab;
}
async close() {
info(`wait until finishing close tab wrapper`);
const deactivationPromise = this._controller.isActive
? new Promise(r => (this._controller.ondeactivated = r))
: Promise.resolve();
BrowserTestUtils.removeTab(this._tab);
await deactivationPromise;
if (this._needCheck) {
is(this._firstEvent, "activated", "First event should be 'activated'");
is(
this._lastEvent,
"deactivated",
"Last event should be 'deactivated'"
);
this._unregisterAllEvents();
}
}
}
const browser = inputWindow ? inputWindow.gBrowser : window.gBrowser;
let tab = await BrowserTestUtils.openNewForegroundTab(browser, url);
return tab;
return new tabWrapper(tab, needCheck);
}
/**

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

@ -2,7 +2,6 @@
tags = mediacontrol
support-files =
file_media_session.html
../../mediacontrol/tests/browser/head.js
../../test/gizmo.mp4
[browser_active_mediasession_among_tabs.js]

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

@ -23,7 +23,7 @@ add_task(async function setupTestingPref() {
*/
add_task(async function testActiveSessionWhenClosingTab() {
info(`open tab1 and load media session test page`);
const tab1 = await createTabAndLoad(PAGE);
const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
await startMediaPlayback(tab1);
info(`pressing '${ACTION}' key`);
@ -33,7 +33,7 @@ add_task(async function testActiveSessionWhenClosingTab() {
await checkIfActionReceived(tab1, ACTION);
info(`open tab2 and load media session test page`);
const tab2 = await createTabAndLoad(PAGE);
const tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
await startMediaPlayback(tab2);
info(`pressing '${ACTION}' key`);
@ -65,7 +65,7 @@ add_task(async function testActiveSessionWhenClosingTab() {
*/
add_task(async function testActiveSessionWhenNavigatingTab() {
info(`open tab1 and load media session test page`);
const tab1 = await createTabAndLoad(PAGE);
const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
await startMediaPlayback(tab1);
info(`pressing '${ACTION}' key`);
@ -75,7 +75,7 @@ add_task(async function testActiveSessionWhenNavigatingTab() {
await checkIfActionReceived(tab1, ACTION);
info(`open tab2 and load media session test page`);
const tab2 = await createTabAndLoad(PAGE);
const tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
await startMediaPlayback(tab2);
info(`pressing '${ACTION}' key`);
@ -108,7 +108,7 @@ add_task(async function testActiveSessionWhenNavigatingTab() {
*/
add_task(async function testCreatingSessionWithoutPlayingMedia() {
info(`open tab1 and load media session test page`);
const tab1 = await createTabAndLoad(PAGE);
const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
await startMediaPlayback(tab1);
info(`pressing '${ACTION}' key`);
@ -118,7 +118,7 @@ add_task(async function testCreatingSessionWithoutPlayingMedia() {
await checkIfActionReceived(tab1, ACTION);
info(`open tab2 and load media session test page`);
const tab2 = await createTabAndLoad(PAGE);
const tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
info(`pressing '${ACTION}' key`);
MediaControlService.generateMediaControlKey(ACTION);

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

@ -12,28 +12,11 @@
#include "mozilla/CheckedInt.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/ProfilerMarkerTypes.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_dom.h"
#include "nsPrintfCString.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# define PROFILER_AUDIO_MARKER(tag, sample) \
do { \
uint64_t startTime = (sample)->mTime.ToMicroseconds(); \
uint64_t endTime = (sample)->GetEndTime().ToMicroseconds(); \
auto profilerTag = (tag); \
mOwnerThread->Dispatch(NS_NewRunnableFunction( \
"AudioSink:AddMarker", [profilerTag, startTime, endTime] { \
PROFILER_ADD_MARKER_WITH_PAYLOAD(profilerTag, MEDIA_PLAYBACK, \
MediaSampleMarkerPayload, \
(startTime, endTime)); \
})); \
} while (0)
#else
# define PROFILER_AUDIO_MARKER(tag, sample)
#endif
namespace mozilla {
extern LazyLogModule gMediaDecoderLog;
@ -271,7 +254,16 @@ UniquePtr<AudioStream::Chunk> AudioSink::PopFrames(uint32_t aFrames) {
SINK_LOG_V("playing audio at time=%" PRId64 " offset=%u length=%u",
mCurrentData->mTime.ToMicroseconds(),
mCurrentData->Frames() - mCursor->Available(), framesToPop);
PROFILER_AUDIO_MARKER("PlayAudio", mCurrentData);
#ifdef MOZ_GECKO_PROFILER
mOwnerThread->Dispatch(NS_NewRunnableFunction(
"AudioSink:AddMarker",
[startTime = mCurrentData->mTime.ToMicroseconds(),
endTime = mCurrentData->GetEndTime().ToMicroseconds()] {
PROFILER_MARKER("PlayAudio", MEDIA_PLAYBACK, {}, MediaSampleMarker,
startTime, endTime);
}));
#endif // MOZ_GECKO_PROFILER
UniquePtr<AudioStream::Chunk> chunk =
MakeUnique<Chunk>(mCurrentData, framesToPop, mCursor->Ptr());

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

@ -16,6 +16,7 @@
#include "VideoUtils.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/ProfilerMarkerTypes.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_media.h"
@ -29,15 +30,6 @@ extern mozilla::LazyLogModule gMediaDecoderLog;
#define VSINK_LOG_V(x, ...) \
MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, (FMT(x, ##__VA_ARGS__)))
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# define VSINK_ADD_PROFILER_MARKER(tag, startTime, endTime) \
PROFILER_ADD_MARKER_WITH_PAYLOAD( \
tag, MEDIA_PLAYBACK, MediaSampleMarkerPayload, (startTime, endTime))
#else
# define VSINK_ADD_PROFILER_MARKER(tag, startTime, endTime)
#endif
namespace mozilla {
using namespace mozilla::layers;
@ -463,8 +455,9 @@ void VideoSink::RenderVideoFrames(int32_t aMaxFrames, int64_t aClockTime,
frame->mTime.ToMicroseconds(), frame->mFrameID,
VideoQueue().GetSize());
if (!wasSent) {
VSINK_ADD_PROFILER_MARKER("PlayVideo", frame->mTime.ToMicroseconds(),
frame->GetEndTime().ToMicroseconds());
PROFILER_MARKER("PlayVideo", MEDIA_PLAYBACK, {}, MediaSampleMarker,
frame->mTime.ToMicroseconds(),
frame->GetEndTime().ToMicroseconds());
}
}
@ -503,8 +496,9 @@ void VideoSink::UpdateRenderedVideoFrames() {
VSINK_LOG_V("discarding video frame mTime=%" PRId64
" clock_time=%" PRId64,
frame->mTime.ToMicroseconds(), clockTime.ToMicroseconds());
VSINK_ADD_PROFILER_MARKER("DiscardVideo", frame->mTime.ToMicroseconds(),
frame->GetEndTime().ToMicroseconds());
PROFILER_MARKER("DiscardVideo", MEDIA_PLAYBACK, {}, MediaSampleMarker,
frame->mTime.ToMicroseconds(),
frame->GetEndTime().ToMicroseconds());
}
}

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

@ -27,6 +27,7 @@
#include "mozilla/WindowsVersion.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/mscom/EnsureMTA.h"
#include "mozilla/ProfilerMarkers.h"
#include "nsComponentManagerUtils.h"
#include "nsIXULRuntime.h"
#include "nsIXULRuntime.h" // for BrowserTabsRemoteAutostart
@ -36,28 +37,19 @@
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# define WFM_DECODER_MODULE_STATUS_MARKER(tag, text, markerTime) \
PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
(text, markerTime))
#else
# define WFM_DECODER_MODULE_STATUS_MARKER(tag, text, markerTime)
#endif
extern const GUID CLSID_WebmMfVpxDec;
namespace mozilla {
// Helper function to add a profile marker and log at the same time.
static void MOZ_FORMAT_PRINTF(2, 3)
WmfDeocderModuleMarkerAndLog(const char* aTag, const char* aFormat, ...) {
WmfDeocderModuleMarkerAndLog(const ProfilerString8View& aMarkerTag,
const char* aFormat, ...) {
va_list ap;
va_start(ap, aFormat);
const nsVprintfCString markerString(aFormat, ap);
va_end(ap);
WFM_DECODER_MODULE_STATUS_MARKER(aTag, markerString,
TimeStamp::NowUnfuzzed());
PROFILER_MARKER_TEXT(aMarkerTag, MEDIA_PLAYBACK, {}, markerString);
LOG("%s", markerString.get());
}

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

@ -9,6 +9,7 @@
#include "VideoUtils.h"
#include "WMFUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/Telemetry.h"
@ -16,15 +17,6 @@
#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# define WFM_MEDIA_DATA_DECODER_STATUS_MARKER(tag, text, markerTime) \
PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
(text, markerTime))
#else
# define WFM_MEDIA_DATA_DECODER_STATUS_MARKER(tag, text, markerTime)
#endif
namespace mozilla {
WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager)
@ -102,8 +94,7 @@ RefPtr<MediaDataDecoder::DecodePromise> WMFMediaDataDecoder::ProcessError(
"reason: %s",
GetDescriptionName().get(), aReason);
LOG(markerString.get());
WFM_MEDIA_DATA_DECODER_STATUS_MARKER("WMFDecoder Error", markerString,
TimeStamp::NowUnfuzzed());
PROFILER_MARKER_TEXT("WMFDecoder Error", MEDIA_PLAYBACK, {}, markerString);
// TODO: For the error DXGI_ERROR_DEVICE_RESET, we could return
// NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER to get the latest device. Maybe retry

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

@ -13,18 +13,10 @@
#include "MediaInfo.h"
#include "PDMFactory.h"
#include "VPXDecoder.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/TaskQueue.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# define MEDIA_CHANGE_MONITOR_STATUS_MARKER(tag, text, markerTime) \
PROFILER_ADD_MARKER_WITH_PAYLOAD(tag, MEDIA_PLAYBACK, TextMarkerPayload, \
(text, markerTime))
#else
# define MEDIA_CHANGE_MONITOR_STATUS_MARKER(tag, text, markerTime)
#endif
namespace mozilla {
// H264ChangeMonitor is used to ensure that only AVCC or AnnexB is fed to the
@ -98,11 +90,9 @@ class H264ChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
mPreviousExtraData = aSample->mExtraData;
UpdateConfigFromExtraData(extra_data);
MEDIA_CHANGE_MONITOR_STATUS_MARKER(
"H264 Stream Change",
"H264ChangeMonitor::CheckForChange has detected a change in the "
"stream and will request a new decoder"_ns,
TimeStamp::NowUnfuzzed());
PROFILER_MARKER_TEXT("H264 Stream Change", MEDIA_PLAYBACK, {},
"H264ChangeMonitor::CheckForChange has detected a "
"change in the stream and will request a new decoder");
return NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER;
}
@ -204,11 +194,10 @@ class VPXChangeMonitor : public MediaChangeMonitor::CodecChangeMonitor {
mCurrentConfig.SetImageRect(
gfx::IntRect(0, 0, info.mImage.width, info.mImage.height));
MEDIA_CHANGE_MONITOR_STATUS_MARKER(
"VPX Stream Change",
PROFILER_MARKER_TEXT(
"VPX Stream Change", MEDIA_PLAYBACK, {},
"VPXChangeMonitor::CheckForChange has detected a change in the "
"stream and will request a new decoder"_ns,
TimeStamp::NowUnfuzzed());
"stream and will request a new decoder");
rv = NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER;
}
mInfo = Some(info);
@ -755,5 +744,3 @@ void MediaChangeMonitor::FlushThenShutdownDecoder(
}
} // namespace mozilla
#undef MEDIA_CHANGE_MONITOR_STATUS_MARKER

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

@ -111,35 +111,43 @@ class RefcountableBase {
template <typename T>
class Refcountable : public T, public RefcountableBase {
public:
NS_METHOD_(MozExternalRefCountType) AddRef() {
return RefcountableBase::AddRef();
}
NS_METHOD_(MozExternalRefCountType) Release() {
return RefcountableBase::Release();
}
Refcountable<T>& operator=(T&& aOther) {
Refcountable& operator=(T&& aOther) {
T::operator=(std::move(aOther));
return *this;
}
Refcountable<T>& operator=(T& aOther) {
Refcountable& operator=(T& aOther) {
T::operator=(aOther);
return *this;
}
private:
~Refcountable<T>() = default;
};
template <typename T>
class Refcountable<UniquePtr<T>> : public UniquePtr<T> {
class Refcountable<UniquePtr<T>> : public UniquePtr<T>,
public RefcountableBase {
public:
explicit Refcountable<UniquePtr<T>>(T* aPtr) : UniquePtr<T>(aPtr) {}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
explicit Refcountable(T* aPtr) : UniquePtr<T>(aPtr) {}
};
template <>
class Refcountable<bool> : public RefcountableBase {
public:
explicit Refcountable(bool aValue) : mValue(aValue) {}
Refcountable& operator=(bool aOther) {
mValue = aOther;
return *this;
}
Refcountable& operator=(const Refcountable& aOther) {
mValue = aOther.mValue;
return *this;
}
explicit operator bool() const { return mValue; }
private:
~Refcountable<UniquePtr<T>>() = default;
bool mValue;
};
/* Async shutdown helpers

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

@ -28,10 +28,6 @@
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
#define PERFLOG(msg, ...) printf_stderr(msg, ##__VA_ARGS__)
namespace mozilla::dom {
@ -216,6 +212,53 @@ void Performance::ClearUserEntries(const Optional<nsAString>& aEntryName,
void Performance::ClearResourceTimings() { mResourceEntries.Clear(); }
#ifdef MOZ_GECKO_PROFILER
struct UserTimingMarker {
static constexpr Span<const char> MarkerTypeName() {
return MakeStringSpan("UserTiming");
}
static void StreamJSONMarkerData(
baseprofiler::SpliceableJSONWriter& aWriter,
const ProfilerString16View& aName, bool aIsMeasure,
const Maybe<ProfilerString16View>& aStartMark,
const Maybe<ProfilerString16View>& aEndMark) {
aWriter.StringProperty("name", NS_ConvertUTF16toUTF8(aName.Data()));
if (aIsMeasure) {
aWriter.StringProperty("entryType", "measure");
} else {
aWriter.StringProperty("entryType", "mark");
}
if (aStartMark.isSome()) {
aWriter.StringProperty("startMark",
NS_ConvertUTF16toUTF8(aStartMark->Data()));
} else {
aWriter.NullProperty("startMark");
}
if (aEndMark.isSome()) {
aWriter.StringProperty("endMark",
NS_ConvertUTF16toUTF8(aEndMark->Data()));
} else {
aWriter.NullProperty("endMark");
}
}
static MarkerSchema MarkerTypeDisplay() {
using MS = MarkerSchema;
MS schema{MS::Location::markerChart, MS::Location::markerTable};
schema.SetAllLabels("{marker.data.name}");
schema.AddStaticLabelValue("Marker", "UserTiming");
schema.AddKeyLabelFormat("entryType", "Entry Type", MS::Format::string);
schema.AddKeyLabelFormat("name", "Name", MS::Format::string);
schema.AddKeyLabelFormat("startMark", "Start Mark", MS::Format::string);
schema.AddKeyLabelFormat("endMark", "End Mark", MS::Format::string);
schema.AddStaticLabelValue("Description",
"UserTimingMeasure is created using the DOM API "
"performance.measure().");
return schema;
}
};
#endif
void Performance::Mark(const nsAString& aName, ErrorResult& aRv) {
// We add nothing when 'privacy.resistFingerprinting' is on.
if (nsContentUtils::ShouldResistFingerprinting()) {
@ -237,8 +280,9 @@ void Performance::Mark(const nsAString& aName, ErrorResult& aRv) {
if (GetOwner()) {
innerWindowId = Some(GetOwner()->WindowID());
}
PROFILER_ADD_MARKER_WITH_PAYLOAD("UserTiming", DOM, UserTimingMarkerPayload,
(aName, TimeStamp::Now(), innerWindowId));
profiler_add_marker("UserTiming", geckoprofiler::category::DOM,
MarkerInnerWindowId(innerWindowId), UserTimingMarker{},
aName, /* aIsMeasure */ false, Nothing{}, Nothing{});
}
#endif
}
@ -332,9 +376,11 @@ void Performance::Measure(const nsAString& aName,
if (GetOwner()) {
innerWindowId = Some(GetOwner()->WindowID());
}
PROFILER_ADD_MARKER_WITH_PAYLOAD("UserTiming", DOM, UserTimingMarkerPayload,
(aName, startMark, endMark, startTimeStamp,
endTimeStamp, innerWindowId));
profiler_add_marker("UserTiming", geckoprofiler::category::DOM,
{MarkerTiming::Interval(startTimeStamp, endTimeStamp),
MarkerInnerWindowId(innerWindowId)},
UserTimingMarker{}, aName, /* aIsMeasure */ true,
startMark, endMark);
}
#endif
}

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

@ -21,7 +21,9 @@ function coordinatesRelativeToWindow(aX, aY, aElement) {
};
}
var apzEnabled = Preferences.get("layers.async-pan-zoom.enabled", false);
var apzEnabled =
Services.appinfo.fissionAutostart ||
Preferences.get("layers.async-pan-zoom.enabled", false);
var pluginHideEnabled = Preferences.get(
"gfx.e10s.hide-plugins-for-scroll",
true

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

@ -24,6 +24,7 @@
#include "js/SourceText.h"
#include "js/Utility.h"
#include "xpcpublic.h"
#include "GeckoProfiler.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIContent.h"
#include "nsJSUtils.h"
@ -83,10 +84,6 @@
#include "nsIScriptError.h"
#include "nsIAsyncOutputStream.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
using JS::SourceText;
using mozilla::Telemetry::LABELS_DOM_SCRIPT_PRELOAD_RESULT;
@ -2180,7 +2177,7 @@ NotifyOffThreadScriptLoadCompletedRunnable::Run() {
#ifdef MOZ_GECKO_PROFILER
if (profiler_is_active()) {
const char* scriptSourceString;
ProfilerString8View scriptSourceString;
if (request->IsTextSource()) {
scriptSourceString = "ScriptCompileOffThread";
} else if (request->IsBinASTSource()) {
@ -2192,10 +2189,11 @@ NotifyOffThreadScriptLoadCompletedRunnable::Run() {
nsAutoCString profilerLabelString;
GetProfilerLabelForRequest(request, profilerLabelString);
PROFILER_ADD_MARKER_WITH_PAYLOAD(
scriptSourceString, JS, TextMarkerPayload,
(profilerLabelString, request->mOffThreadParseStartTime,
request->mOffThreadParseStopTime));
PROFILER_MARKER_TEXT(
scriptSourceString, JS,
MarkerTiming::Interval(request->mOffThreadParseStartTime,
request->mOffThreadParseStopTime),
profilerLabelString);
}
#endif

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

@ -2458,7 +2458,7 @@ nsresult XMLHttpRequestMainThread::CreateChannel() {
rv = httpChannel->SetRequestMethod(mRequestMethod);
NS_ENSURE_SUCCESS(rv, rv);
httpChannel->SetSource(profiler_get_backtrace());
httpChannel->SetSource(profiler_capture_backtrace());
// Set the initiator type
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
@ -3240,7 +3240,8 @@ void XMLHttpRequestMainThread::SetOriginStack(
mOriginStack = std::move(aOriginStack);
}
void XMLHttpRequestMainThread::SetSource(UniqueProfilerBacktrace aSource) {
void XMLHttpRequestMainThread::SetSource(
UniquePtr<ProfileChunkedBuffer> aSource) {
if (!mChannel) {
return;
}

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

@ -396,7 +396,7 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
void SetOriginStack(UniquePtr<SerializedStackHolder> aOriginStack);
void SetSource(UniqueProfilerBacktrace aSource);
void SetSource(UniquePtr<ProfileChunkedBuffer> aSource);
virtual uint16_t ErrorCode() const override {
return static_cast<uint16_t>(mErrorLoad);

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

@ -653,7 +653,7 @@ class OpenRunnable final : public WorkerThreadProxySyncRunnable {
// Remember the worker thread's stack when the XHR was opened for profiling
// purposes.
UniqueProfilerBacktrace mSource;
UniquePtr<ProfileChunkedBuffer> mSource;
public:
OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
@ -664,7 +664,7 @@ class OpenRunnable final : public WorkerThreadProxySyncRunnable {
XMLHttpRequestResponseType aResponseType,
const nsString& aMimeTypeOverride,
UniquePtr<SerializedStackHolder> aOriginStack,
UniqueProfilerBacktrace aSource = nullptr)
UniquePtr<ProfileChunkedBuffer> aSource = nullptr)
: WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
mMethod(aMethod),
mURL(aURL),
@ -1741,7 +1741,7 @@ void XMLHttpRequestWorker::Open(const nsACString& aMethod,
mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
mBackgroundRequest, mWithCredentials, mTimeout, mResponseType,
alsoOverrideMimeType ? mMimeTypeOverride : VoidString(), std::move(stack),
profiler_get_backtrace());
profiler_capture_backtrace());
++mProxy->mOpenCount;
runnable->Dispatch(Canceling, aRv);

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

@ -11,9 +11,6 @@
#include "GeckoProfiler.h"
#include "gfxUtils.h"
#include "nsThreadUtils.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
using namespace mozilla;
using namespace mozilla::gfx;
@ -84,12 +81,32 @@ void ProfilerScreenshots::SubmitScreenshot(
nullptr, &dataURL);
if (NS_SUCCEEDED(rv)) {
// Add a marker with the data URL.
AUTO_PROFILER_STATS(add_marker_with_ScreenshotPayload);
profiler_add_marker_for_thread(
sourceThread, JS::ProfilingCategoryPair::GRAPHICS,
"CompositorScreenshot",
ScreenshotPayload(timeStamp, std::move(dataURL), originalSize,
windowIdentifier));
struct ScreenshotMarker {
static constexpr mozilla::Span<const char> MarkerTypeName() {
return mozilla::MakeStringSpan("CompositorScreenshot");
}
static void StreamJSONMarkerData(
mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
const mozilla::ProfilerString8View& aScreenshotDataURL,
const mozilla::gfx::IntSize& aWindowSize,
uintptr_t aWindowIdentifier) {
aWriter.UniqueStringProperty("url", aScreenshotDataURL);
char hexWindowID[32];
SprintfLiteral(hexWindowID, "0x%" PRIXPTR, aWindowIdentifier);
aWriter.StringProperty("windowID", hexWindowID);
aWriter.DoubleProperty("windowWidth", aWindowSize.width);
aWriter.DoubleProperty("windowHeight", aWindowSize.height);
}
static mozilla::MarkerSchema MarkerTypeDisplay() {
return mozilla::MarkerSchema::SpecialFrontendLocation{};
}
};
profiler_add_marker(
"CompositorScreenshot", geckoprofiler::category::GRAPHICS,
{MarkerThreadId(sourceThread),
MarkerTiming::InstantAt(timeStamp)},
ScreenshotMarker{}, dataURL, originalSize, windowIdentifier);
}
}

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

@ -40,10 +40,6 @@
#include <vector>
#include "GeckoProfiler.h" // for GeckoProfiler
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h" // for LayerTranslationMarkerPayload
#endif
static mozilla::LazyLogModule sGfxCullLog("gfx.culling");
#define CULLING_LOG(...) MOZ_LOG(sGfxCullLog, LogLevel::Debug, (__VA_ARGS__))
@ -103,9 +99,37 @@ static void PrintUniformityInfo(Layer* aLayer) {
}
Point translation = transform.As2D().GetTranslation();
PROFILER_ADD_MARKER_WITH_PAYLOAD("LayerTranslation", GRAPHICS,
LayerTranslationMarkerPayload,
(aLayer, translation, TimeStamp::Now()));
// Contains the translation applied to a 2d layer so we can track the layer
// position at each frame.
struct LayerTranslationMarker {
static constexpr Span<const char> MarkerTypeName() {
return MakeStringSpan("LayerTranslation");
}
static void StreamJSONMarkerData(
baseprofiler::SpliceableJSONWriter& aWriter,
ProfileBufferRawPointer<layers::Layer> aLayer, gfx::Point aPoint) {
const size_t bufferSize = 32;
char buffer[bufferSize];
SprintfLiteral(buffer, "%p", aLayer.mRawPointer);
aWriter.StringProperty("layer", buffer);
aWriter.IntProperty("x", aPoint.x);
aWriter.IntProperty("y", aPoint.y);
}
static MarkerSchema MarkerTypeDisplay() {
using MS = MarkerSchema;
MS schema{MS::Location::markerChart, MS::Location::markerTable};
schema.AddKeyLabelFormat("layer", "Layer", MS::Format::string);
schema.AddKeyLabelFormat("x", "X", MS::Format::integer);
schema.AddKeyLabelFormat("y", "Y", MS::Format::integer);
return schema;
}
};
profiler_add_marker("LayerTranslation", geckoprofiler::category::GRAPHICS, {},
LayerTranslationMarker{},
WrapProfileBufferRawPointer(aLayer), translation);
#endif
}

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

@ -90,9 +90,6 @@
#include "mozilla/HalTypes.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
#include "mozilla/VsyncDispatcher.h"
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
# include "VsyncSource.h"
@ -2176,8 +2173,23 @@ already_AddRefed<IAPZCTreeManager> CompositorBridgeParent::GetAPZCTreeManager(
static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
if (profiler_thread_is_being_profiled()) {
PROFILER_ADD_MARKER_WITH_PAYLOAD("VsyncTimestamp", GRAPHICS,
VsyncMarkerPayload, (aVsyncTimestamp));
// Tracks when a vsync occurs according to the HardwareComposer.
struct VsyncMarker {
static constexpr mozilla::Span<const char> MarkerTypeName() {
return mozilla::MakeStringSpan("VsyncTimestamp");
}
static void StreamJSONMarkerData(
baseprofiler::SpliceableJSONWriter& aWriter) {}
static MarkerSchema MarkerTypeDisplay() {
using MS = MarkerSchema;
MS schema{MS::Location::markerChart, MS::Location::markerTable};
// Nothing outside the defaults.
return schema;
}
};
profiler_add_marker("VsyncTimestamp", geckoprofiler::category::GRAPHICS,
MarkerTiming::InstantAt(aVsyncTimestamp),
VsyncMarker{});
}
}
#endif
@ -2742,42 +2754,23 @@ int32_t RecordContentFrameTime(
#ifdef MOZ_GECKO_PROFILER
if (profiler_can_accept_markers()) {
class ContentFramePayload : public ProfilerMarkerPayload {
public:
ContentFramePayload(const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime)
: ProfilerMarkerPayload(aStartTime, aEndTime) {}
mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes()
const override {
return CommonPropsTagAndSerializationBytes();
struct ContentFrameMarker {
static constexpr Span<const char> MarkerTypeName() {
return MakeStringSpan("CONTENT_FRAME_TIME");
}
void SerializeTagAndPayload(
mozilla::ProfileBufferEntryWriter& aEntryWriter) const override {
static const DeserializerTag tag = TagForDeserializer(Deserialize);
SerializeTagAndCommonProps(tag, aEntryWriter);
}
void StreamPayload(mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime,
UniqueStacks& aUniqueStacks) const override {
StreamCommonProps("CONTENT_FRAME_TIME", aWriter, aProcessStartTime,
aUniqueStacks);
}
private:
explicit ContentFramePayload(CommonProps&& aCommonProps)
: ProfilerMarkerPayload(std::move(aCommonProps)) {}
static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize(
mozilla::ProfileBufferEntryReader& aEntryReader) {
ProfilerMarkerPayload::CommonProps props =
DeserializeCommonProps(aEntryReader);
return UniquePtr<ProfilerMarkerPayload>(
new ContentFramePayload(std::move(props)));
static void StreamJSONMarkerData(
baseprofiler::SpliceableJSONWriter& aWriter) {}
static MarkerSchema MarkerTypeDisplay() {
using MS = MarkerSchema;
MS schema{MS::Location::markerChart, MS::Location::markerTable};
// Nothing outside the defaults.
return schema;
}
};
AUTO_PROFILER_STATS(add_marker_with_ContentFramePayload);
profiler_add_marker_for_thread(
profiler_current_thread_id(), JS::ProfilingCategoryPair::GRAPHICS,
"CONTENT_FRAME_TIME", ContentFramePayload(aTxnStart, aCompositeEnd));
profiler_add_marker("CONTENT_FRAME_TIME", geckoprofiler::category::GRAPHICS,
MarkerTiming::Interval(aTxnStart, aCompositeEnd),
ContentFrameMarker{});
}
#endif

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

@ -39,7 +39,7 @@
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# include "mozilla/BaseProfilerMarkerTypes.h"
#endif
namespace mozilla {
@ -372,43 +372,10 @@ void ContentCompositorBridgeParent::ShadowLayersUpdated(
auto endTime = TimeStamp::Now();
#ifdef MOZ_GECKO_PROFILER
if (profiler_can_accept_markers()) {
class ContentBuildPayload : public ProfilerMarkerPayload {
public:
ContentBuildPayload(const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime)
: ProfilerMarkerPayload(aStartTime, aEndTime) {}
mozilla::ProfileBufferEntryWriter::Length TagAndSerializationBytes()
const override {
return CommonPropsTagAndSerializationBytes();
}
void SerializeTagAndPayload(
mozilla::ProfileBufferEntryWriter& aEntryWriter) const override {
static const DeserializerTag tag = TagForDeserializer(Deserialize);
SerializeTagAndCommonProps(tag, aEntryWriter);
}
void StreamPayload(mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime,
UniqueStacks& aUniqueStacks) const override {
StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter, aProcessStartTime,
aUniqueStacks);
}
private:
explicit ContentBuildPayload(CommonProps&& aCommonProps)
: ProfilerMarkerPayload(std::move(aCommonProps)) {}
static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize(
mozilla::ProfileBufferEntryReader& aEntryReader) {
ProfilerMarkerPayload::CommonProps props =
DeserializeCommonProps(aEntryReader);
return UniquePtr<ProfilerMarkerPayload>(
new ContentBuildPayload(std::move(props)));
}
};
AUTO_PROFILER_STATS(add_marker_with_ContentBuildPayload);
profiler_add_marker_for_thread(
profiler_current_thread_id(), JS::ProfilingCategoryPair::GRAPHICS,
"CONTENT_FULL_PAINT_TIME",
ContentBuildPayload(aInfo.transactionStart(), endTime));
profiler_add_marker(
"CONTENT_FULL_PAINT_TIME", geckoprofiler::category::GRAPHICS,
MarkerTiming::Interval(aInfo.transactionStart(), endTime),
baseprofiler::markers::ContentBuildMarker{});
}
#endif
Telemetry::Accumulate(

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

@ -45,7 +45,7 @@
#endif
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
# include "mozilla/ProfilerMarkerTypes.h"
#endif
bool is_in_main_thread() { return NS_IsMainThread(); }
@ -59,26 +59,20 @@ bool is_in_render_thread() {
}
void gecko_profiler_start_marker(const char* name) {
#ifdef MOZ_GECKO_PROFILER
profiler_tracing_marker("WebRender", name,
JS::ProfilingCategoryPair::GRAPHICS,
TRACING_INTERVAL_START);
#endif
PROFILER_MARKER(mozilla::ProfilerString8View::WrapNullTerminatedString(name),
GRAPHICS, mozilla::MarkerTiming::IntervalStart(), Tracing,
"WebRender");
}
void gecko_profiler_end_marker(const char* name) {
#ifdef MOZ_GECKO_PROFILER
profiler_tracing_marker("WebRender", name,
JS::ProfilingCategoryPair::GRAPHICS,
TRACING_INTERVAL_END);
#endif
PROFILER_MARKER(mozilla::ProfilerString8View::WrapNullTerminatedString(name),
GRAPHICS, mozilla::MarkerTiming::IntervalEnd(), Tracing,
"WebRender");
}
void gecko_profiler_event_marker(const char* name) {
#ifdef MOZ_GECKO_PROFILER
profiler_tracing_marker("WebRender", name,
JS::ProfilingCategoryPair::GRAPHICS, TRACING_EVENT);
#endif
PROFILER_MARKER(mozilla::ProfilerString8View::WrapNullTerminatedString(name),
GRAPHICS, {}, Tracing, "WebRender");
}
void gecko_profiler_add_text_marker(const char* name, const char* text_bytes,
@ -226,46 +220,10 @@ class SceneBuiltNotification : public wr::NotificationHandler {
auto endTime = TimeStamp::Now();
#ifdef MOZ_GECKO_PROFILER
if (profiler_can_accept_markers()) {
class ContentFullPaintPayload : public ProfilerMarkerPayload {
public:
ContentFullPaintPayload(const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime)
: ProfilerMarkerPayload(aStartTime, aEndTime) {}
mozilla::ProfileBufferEntryWriter::Length
TagAndSerializationBytes() const override {
return CommonPropsTagAndSerializationBytes();
}
void SerializeTagAndPayload(mozilla::ProfileBufferEntryWriter&
aEntryWriter) const override {
static const DeserializerTag tag =
TagForDeserializer(Deserialize);
SerializeTagAndCommonProps(tag, aEntryWriter);
}
void StreamPayload(
mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
const TimeStamp& aProcessStartTime,
UniqueStacks& aUniqueStacks) const override {
StreamCommonProps("CONTENT_FULL_PAINT_TIME", aWriter,
aProcessStartTime, aUniqueStacks);
}
private:
explicit ContentFullPaintPayload(CommonProps&& aCommonProps)
: ProfilerMarkerPayload(std::move(aCommonProps)) {}
static mozilla::UniquePtr<ProfilerMarkerPayload> Deserialize(
mozilla::ProfileBufferEntryReader& aEntryReader) {
ProfilerMarkerPayload::CommonProps props =
DeserializeCommonProps(aEntryReader);
return UniquePtr<ProfilerMarkerPayload>(
new ContentFullPaintPayload(std::move(props)));
}
};
AUTO_PROFILER_STATS(add_marker_with_ContentFullPaintPayload);
profiler_add_marker_for_thread(
profiler_current_thread_id(),
JS::ProfilingCategoryPair::GRAPHICS, "CONTENT_FULL_PAINT_TIME",
ContentFullPaintPayload(startTime, endTime));
profiler_add_marker("CONTENT_FULL_PAINT_TIME",
geckoprofiler::category::GRAPHICS,
MarkerTiming::Interval(startTime, endTime),
baseprofiler::markers::ContentBuildMarker{});
}
#endif
Telemetry::Accumulate(

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

@ -13,7 +13,7 @@
-moz-transform:translate3d(0, 80px, 0);
}
#el0:before {
display: -moz-grid;
display: -moz-box;
content: counter(c, hiragana) attr(id);
counter-increment: c 694;
}

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

@ -3291,6 +3291,11 @@ bool gfxPlatform::AsyncPanZoomEnabled() {
#ifdef MOZ_WIDGET_ANDROID
return true;
#else
// If Fission is enabled, OOP iframes require APZ for hittest. So, we
// need to forcibly enable APZ in that case for avoiding users confused.
if (FissionAutostart()) {
return true;
}
return StaticPrefs::
layers_async_pan_zoom_enabled_AtStartup_DoNotUseDirectly();
#endif

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

@ -17,10 +17,6 @@
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/widget/CompositorWidget.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
namespace mozilla {
namespace wr {

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

@ -22,6 +22,8 @@
# define debugf(...) printf(__VA_ARGS__)
#endif
// #define PRINT_TIMINGS
#ifdef _WIN32
# define ALWAYS_INLINE __forceinline
# define NO_INLINE __declspec(noinline)
@ -4021,8 +4023,8 @@ void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
v.validate();
}
#ifndef NDEBUG
// uint64_t start = get_time_value();
#ifdef PRINT_TIMINGS
uint64_t start = get_time_value();
#endif
ctx->shaded_rows = 0;
@ -4074,17 +4076,22 @@ void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
q.value += ctx->shaded_pixels;
}
#ifndef NDEBUG
// uint64_t end = get_time_value();
// debugf("draw(%d): %fms for %d pixels in %d rows (avg %f pixels/row, %f
// ns/pixel)\n", instancecount, double(end - start)/(1000.*1000.),
// ctx->shaded_pixels, ctx->shaded_rows,
// double(ctx->shaded_pixels)/ctx->shaded_rows, double(end -
// start)/max(ctx->shaded_pixels, 1));
#ifdef PRINT_TIMINGS
uint64_t end = get_time_value();
printf("draw(%s, %d): %fms for %d pixels in %d rows (avg %f pixels/row, %fns/pixel)\n",
ctx->programs[ctx->current_program].impl->get_name(),
instancecount, double(end - start)/(1000.*1000.),
ctx->shaded_pixels, ctx->shaded_rows,
double(ctx->shaded_pixels)/ctx->shaded_rows,
double(end - start)/max(ctx->shaded_pixels, 1));
#endif
}
void Finish() {}
void Finish() {
#ifdef PRINT_TIMINGS
printf("Finish\n");
#endif
}
void MakeCurrent(Context* c) {
if (ctx == c) {

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

@ -8,10 +8,6 @@
#include "chrome/common/ipc_message.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
namespace IPC {
void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid,
@ -25,11 +21,12 @@ void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid,
return;
}
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"IPC", IPC, IPCMarkerPayload,
(aOtherPid, aMessage.seqno(), aMessage.type(),
mozilla::ipc::UnknownSide, aDirection, aPhase, aMessage.is_sync(),
mozilla::TimeStamp::NowUnfuzzed()));
// The current timestamp must be given to the `IPCMarker` payload.
const mozilla::TimeStamp now = mozilla::TimeStamp::NowUnfuzzed();
PROFILER_MARKER("IPC", IPC, mozilla::MarkerTiming::InstantAt(now),
IPCMarker, now, now, aOtherPid, aMessage.seqno(),
aMessage.type(), mozilla::ipc::UnknownSide, aDirection,
aPhase, aMessage.is_sync());
}
#endif
}

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

@ -12,33 +12,25 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/process_watcher.h"
#ifdef MOZ_WIDGET_COCOA
# include "chrome/common/mach_ipc_mac.h"
# include "base/rand_util.h"
# include "nsILocalFileMac.h"
# include "SharedMemoryBasic.h"
# include "base/rand_util.h"
# include "chrome/common/mach_ipc_mac.h"
# include "nsILocalFileMac.h"
#endif
#include "MainThreadUtils.h"
#include "mozilla/Sprintf.h"
#include "prenv.h"
#include "nsXPCOMPrivate.h"
#include "prenv.h"
#if defined(MOZ_SANDBOX)
# include "mozilla/SandboxSettings.h"
# include "nsAppDirectoryServiceDefs.h"
#endif
#include "nsExceptionHandler.h"
#include <sys/stat.h>
#include "nsDirectoryService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIFile.h"
#include "nsPrintfCString.h"
#include "nsIObserverService.h"
#include "mozilla/ipc/BrowserProcessSubThread.h"
#include "mozilla/ipc/EnvironmentMap.h"
#include "mozilla/net/SocketProcessHost.h"
#include "ProtocolUtils.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Logging.h"
#include "mozilla/Maybe.h"
@ -50,18 +42,26 @@
#include "mozilla/StaticMutex.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/Telemetry.h"
#include "ProtocolUtils.h"
#include <sys/stat.h>
#include "mozilla/ipc/BrowserProcessSubThread.h"
#include "mozilla/ipc/EnvironmentMap.h"
#include "mozilla/net/SocketProcessHost.h"
#include "nsDirectoryService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsExceptionHandler.h"
#include "nsIFile.h"
#include "nsIObserverService.h"
#include "nsPrintfCString.h"
#ifdef XP_WIN
# include "nsIWinTaskbar.h"
# include <stdlib.h>
# include "nsIWinTaskbar.h"
# define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
# if defined(MOZ_SANDBOX)
# include "WinUtils.h"
# include "mozilla/Preferences.h"
# include "mozilla/sandboxing/sandboxLogging.h"
# include "WinUtils.h"
# if defined(_ARM64_)
# include "mozilla/remoteSandboxBroker.h"
# endif
@ -79,10 +79,10 @@
# include "nsMacUtilsImpl.h"
#endif
#include "nsTArray.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsNativeCharsetUtils.h"
#include "nsTArray.h"
#include "nscore.h" // for NS_FREE_PERMANENT_DATA
#include "private/pprio.h"
@ -696,65 +696,66 @@ bool GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts) {
// to be sure that all of our post-launch processing on |this| happens before
// mHandlePromise notifies.
MOZ_ASSERT(mHandlePromise == nullptr);
RefPtr<ProcessHandlePromise::Private> p =
new ProcessHandlePromise::Private(__func__);
mHandlePromise = p;
mozilla::InvokeAsync<GeckoChildProcessHost*>(
IOThread(), launcher.get(), __func__, &BaseProcessLauncher::Launch, this)
->Then(
IOThread(), __func__,
[this, p](const LaunchResults aResults) {
{
if (!OpenPrivilegedHandle(base::GetProcId(aResults.mHandle))
mHandlePromise =
mozilla::InvokeAsync<GeckoChildProcessHost*>(
IOThread(), launcher.get(), __func__, &BaseProcessLauncher::Launch,
this)
->Then(
IOThread(), __func__,
[this](const LaunchResults& aResults) {
{
if (!OpenPrivilegedHandle(base::GetProcId(aResults.mHandle))
#ifdef XP_WIN
// If we failed in opening the process handle, try harder by
// duplicating one.
&& !::DuplicateHandle(::GetCurrentProcess(), aResults.mHandle,
::GetCurrentProcess(),
&mChildProcessHandle,
PROCESS_DUP_HANDLE | PROCESS_TERMINATE |
PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ | SYNCHRONIZE,
FALSE, 0)
// If we failed in opening the process handle, try harder
// by duplicating one.
&& !::DuplicateHandle(
::GetCurrentProcess(), aResults.mHandle,
::GetCurrentProcess(), &mChildProcessHandle,
PROCESS_DUP_HANDLE | PROCESS_TERMINATE |
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ |
SYNCHRONIZE,
FALSE, 0)
#endif // XP_WIN
) {
MOZ_CRASH("cannot open handle to child process");
}
) {
MOZ_CRASH("cannot open handle to child process");
}
#ifdef XP_MACOSX
this->mChildTask = aResults.mChildTask;
this->mChildTask = aResults.mChildTask;
#endif
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
this->mSandboxBroker = aResults.mSandboxBroker;
this->mSandboxBroker = aResults.mSandboxBroker;
#endif
MonitorAutoLock lock(mMonitor);
// The OnChannel{Connected,Error} may have already advanced the
// state.
if (mProcessState < PROCESS_CREATED) {
mProcessState = PROCESS_CREATED;
}
lock.Notify();
}
p->Resolve(aResults.mHandle, __func__);
},
[this, p](const LaunchError aError) {
// WaitUntilConnected might be waiting for us to signal.
// If something failed let's set the error state and notify.
CHROMIUM_LOG(ERROR)
<< "Failed to launch "
<< XRE_GeckoProcessTypeToString(mProcessType) << " subprocess";
Telemetry::Accumulate(
Telemetry::SUBPROCESS_LAUNCH_FAILURE,
nsDependentCString(XRE_GeckoProcessTypeToString(mProcessType)));
{
MonitorAutoLock lock(mMonitor);
mProcessState = PROCESS_ERROR;
lock.Notify();
}
p->Reject(aError, __func__);
});
MonitorAutoLock lock(mMonitor);
// The OnChannel{Connected,Error} may have already advanced
// the state.
if (mProcessState < PROCESS_CREATED) {
mProcessState = PROCESS_CREATED;
}
lock.Notify();
}
return ProcessHandlePromise::CreateAndResolve(aResults.mHandle,
__func__);
},
[this](const LaunchError aError) {
// WaitUntilConnected might be waiting for us to signal.
// If something failed let's set the error state and notify.
CHROMIUM_LOG(ERROR)
<< "Failed to launch "
<< XRE_GeckoProcessTypeToString(mProcessType)
<< " subprocess";
Telemetry::Accumulate(
Telemetry::SUBPROCESS_LAUNCH_FAILURE,
nsDependentCString(
XRE_GeckoProcessTypeToString(mProcessType)));
{
MonitorAutoLock lock(mMonitor);
mProcessState = PROCESS_ERROR;
lock.Notify();
}
return ProcessHandlePromise::CreateAndReject(aError, __func__);
});
return true;
}

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

@ -37,10 +37,6 @@
using namespace mozilla::tasktracer;
#endif
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
// Undo the damage done by mozzconf.h
#undef compress
@ -2789,11 +2785,11 @@ void MessageChannel::AddProfilerMarker(const IPC::Message& aMessage,
if (profiler_feature_active(ProfilerFeature::IPCMessages)) {
int32_t pid = mListener->OtherPidMaybeInvalid();
if (pid != kInvalidProcessId) {
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"IPC", IPC, IPCMarkerPayload,
(pid, aMessage.seqno(), aMessage.type(), mSide, aDirection,
MessagePhase::Endpoint, aMessage.is_sync(),
TimeStamp::NowUnfuzzed()));
// The current timestamp must be given to the `IPCMarker` payload.
const TimeStamp now = TimeStamp::NowUnfuzzed();
PROFILER_MARKER("IPC", IPC, MarkerTiming::InstantAt(now), IPCMarker, now,
now, pid, aMessage.seqno(), aMessage.type(), mSide,
aDirection, MessagePhase::Endpoint, aMessage.is_sync());
}
}
#endif

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

@ -29,6 +29,10 @@
#include "MessageLink.h"
#include "mozilla/ipc/Transport.h"
#ifdef MOZ_GECKO_PROFILER
# include "mozilla/ProfilerMarkersPrerequisites.h"
#endif
class nsIEventTarget;
namespace mozilla {
@ -879,4 +883,74 @@ struct ParamTraits<mozilla::ipc::ResponseRejectReason>
mozilla::ipc::ResponseRejectReason::EndGuard_> {};
} // namespace IPC
#ifdef MOZ_GECKO_PROFILER
namespace geckoprofiler::markers {
struct IPCMarker {
static constexpr mozilla::Span<const char> MarkerTypeName() {
return mozilla::MakeStringSpan("IPC");
}
static void StreamJSONMarkerData(
mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
mozilla::TimeStamp aStart, mozilla::TimeStamp aEnd, int32_t aOtherPid,
int32_t aMessageSeqno, IPC::Message::msgid_t aMessageType,
mozilla::ipc::Side aSide, mozilla::ipc::MessageDirection aDirection,
mozilla::ipc::MessagePhase aPhase, bool aSync) {
using namespace mozilla::ipc;
// This payload still streams a startTime and endTime property because it
// made the migration to MarkerTiming on the front-end easier.
mozilla::baseprofiler::WritePropertyTime(aWriter, "startTime", aStart);
mozilla::baseprofiler::WritePropertyTime(aWriter, "endTime", aEnd);
aWriter.IntProperty("otherPid", aOtherPid);
aWriter.IntProperty("messageSeqno", aMessageSeqno);
aWriter.StringProperty(
"messageType",
mozilla::MakeStringSpan(IPC::StringFromIPCMessageType(aMessageType)));
aWriter.StringProperty("side", IPCSideToString(aSide));
aWriter.StringProperty("direction",
aDirection == MessageDirection::eSending
? mozilla::MakeStringSpan("sending")
: mozilla::MakeStringSpan("receiving"));
aWriter.StringProperty("phase", IPCPhaseToString(aPhase));
aWriter.BoolProperty("sync", aSync);
}
static mozilla::MarkerSchema MarkerTypeDisplay() {
return mozilla::MarkerSchema::SpecialFrontendLocation{};
}
private:
static mozilla::Span<const char> IPCSideToString(mozilla::ipc::Side aSide) {
switch (aSide) {
case mozilla::ipc::ParentSide:
return mozilla::MakeStringSpan("parent");
case mozilla::ipc::ChildSide:
return mozilla::MakeStringSpan("child");
case mozilla::ipc::UnknownSide:
return mozilla::MakeStringSpan("unknown");
default:
MOZ_ASSERT_UNREACHABLE("Invalid IPC side");
return mozilla::MakeStringSpan("<invalid IPC side>");
}
}
static mozilla::Span<const char> IPCPhaseToString(
mozilla::ipc::MessagePhase aPhase) {
switch (aPhase) {
case mozilla::ipc::MessagePhase::Endpoint:
return mozilla::MakeStringSpan("endpoint");
case mozilla::ipc::MessagePhase::TransferStart:
return mozilla::MakeStringSpan("transferStart");
case mozilla::ipc::MessagePhase::TransferEnd:
return mozilla::MakeStringSpan("transferEnd");
default:
MOZ_ASSERT_UNREACHABLE("Invalid IPC phase");
return mozilla::MakeStringSpan("<invalid IPC phase>");
}
}
};
} // namespace geckoprofiler::markers
#endif
#endif // ifndef ipc_glue_MessageChannel_h

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

@ -23,6 +23,10 @@
#include <objbase.h>
#include <objidlbase.h>
#ifdef MOZ_GECKO_PROFILER
# include "mozilla/ProfilerMarkerTypes.h"
#endif
// {9DBE6B28-E5E7-4FDE-AF00-9404604E74DC}
static const GUID GUID_MozProfilerMarkerExtension = {
0x9dbe6b28, 0xe5e7, 0x4fde, {0xaf, 0x0, 0x94, 0x4, 0x60, 0x4e, 0x74, 0xdc}};
@ -133,8 +137,8 @@ void ProfilerMarkerChannelHook::ClientGetSize(REFGUID aExtensionId, REFIID aIid,
if (NS_IsMainThread()) {
nsAutoCString markerName;
BuildMarkerName(aIid, markerName);
PROFILER_TRACING_MARKER("MSCOM", markerName.get(), IPC,
TRACING_INTERVAL_START);
PROFILER_MARKER(markerName, IPC, mozilla::MarkerTiming::IntervalStart(),
Tracing, "MSCOM");
}
if (aOutDataSize) {
@ -150,8 +154,8 @@ void ProfilerMarkerChannelHook::ClientNotify(REFGUID aExtensionId, REFIID aIid,
if (NS_IsMainThread() && aExtensionId == GUID_MozProfilerMarkerExtension) {
nsAutoCString markerName;
BuildMarkerName(aIid, markerName);
PROFILER_TRACING_MARKER("MSCOM", markerName.get(), IPC,
TRACING_INTERVAL_END);
PROFILER_MARKER(markerName, IPC, mozilla::MarkerTiming::IntervalEnd(),
Tracing, "MSCOM");
}
}

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

@ -79,6 +79,13 @@ patterns = [
"0",
"0",
),
(
"<unnamed>",
1,
"js::jit::AssemblerBufferWithConstantPools<.*>::executableCopy",
"&cur->instructions[0]",
"dest",
),
]
@ -97,7 +104,8 @@ class JitSource(gdb.Command):
b.enabled = True
def search_stack(self, base_name, hops, name, src, dst, address):
if not re.match(base_name, gdb.newest_frame().name()):
current_frame_name = gdb.newest_frame().name() or "<unnamed>"
if not re.match(base_name, current_frame_name):
return None
f = gdb.newest_frame()
for _ in range(hops):

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

@ -63,9 +63,6 @@
#include "nsIXULRuntime.h"
#include "nsJSPrincipals.h"
#include "ExpandedPrincipal.h"
#ifdef MOZ_GECKO_PROFILER
# include "ProfilerMarkerPayload.h"
#endif
#if defined(XP_LINUX) && !defined(ANDROID)
// For getrlimit and min/max.
@ -591,9 +588,7 @@ bool XPCJSContext::InterruptCallback(JSContext* cx) {
if (const char* file = scriptFilename.get()) {
filename.Assign(file, strlen(file));
}
PROFILER_ADD_MARKER_WITH_PAYLOAD("JS::InterruptCallback", JS,
TextMarkerPayload,
(filename, TimeStamp::Now()));
PROFILER_MARKER_TEXT("JS::InterruptCallback", JS, {}, filename);
}
#endif

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

@ -8,16 +8,15 @@
#define mozilla_AutoProfilerStyleMarker_h
#include "mozilla/Attributes.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/ServoTraversalStatistics.h"
#include "mozilla/TimeStamp.h"
#include "GeckoProfiler.h"
#include "ProfilerMarkerPayload.h"
namespace mozilla {
class MOZ_RAII AutoProfilerStyleMarker {
public:
explicit AutoProfilerStyleMarker(UniqueProfilerBacktrace aCause,
explicit AutoProfilerStyleMarker(UniquePtr<ProfileChunkedBuffer> aCause,
const Maybe<uint64_t>& aInnerWindowID)
: mActive(profiler_can_accept_markers()),
mStartTime(TimeStamp::Now()),
@ -36,17 +35,57 @@ class MOZ_RAII AutoProfilerStyleMarker {
if (!mActive) {
return;
}
struct StyleMarker {
static constexpr mozilla::Span<const char> MarkerTypeName() {
return mozilla::MakeStringSpan("Styles");
}
static void StreamJSONMarkerData(
baseprofiler::SpliceableJSONWriter& aWriter,
uint32_t aElementsTraversed, uint32_t aElementsStyled,
uint32_t aElementsMatched, uint32_t aStylesShared,
uint32_t aStylesReused) {
aWriter.IntProperty("elementsTraversed", aElementsTraversed);
aWriter.IntProperty("elementsStyled", aElementsStyled);
aWriter.IntProperty("elementsMatched", aElementsMatched);
aWriter.IntProperty("stylesShared", aStylesShared);
aWriter.IntProperty("stylesReused", aStylesReused);
}
static MarkerSchema MarkerTypeDisplay() {
using MS = MarkerSchema;
MS schema{MS::Location::markerChart, MS::Location::markerTable,
MS::Location::timelineOverview};
schema.AddKeyLabelFormat("elementsTraversed", "Elements traversed",
MS::Format::integer);
schema.AddKeyLabelFormat("elementsStyled", "Elements styled",
MS::Format::integer);
schema.AddKeyLabelFormat("elementsMatched", "Elements matched",
MS::Format::integer);
schema.AddKeyLabelFormat("stylesShared", "Styles shared",
MS::Format::integer);
schema.AddKeyLabelFormat("stylesReused", "Styles reused",
MS::Format::integer);
return schema;
}
};
ServoTraversalStatistics::sActive = false;
PROFILER_ADD_MARKER_WITH_PAYLOAD(
"Styles", LAYOUT, StyleMarkerPayload,
(mStartTime, TimeStamp::Now(), std::move(mCause),
ServoTraversalStatistics::sSingleton, mInnerWindowID));
profiler_add_marker("Styles", geckoprofiler::category::LAYOUT,
{MarkerTiming::IntervalUntilNowFrom(mStartTime),
MarkerStack::TakeBacktrace(std::move(mCause)),
MarkerInnerWindowId(mInnerWindowID)},
StyleMarker{},
ServoTraversalStatistics::sSingleton.mElementsTraversed,
ServoTraversalStatistics::sSingleton.mElementsStyled,
ServoTraversalStatistics::sSingleton.mElementsMatched,
ServoTraversalStatistics::sSingleton.mStylesShared,
ServoTraversalStatistics::sSingleton.mStylesReused);
}
private:
bool mActive;
TimeStamp mStartTime;
UniqueProfilerBacktrace mCause;
UniquePtr<ProfileChunkedBuffer> mCause;
Maybe<uint64_t> mInnerWindowID;
};

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

@ -9565,7 +9565,7 @@ bool PresShell::DoReflow(nsIFrame* target, bool aInterruptible,
innerWindowID = Some(window->WindowID());
}
AutoProfilerTracing tracingLayoutFlush(
"Paint", "Reflow", JS::ProfilingCategoryPair::LAYOUT,
"Paint", "Reflow", geckoprofiler::category::LAYOUT,
std::move(mReflowCause), innerWindowID);
mReflowCause = nullptr;
#endif

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

@ -2861,8 +2861,8 @@ class PresShell final : public nsStubDocumentObserver,
// These two fields capture call stacks of any changes that require a restyle
// or a reflow. Only the first change per restyle / reflow is recorded (the
// one that caused a call to SetNeedStyleFlush() / SetNeedLayoutFlush()).
UniqueProfilerBacktrace mStyleCause;
UniqueProfilerBacktrace mReflowCause;
UniquePtr<ProfileChunkedBuffer> mStyleCause;
UniquePtr<ProfileChunkedBuffer> mReflowCause;
#endif
nsTArray<UniquePtr<DelayedEvent>> mDelayedEvents;

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

@ -23,7 +23,7 @@ void PresShell::SetNeedLayoutFlush() {
#ifdef MOZ_GECKO_PROFILER
if (!mReflowCause) {
mReflowCause = profiler_get_backtrace();
mReflowCause = profiler_capture_backtrace();
}
#endif
@ -40,7 +40,7 @@ void PresShell::SetNeedStyleFlush() {
#ifdef MOZ_GECKO_PROFILER
if (!mStyleCause) {
mStyleCause = profiler_get_backtrace();
mStyleCause = profiler_capture_backtrace();
}
#endif

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше