зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central. a=merge
This commit is contained in:
Коммит
1d34bd022d
|
@ -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"
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -11,23 +11,23 @@
|
|||
padding: 5px;
|
||||
display: table;
|
||||
-moz-transform:translate3d(0, 80px, 0);
|
||||
}
|
||||
}
|
||||
#el0:before {
|
||||
display: -moz-grid;
|
||||
display: -moz-box;
|
||||
content: counter(c, hiragana) attr(id);
|
||||
counter-increment: c 694;
|
||||
}
|
||||
}
|
||||
#el0:after {
|
||||
counter-reset: c 694;
|
||||
content: counter(c, cjk-ideographic) attr(id);
|
||||
}
|
||||
}
|
||||
#el1 {
|
||||
text-shadow: 0px 20px 0px, 0px -20px 10px;
|
||||
line-height: 4px;
|
||||
transform: translate3d(0px, -300px, 0px);
|
||||
display: table-row-group;
|
||||
border-spacing: 7px;
|
||||
}
|
||||
}
|
||||
#el1:after {
|
||||
counter-reset: c;
|
||||
display: -moz-box;
|
||||
|
@ -36,18 +36,18 @@
|
|||
}
|
||||
#el2 {
|
||||
display: table-row-group;
|
||||
-moz-transform:translate3d(0, 80px, 0);
|
||||
-moz-transform:translate3d(0, 80px, 0);
|
||||
}
|
||||
#el2:after {
|
||||
content: counter(c, cjk-ideographic) attr(id);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function MaybeReload() {
|
||||
var countdown = 10;
|
||||
if (location.search) {
|
||||
countdown = parseInt(location.search.slice(1)) - 1;
|
||||
}
|
||||
}
|
||||
if (countdown > 0) {
|
||||
location.search = countdown;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче