This commit is contained in:
Wes Kocher 2016-08-29 17:54:53 -07:00
Родитель 63a3869bb8 a4c2350d88
Коммит 6c4583e52f
59 изменённых файлов: 1038 добавлений и 777 удалений

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

@ -1525,6 +1525,10 @@ const DownloadsFooter = {
} else {
this._footerNode.removeAttribute("showingsummary");
}
if (!aValue && this._showingSummary) {
// Make sure the panel's height shrinks when the summary is hidden.
DownloadsBlockedSubview.view.setHeightToFit();
}
this._showingSummary = aValue;
}
return aValue;

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

@ -10,8 +10,15 @@ const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"]
.userAgent;
const NOKIA_UA = "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; " +
"Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)";
const Types = require("devtools/client/responsive.html/types");
addRDMTask(TEST_URL, function* ({ ui, manager }) {
let { store } = ui.toolWindow;
// Wait until the viewport has been added and the device list has been loaded
yield waitUntilState(store, state => state.viewports.length == 1
&& state.devices.listState == Types.deviceListState.LOADED);
// Test defaults
testViewportDimensions(ui, 320, 480);
yield testUserAgent(ui, DEFAULT_UA);

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

@ -1056,7 +1056,11 @@ AudioChannelService::AudioChannelWindow::RequestAudioFocus(AudioChannelAgent* aA
// Only foreground window can request audio focus, but it would still own the
// audio focus even it goes to background. Audio focus would be abandoned
// only when other foreground window starts audio competing.
mOwningAudioFocus = !(aAgent->Window()->IsBackground());
// One exception is if the pref "media.block-autoplay-until-in-foreground"
// is on and the background page is the non-visited before. Because the media
// in that page would be blocked until the page is going to foreground.
mOwningAudioFocus = (!(aAgent->Window()->IsBackground()) ||
aAgent->Window()->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK) ;
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
("AudioChannelWindow, RequestAudioFocus, this = %p, "

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

@ -1498,6 +1498,8 @@ nsDocument::nsDocument(const char* aContentType)
// Add the base queue sentinel to the processing stack.
sProcessingStack->AppendElement((CustomElementData*) nullptr);
}
mEverInForeground = false;
}
void
@ -12596,6 +12598,10 @@ nsDocument::UpdateVisibilityState()
EnumerateActivityObservers(NotifyActivityChanged, nullptr);
}
if (mVisibilityState == dom::VisibilityState::Visible) {
MaybeActiveMediaComponents();
}
}
VisibilityState
@ -12631,6 +12637,23 @@ nsDocument::PostVisibilityUpdateEvent()
NS_DispatchToMainThread(event);
}
void
nsDocument::MaybeActiveMediaComponents()
{
if (mEverInForeground) {
return;
}
if (!mWindow) {
return;
}
mEverInForeground = true;
if (GetWindow()->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK) {
GetWindow()->SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
}
}
NS_IMETHODIMP
nsDocument::GetMozHidden(bool* aHidden)
{

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

@ -1209,6 +1209,10 @@ public:
// Posts an event to call UpdateVisibilityState
virtual void PostVisibilityUpdateEvent() override;
// Since we wouldn't automatically play media from non-visited page, we need
// to notify window when the page was first visited.
void MaybeActiveMediaComponents();
virtual void DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const override;
// DocAddSizeOfIncludingThis is inherited from nsIDocument.

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

@ -618,7 +618,8 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMWindowOuter *aOuterWindow)
mInnerObjectsFreed(false),
mIsModalContentWindow(false),
mIsActive(false), mIsBackground(false),
mMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED),
mMediaSuspend(Preferences::GetBool("media.block-autoplay-until-in-foreground", true) ?
nsISuspendedTypes::SUSPENDED_BLOCK : nsISuspendedTypes::NONE_SUSPENDED),
mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
mDesktopModeViewport(false), mInnerWindow(nullptr),
mOuterWindow(aOuterWindow),

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

@ -3069,6 +3069,9 @@ protected:
// Do we currently have an event posted to call FlushUserFontSet?
bool mPostedFlushUserFontSet : 1;
// True is document has ever been in a foreground window.
bool mEverInForeground : 1;
enum Type {
eUnknown, // should never be used
eHTML,

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

@ -74,7 +74,7 @@ function setupTestFrame() {
ac.onactivestatechanged = () => {
ac.onactivestatechanged = null;
error("Should not receive onactivestatechanged!");
ok(true, "Should receive onactivestatechanged!");
};
continueTest();

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

@ -1020,6 +1020,9 @@ void HTMLMediaElement::AbortExistingLoads()
if (mTextTrackManager) {
mTextTrackManager->NotifyReset();
}
mEventDeliveryPaused = false;
mPendingEvents.Clear();
}
void HTMLMediaElement::NoSupportedMediaSourceError()
@ -1396,7 +1399,7 @@ void HTMLMediaElement::NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream)
if (videoHasChanged) {
// We are a video element and HasVideo() changed so update the screen
// wakelock
NotifyOwnerDocumentActivityChangedInternal();
NotifyOwnerDocumentActivityChanged();
}
mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
@ -2882,7 +2885,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mPlayingThroughTheAudioChannelBeforeSeek(false),
mPausedForInactiveDocumentOrChannel(false),
mEventDeliveryPaused(false),
mWaitingFired(false),
mIsRunningLoadMethod(false),
mIsDoingExplicitLoad(false),
mIsLoadingFromSourceChildren(false),
@ -2904,7 +2906,6 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mAudioChannelVolume(1.0),
mPlayingThroughTheAudioChannel(false),
mDisableVideo(false),
mPlayBlockedBecauseHidden(false),
mElementInTreeState(ELEMENT_NOT_INTREE),
mHasUserInteraction(false),
mFirstFrameLoaded(false),
@ -2922,7 +2923,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mPaused.SetOuter(this);
RegisterActivityObserver();
NotifyOwnerDocumentActivityChangedInternal();
NotifyOwnerDocumentActivityChanged();
MOZ_ASSERT(NS_IsMainThread());
mWatchManager.Watch(mDownloadSuspendedByCache, &HTMLMediaElement::UpdateReadyStateInternal);
@ -3011,14 +3012,14 @@ HTMLMediaElement::NotifyXPCOMShutdown()
void
HTMLMediaElement::Play(ErrorResult& aRv)
{
nsresult rv = PlayInternal(nsContentUtils::IsCallerChrome());
nsresult rv = PlayInternal();
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
nsresult
HTMLMediaElement::PlayInternal(bool aCallerIsChrome)
HTMLMediaElement::PlayInternal()
{
if (!IsAllowedToPlay()) {
return NS_OK;
@ -3037,14 +3038,6 @@ HTMLMediaElement::PlayInternal(bool aCallerIsChrome)
ResumeLoad(PRELOAD_ENOUGH);
}
if (Preferences::GetBool("media.block-play-until-visible", false) &&
!aCallerIsChrome &&
OwnerDoc()->Hidden()) {
LOG(LogLevel::Debug, ("%p Blocked playback because owner hidden.", this));
mPlayBlockedBecauseHidden = true;
return NS_OK;
}
// Even if we just did Load() or ResumeLoad(), we could already have a decoder
// here if we managed to clone an existing decoder.
if (mDecoder) {
@ -3063,9 +3056,28 @@ HTMLMediaElement::PlayInternal(bool aCallerIsChrome)
mCurrentPlayRangeStart = CurrentTime();
}
bool oldPaused = mPaused;
mPaused = false;
mAutoplaying = false;
SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
// We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
// and our preload status.
AddRemoveSelfReference();
UpdatePreloadAction();
UpdateSrcMediaStreamPlaying();
UpdateAudioChannelPlayingState();
// The check here is to handle the case that the media element starts playing
// after it loaded fail. eg. preload the data before playing.
OpenUnsupportedMediaWithExtenalAppIfNeeded();
// We should check audio channel playing state before dispatching any events,
// because we don't want to dispatch events for blocked media. For blocked
// media, the event would be pending until media is resumed.
// TODO: If the playback has ended, then the user agent must set
// seek to the effective start.
if (mPaused) {
if (oldPaused) {
DispatchAsyncEvent(NS_LITERAL_STRING("play"));
switch (mReadyState) {
case nsIDOMHTMLMediaElement::HAVE_NOTHING:
@ -3084,27 +3096,12 @@ HTMLMediaElement::PlayInternal(bool aCallerIsChrome)
}
}
mPaused = false;
mAutoplaying = false;
SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
// We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
// and our preload status.
AddRemoveSelfReference();
UpdatePreloadAction();
UpdateSrcMediaStreamPlaying();
UpdateAudioChannelPlayingState();
// The check here is to handle the case that the media element starts playing
// after it loaded fail. eg. preload the data before playing.
OpenUnsupportedMediaWithExtenalAppIfNeeded();
return NS_OK;
}
NS_IMETHODIMP HTMLMediaElement::Play()
{
return PlayInternal(/* aCallerIsChrome = */ true);
return PlayInternal();
}
HTMLMediaElement::WakeLockBoolWrapper&
@ -3796,10 +3793,6 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
// Force a same-origin check before allowing events for this media resource.
mMediaSecurityVerified = false;
// The new stream has not been suspended by us.
mPausedForInactiveDocumentOrChannel = false;
mEventDeliveryPaused = false;
mPendingEvents.Clear();
// Set mDecoder now so if methods like GetCurrentSrc get called between
// here and Load(), they work.
SetDecoder(aDecoder);
@ -3865,7 +3858,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
// We may want to suspend the new stream now.
// This will also do an AddRemoveSelfReference.
NotifyOwnerDocumentActivityChangedInternal();
NotifyOwnerDocumentActivityChanged();
UpdateAudioChannelPlayingState();
if (!mPaused) {
@ -4308,7 +4301,8 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
// get dispatched.
AutoNotifyAudioChannelAgent autoNotify(this);
mMediaInfo = *aInfo;
SetMediaInfo(*aInfo);
mIsEncrypted = aInfo->IsEncrypted()
#ifdef MOZ_EME
|| mPendingEncryptedInitData.IsEncrypted()
@ -4350,7 +4344,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
if (IsVideo() && aInfo->HasVideo()) {
// We are a video element playing video so update the screen wakelock
NotifyOwnerDocumentActivityChangedInternal();
NotifyOwnerDocumentActivityChanged();
}
if (mDefaultPlaybackStartPosition != 0.0) {
@ -4775,11 +4769,6 @@ HTMLMediaElement::UpdateReadyStateInternal()
if (mFirstFrameLoaded) {
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
}
if (!mWaitingFired && nextFrameStatus == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING) {
FireTimeUpdate(false);
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
mWaitingFired = true;
}
return;
}
@ -4838,9 +4827,24 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
UpdateAudioChannelPlayingState();
// Handle raising of "waiting" event during seek (see 4.8.10.9)
// or
// 4.8.12.7 Ready states:
// "If the previous ready state was HAVE_FUTURE_DATA or more, and the new
// ready state is HAVE_CURRENT_DATA or less
// If the media element was potentially playing before its readyState
// attribute changed to a value lower than HAVE_FUTURE_DATA, and the element
// has not ended playback, and playback has not stopped due to errors,
// paused for user interaction, or paused for in-band content, the user agent
// must queue a task to fire a simple event named timeupdate at the element,
// and queue a task to fire a simple event named waiting at the element."
if (mPlayingBeforeSeek &&
mReadyState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
} else if (oldState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
mReadyState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
!Paused() && !Ended() && !mError) {
FireTimeUpdate(false);
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
}
if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
@ -4850,10 +4854,6 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
mLoadedDataFired = true;
}
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
mWaitingFired = false;
}
if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
@ -4956,13 +4956,6 @@ void HTMLMediaElement::CheckAutoplayDataReady()
return;
}
if (Preferences::GetBool("media.block-play-until-visible", false) &&
OwnerDoc()->Hidden()) {
LOG(LogLevel::Debug, ("%p Blocked autoplay because owner hidden.", this));
mPlayBlockedBecauseHidden = true;
return;
}
mPaused = false;
// We changed mPaused which can affect AddRemoveSelfReference
AddRemoveSelfReference();
@ -4974,12 +4967,15 @@ void HTMLMediaElement::CheckAutoplayDataReady()
if (mCurrentPlayRangeStart == -1.0) {
mCurrentPlayRangeStart = CurrentTime();
}
mDecoder->Play();
if (!ShouldElementBePaused()) {
mDecoder->Play();
}
} else if (mSrcStream) {
SetPlayedOrSeeked(true);
}
DispatchAsyncEvent(NS_LITERAL_STRING("play"));
// For blocked media, the event would be pending until it is resumed.
DispatchAsyncEvent(NS_LITERAL_STRING("play"));
}
bool HTMLMediaElement::IsActive() const
@ -5303,24 +5299,6 @@ bool HTMLMediaElement::IsBeingDestroyed()
}
void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
{
bool pauseElement = NotifyOwnerDocumentActivityChangedInternal();
if (pauseElement && mAudioChannelAgent) {
// If the element is being paused since we are navigating away from the
// document, notify the audio channel agent.
// Be careful to ignore this event during a docshell frame swap.
auto docShell = static_cast<nsDocShell*>(OwnerDoc()->GetDocShell());
if (!docShell) {
return;
}
if (!docShell->InFrameSwap()) {
NotifyAudioChannelAgent(false);
}
}
}
bool
HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal()
{
bool visible = !IsHidden();
if (visible) {
@ -5335,20 +5313,10 @@ HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal()
mDecoder->NotifyOwnerActivityChanged(visible);
}
bool pauseElement = !IsActive();
bool pauseElement = ShouldElementBePaused();
SuspendOrResumeElement(pauseElement, !IsActive());
if (!mPausedForInactiveDocumentOrChannel &&
mPlayBlockedBecauseHidden &&
!OwnerDoc()->Hidden()) {
LOG(LogLevel::Debug, ("%p Resuming playback now that owner doc is visble.", this));
mPlayBlockedBecauseHidden = false;
Play();
}
AddRemoveSelfReference();
return pauseElement;
}
void HTMLMediaElement::AddRemoveSelfReference()
@ -5533,7 +5501,7 @@ HTMLMediaElement::CopyInnerTo(Element* aDest)
NS_ENSURE_SUCCESS(rv, rv);
if (aDest->OwnerDoc()->IsStaticDocument()) {
HTMLMediaElement* dest = static_cast<HTMLMediaElement*>(aDest);
dest->mMediaInfo = mMediaInfo;
dest->SetMediaInfo(mMediaInfo);
}
return rv;
}
@ -5789,11 +5757,6 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
return false;
}
// If this element doesn't have any audio tracks.
if (!HasAudio()) {
return false;
}
// We should consider any bfcached page or inactive document as non-playing.
if (!IsActive()) {
return false;
@ -5804,11 +5767,6 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
return true;
}
// If we are actually playing...
if (IsCurrentlyPlaying()) {
return true;
}
// If we are seeking, we consider it as playing
if (mPlayingThroughTheAudioChannelBeforeSeek) {
return true;
@ -5819,7 +5777,7 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
return true;
}
return false;
return true;
}
void
@ -5952,7 +5910,7 @@ HTMLMediaElement::ResumeFromAudioChannelPaused(SuspendTypes aSuspend)
mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE);
SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
nsresult rv = PlayInternal(nsContentUtils::IsCallerChrome());
nsresult rv = PlayInternal();
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
@ -5965,7 +5923,7 @@ HTMLMediaElement::ResumeFromAudioChannelBlocked()
MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
mPaused.SetCanPlay(true);
mPaused = false;
SuspendOrResumeElement(false /* resume */, false);
}
@ -5989,8 +5947,8 @@ HTMLMediaElement::BlockByAudioChannel()
}
SetAudioChannelSuspended(nsISuspendedTypes::SUSPENDED_BLOCK);
SuspendOrResumeElement(true /* suspend */, false);
mPaused.SetCanPlay(false);
mPaused = true;
SuspendOrResumeElement(true /* suspend */, true /* pending event */);
}
void
@ -6280,48 +6238,11 @@ HTMLMediaElement::CannotDecryptWaitingForKey()
NS_IMETHODIMP HTMLMediaElement::WindowAudioCaptureChanged(bool aCapture)
{
MOZ_ASSERT(mAudioChannelAgent);
MOZ_ASSERT(HasAudio());
if (!OwnerDoc()->GetInnerWindow()) {
return NS_OK;
if (mAudioCapturedByWindow != aCapture) {
mAudioCapturedByWindow = aCapture;
AudioCaptureStreamChangeIfNeeded();
}
if (aCapture != mAudioCapturedByWindow) {
if (aCapture) {
mAudioCapturedByWindow = true;
nsCOMPtr<nsPIDOMWindowInner> window = OwnerDoc()->GetInnerWindow();
uint64_t id = window->WindowID();
MediaStreamGraph* msg =
MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
mAudioChannel);
if (GetSrcMediaStream()) {
mCaptureStreamPort = msg->ConnectToCaptureStream(id, GetSrcMediaStream());
} else {
RefPtr<DOMMediaStream> stream = CaptureStreamInternal(false, msg);
mCaptureStreamPort = msg->ConnectToCaptureStream(id, stream->GetPlaybackStream());
}
} else {
mAudioCapturedByWindow = false;
if (mDecoder) {
ProcessedMediaStream* ps =
mCaptureStreamPort->GetSource()->AsProcessedStream();
MOZ_ASSERT(ps);
for (uint32_t i = 0; i < mOutputStreams.Length(); i++) {
if (mOutputStreams[i].mStream->GetPlaybackStream() == ps) {
mOutputStreams.RemoveElementAt(i);
break;
}
}
mDecoder->RemoveOutputStream(ps);
}
mCaptureStreamPort->Destroy();
mCaptureStreamPort = nullptr;
}
}
return NS_OK;
}
@ -6588,6 +6509,69 @@ HTMLMediaElement::OnVisibilityChange(Visibility aOldVisibility,
}
bool
HTMLMediaElement::ShouldElementBePaused()
{
// The media in the non-visited page would be blocked.
if (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) {
return true;
}
// Bfcached page or inactive document.
if (!IsActive()) {
return true;
}
return false;
}
void
HTMLMediaElement::SetMediaInfo(const MediaInfo aInfo)
{
mMediaInfo = aInfo;
AudioCaptureStreamChangeIfNeeded();
}
void
HTMLMediaElement::AudioCaptureStreamChangeIfNeeded()
{
// TODO : only capture media element with audio track, see bug1298777.
if (mAudioCapturedByWindow && !mCaptureStreamPort) {
nsCOMPtr<nsPIDOMWindowInner> window = OwnerDoc()->GetInnerWindow();
if (!OwnerDoc()->GetInnerWindow()) {
return;
}
uint64_t id = window->WindowID();
MediaStreamGraph* msg =
MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
mAudioChannel);
if (GetSrcMediaStream()) {
mCaptureStreamPort = msg->ConnectToCaptureStream(id, GetSrcMediaStream());
} else {
RefPtr<DOMMediaStream> stream = CaptureStreamInternal(false, msg);
mCaptureStreamPort = msg->ConnectToCaptureStream(id, stream->GetPlaybackStream());
}
} else if (!mAudioCapturedByWindow && mCaptureStreamPort) {
if (mDecoder) {
ProcessedMediaStream* ps =
mCaptureStreamPort->GetSource()->AsProcessedStream();
MOZ_ASSERT(ps);
for (uint32_t i = 0; i < mOutputStreams.Length(); i++) {
if (mOutputStreams[i].mStream->GetPlaybackStream() == ps) {
mOutputStreams.RemoveElementAt(i);
break;
}
}
mDecoder->RemoveOutputStream(ps);
}
mCaptureStreamPort->Destroy();
mCaptureStreamPort = nullptr;
}
}
void
HTMLMediaElement::NotifyCueDisplayStatesChanged()
{

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

@ -161,12 +161,7 @@ public:
* Call this to reevaluate whether we should start/stop due to our owner
* document being active, inactive, visible or hidden.
*/
void NotifyOwnerDocumentActivityChanged();
// This method does the work necessary for the
// NotifyOwnerDocumentActivityChanged() notification. It returns true if the
// media element was paused as a result.
virtual bool NotifyOwnerDocumentActivityChangedInternal();
virtual void NotifyOwnerDocumentActivityChanged();
// Called by the video decoder object, on the main thread,
// when it has read the metadata containing video dimensions,
@ -744,6 +739,8 @@ public:
bool ComputedMuted() const;
nsSuspendedTypes ComputedSuspended() const;
void SetMediaInfo(const MediaInfo aInfo);
protected:
virtual ~HTMLMediaElement();
@ -805,7 +802,7 @@ protected:
nsTArray<Pair<nsString, RefPtr<MediaInputPort>>> mTrackPorts;
};
nsresult PlayInternal(bool aCallerIsChrome);
nsresult PlayInternal();
/** Use this method to change the mReadyState member, so required
* events can be fired.
@ -1221,6 +1218,12 @@ protected:
// channel agent is ready to be used.
bool MaybeCreateAudioChannelAgent();
// Determine if the element should be paused because of suspend conditions.
bool ShouldElementBePaused();
// Create or destroy the captured stream depend on mAudioCapturedByWindow.
void AudioCaptureStreamChangeIfNeeded();
/**
* We have different kinds of suspended cases,
* - SUSPENDED_PAUSE
@ -1516,10 +1519,6 @@ protected:
// True iff event delivery is suspended (mPausedForInactiveDocumentOrChannel must also be true).
bool mEventDeliveryPaused;
// True if we've reported a "waiting" event since the last
// readyState change to HAVE_CURRENT_DATA.
bool mWaitingFired;
// True if we're running the "load()" method.
bool mIsRunningLoadMethod;
@ -1615,11 +1614,6 @@ protected:
// playback.
bool mDisableVideo;
// True if we blocked either a play() call or autoplay because the
// media's owner doc was not visible. Only enforced when the pref
// media.block-play-until-visible=true.
bool mPlayBlockedBecauseHidden;
// An agent used to join audio channel service.
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;

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

@ -221,12 +221,11 @@ HTMLVideoElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return HTMLVideoElementBinding::Wrap(aCx, this, aGivenProto);
}
bool
HTMLVideoElement::NotifyOwnerDocumentActivityChangedInternal()
void
HTMLVideoElement::NotifyOwnerDocumentActivityChanged()
{
bool pauseElement = HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal();
HTMLMediaElement::NotifyOwnerDocumentActivityChanged();
UpdateScreenWakeLock();
return pauseElement;
}
FrameStatistics*

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

@ -128,7 +128,7 @@ public:
void SetMozUseScreenWakeLock(bool aValue);
bool NotifyOwnerDocumentActivityChangedInternal() override;
void NotifyOwnerDocumentActivityChanged() override;
// Gives access to the decoder's frame statistics, if present.
FrameStatistics* GetFrameStatistics();

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

@ -30,7 +30,7 @@ function load1Soon() {
function load1Done() {
// Check the title
var title = ctx.tab1Browser.contentWindow.document.title;
var title = ctx.tab1Browser.contentTitle;
checkTitle(title);
// Try loading the same image in a new tab to make sure things work in
@ -49,7 +49,7 @@ function load2Soon() {
function load2Done() {
// Check the title
var title = ctx.tab2Browser.contentWindow.document.title;
var title = ctx.tab2Browser.contentTitle;
checkTitle(title);
// Clean up

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

@ -1303,14 +1303,6 @@ StartMacOSContentSandbox()
MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
}
nsCOMPtr<nsIFile> profileDir;
ContentChild::GetSingleton()->GetProfileDir(getter_AddRefs(profileDir));
nsCString profileDirPath;
rv = profileDir->GetNativePath(profileDirPath);
if (NS_FAILED(rv)) {
MOZ_CRASH("Failed to get profile path");
}
MacSandboxInfo info;
info.type = MacSandboxType_Content;
info.level = info.level = sandboxLevel;
@ -1318,7 +1310,6 @@ StartMacOSContentSandbox()
info.appBinaryPath.assign(appBinaryPath.get());
info.appDir.assign(appDir.get());
info.appTempDir.assign(tempDirPath.get());
info.profileDir.assign(profileDirPath.get());
std::string err;
if (!mozilla::StartMacSandbox(info, err)) {

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

@ -21,9 +21,6 @@
#include "nsWeakPtr.h"
#include "nsIWindowProvider.h"
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
#include "nsIFile.h"
#endif
struct ChromePackage;
class nsIObserver;
@ -117,19 +114,6 @@ public:
void GetProcessName(nsACString& aName) const;
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
void GetProfileDir(nsIFile** aProfileDir) const
{
*aProfileDir = mProfileDir;
NS_IF_ADDREF(*aProfileDir);
}
void SetProfileDir(nsIFile* aProfileDir)
{
mProfileDir = aProfileDir;
}
#endif
bool IsAlive() const;
static void AppendProcessId(nsACString& aName);
@ -697,10 +681,6 @@ private:
nsCOMPtr<nsIDomainPolicy> mPolicy;
nsCOMPtr<nsITimer> mForceKillTimer;
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
nsCOMPtr<nsIFile> mProfileDir;
#endif
// Hashtable to keep track of the pending GetFilesHelper objects.
// This GetFilesHelperChild objects are removed when RecvGetFilesResponse is
// received.

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

@ -114,21 +114,6 @@ ContentProcess::SetAppDir(const nsACString& aPath)
mXREEmbed.SetAppDir(aPath);
}
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
void
ContentProcess::SetProfile(const nsACString& aProfile)
{
bool flag;
nsresult rv =
XRE_GetFileFromPath(aProfile.BeginReading(), getter_AddRefs(mProfileDir));
if (NS_FAILED(rv) ||
NS_FAILED(mProfileDir->Exists(&flag)) || !flag) {
NS_WARNING("Invalid profile directory passed to content process.");
mProfileDir = nullptr;
}
}
#endif
bool
ContentProcess::Init()
{
@ -139,10 +124,6 @@ ContentProcess::Init()
mContent.InitXPCOM();
mContent.InitGraphicsDeviceData();
#if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
mContent.SetProfileDir(mProfileDir);
#endif
#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
SetUpSandboxEnvironment();
#endif

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

@ -39,18 +39,9 @@ public:
void SetAppDir(const nsACString& aPath);
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
void SetProfile(const nsACString& aProfile);
#endif
private:
ContentChild mContent;
mozilla::ipc::ScopedXREEmbed mXREEmbed;
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
nsCOMPtr<nsIFile> mProfileDir;
#endif
#if defined(XP_WIN)
// This object initializes and configures COM.
mozilla::mscom::MainThreadRuntime mCOMRuntime;

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

@ -61,7 +61,6 @@ public:
virtual void NotifyDecodedFrames(const FrameStatisticsData& aStats) = 0;
virtual AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() { return nullptr; };
virtual AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration() { return nullptr; }
// Return an event that will be notified when data arrives in MediaResource.
// MediaDecoderReader will register with this event to receive notifications

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

@ -45,7 +45,7 @@ public:
: mStart(T())
, mEnd(T())
, mFuzz(T())
{}
{ }
template<typename StartArg, typename EndArg>
Interval(StartArg&& aStart, EndArg&& aEnd)
@ -69,7 +69,7 @@ public:
: mStart(aOther.mStart)
, mEnd(aOther.mEnd)
, mFuzz(aOther.mFuzz)
{}
{ }
Interval(SelfType&& aOther)
: mStart(Move(aOther.mStart))
@ -136,8 +136,8 @@ public:
bool Contains(const SelfType& aOther) const
{
return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
(aOther.mEnd - aOther.mFuzz <= mEnd + mFuzz);
return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz)
&& (aOther.mEnd - aOther.mFuzz <= mEnd + mFuzz);
}
bool ContainsStrict(const SelfType& aOther) const
@ -147,14 +147,14 @@ public:
bool ContainsWithStrictEnd(const SelfType& aOther) const
{
return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
aOther.mEnd <= mEnd;
return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz)
&& aOther.mEnd <= mEnd;
}
bool Intersects(const SelfType& aOther) const
{
return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz) &&
(aOther.mStart - aOther.mFuzz < mEnd + mFuzz);
return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz)
&& (aOther.mStart - aOther.mFuzz < mEnd + mFuzz);
}
bool IntersectsStrict(const SelfType& aOther) const
@ -165,8 +165,8 @@ public:
// Same as Intersects, but including the boundaries.
bool Touches(const SelfType& aOther) const
{
return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
(aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz)
&& (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
}
// Returns true if aOther is strictly to the right of this and contiguous.
@ -235,9 +235,9 @@ public:
// of aOther
bool TouchesOnRight(const SelfType& aOther) const
{
return aOther.mStart <= mStart &&
(mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
(aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
return aOther.mStart <= mStart
&& (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz)
&& (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
}
T mStart;
@ -572,7 +572,8 @@ public:
}
}
bool Contains(const ElemType& aInterval) const {
bool Contains(const ElemType& aInterval) const
{
for (const auto& interval : mIntervals) {
if (interval.Contains(aInterval)) {
return true;
@ -581,8 +582,20 @@ public:
return false;
}
bool Contains(const T& aX) const {
bool ContainsStrict(const ElemType& aInterval) const
{
for (const auto& interval : mIntervals) {
if (interval.ContainsStrict(aInterval)) {
return true;
}
}
return false;
}
bool Contains(const T& aX) const
{
for (const auto& interval : mIntervals)
{
if (interval.Contains(aX)) {
return true;
}
@ -590,7 +603,8 @@ public:
return false;
}
bool ContainsStrict(const T& aX) const {
bool ContainsStrict(const T& aX) const
{
for (const auto& interval : mIntervals) {
if (interval.ContainsStrict(aX)) {
return true;
@ -599,7 +613,8 @@ public:
return false;
}
bool ContainsWithStrictEnd(const T& aX) const {
bool ContainsWithStrictEnd(const T& aX) const
{
for (const auto& interval : mIntervals) {
if (interval.ContainsWithStrictEnd(aX)) {
return true;
@ -618,7 +633,8 @@ public:
return *this;
}
void SetFuzz(const T& aFuzz) {
void SetFuzz(const T& aFuzz)
{
for (auto& interval : mIntervals) {
interval.SetFuzz(aFuzz);
}

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

@ -826,9 +826,6 @@ protected:
public:
AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override;
AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration() override {
return &mExplicitDuration;
}
AbstractCanonical<double>* CanonicalVolume() {
return &mVolume;
}
@ -841,6 +838,9 @@ public:
AbstractCanonical<media::NullableTimeUnit>* CanonicalEstimatedDuration() {
return &mEstimatedDuration;
}
AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration() {
return &mExplicitDuration;
}
AbstractCanonical<PlayState>* CanonicalPlayState() {
return &mPlayState;
}

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

@ -2542,6 +2542,12 @@ void MediaDecoderStateMachine::UpdateNextFrameStatus()
if (status != mNextFrameStatus) {
DECODER_LOG("Changed mNextFrameStatus to %s", statusString);
if(status == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING ||
status == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE) {
// Ensure currentTime is up to date prior updating mNextFrameStatus so that
// the MediaDecoderOwner fire events at correct currentTime.
UpdatePlaybackPositionPeriodically();
}
}
mNextFrameStatus = status;

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

@ -77,7 +77,6 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
, mDemuxOnly(false)
, mSeekScheduled(false)
, mVideoFrameContainer(aVideoFrameContainer)
, mExplicitDuration(mTaskQueue, Maybe<double>(), "MediaFormatReader::mExplicitDuration(Mirror)")
{
MOZ_ASSERT(aDemuxer);
MOZ_COUNT_CTOR(MediaFormatReader);
@ -140,8 +139,6 @@ MediaFormatReader::Shutdown()
mPlatform = nullptr;
mVideoFrameContainer = nullptr;
mExplicitDuration.DisconnectIfConnected();
return MediaDecoderReader::Shutdown();
}
@ -254,10 +251,6 @@ MediaFormatReader::AsyncReadMetadata()
return MetadataPromise::CreateAndResolve(metadata, __func__);
}
if (mDecoder->CanonicalExplicitDuration()) {
mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration());
}
RefPtr<MetadataPromise> p = mMetadataPromise.Ensure(__func__);
mDemuxerInitRequest.Begin(mDemuxer->Init()
@ -1083,20 +1076,6 @@ MediaFormatReader::InternalSeek(TrackType aTrack, const InternalSeekTarget& aTar
[self, aTrack] (DemuxerFailureReason aResult) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
if (aResult == DemuxerFailureReason::END_OF_STREAM) {
// We want to enter EOS when performing an
// internal seek only if we're attempting to seek past
// the explicit duration to avoid unwanted ended
// event to be fired.
if (self->mExplicitDuration.Ref().isSome() &&
decoder.mTimeThreshold.ref().Time() <
TimeUnit::FromSeconds(
self->mExplicitDuration.Ref().ref())) {
aResult = DemuxerFailureReason::WAITING_FOR_DATA;
}
}
switch (aResult) {
case DemuxerFailureReason::WAITING_FOR_DATA:
self->NotifyWaitingForData(aTrack);
@ -1754,14 +1733,6 @@ MediaFormatReader::OnSeekFailed(TrackType aTrack, DemuxerFailureReason aResult)
mAudio.mSeekRequest.Complete();
}
// We want to enter EOS when performing a seek only if we're attempting to
// seek past the explicit duration to avoid unwanted ended
// event to be fired.
if (mExplicitDuration.Ref().isSome() &&
mPendingSeekTime.ref() < TimeUnit::FromSeconds(mExplicitDuration.Ref().ref())) {
aResult = DemuxerFailureReason::WAITING_FOR_DATA;
}
if (aResult == DemuxerFailureReason::WAITING_FOR_DATA) {
if (HasVideo() && aTrack == TrackType::kAudioTrack &&
mFallbackSeekTime.isSome() &&

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

@ -9,9 +9,8 @@
#include "mozilla/Atomics.h"
#include "mozilla/Maybe.h"
#include "mozilla/Monitor.h"
#include "mozilla/StateMirroring.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/Monitor.h"
#include "MediaDataDemuxer.h"
#include "MediaDecoderReader.h"
@ -585,9 +584,6 @@ private:
RefPtr<GMPCrashHelper> mCrashHelper;
void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
// The duration explicitly set by JS, mirrored from the main thread.
Mirror<Maybe<double>> mExplicitDuration;
};
} // namespace mozilla

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

@ -399,7 +399,7 @@ VideoSink::UpdateRenderedVideoFrames()
// Skip frames up to the playback position.
int64_t lastDisplayedFrameEndTime = 0;
while (VideoQueue().GetSize() > mMinVideoQueueSize &&
clockTime > VideoQueue().PeekFront()->GetEndTime()) {
clockTime >= VideoQueue().PeekFront()->GetEndTime()) {
RefPtr<MediaData> frame = VideoQueue().PopFront();
if (frame->As<VideoData>()->mSentToCompositor) {
lastDisplayedFrameEndTime = frame->GetEndTime();

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

@ -536,16 +536,13 @@ MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
return;
}
// 3. Update duration to new duration.
// 4. If a user agent is unable to partially render audio frames or text cues
// that start before and end after the duration, then run the following steps:
// Update new duration to the highest end time reported by the buffered
// attribute across all SourceBuffer objects in sourceBuffers.
// 1. Update new duration to the highest end time reported by the buffered
// attribute across all SourceBuffer objects in sourceBuffers.
// 2. Update duration to new duration.
// 3. Let highest end time be the largest track buffer ranges end time across
// all the track buffers across all SourceBuffer objects in sourceBuffers.
double highestEndTime = mSourceBuffers->HighestEndTime();
// 4. If new duration is less than highest end time, then
// 4.1 Update new duration to equal highest end time.
aNewDuration =
std::max(aNewDuration, mSourceBuffers->GetHighestBufferedEndTime());
std::max(aNewDuration, highestEndTime);
// 5. Update the media duration to new duration and run the HTMLMediaElement
// duration change algorithm.

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

@ -277,10 +277,13 @@ MediaSourceDecoder::NextFrameBufferedStatus()
// Next frame hasn't been decoded yet.
// Use the buffered range to consider if we have the next frame available.
TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());
TimeInterval interval(currentPosition,
currentPosition + media::TimeUnit::FromMicroseconds(DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED),
MediaSourceDemuxer::EOS_FUZZ);
return GetBuffered().Contains(ClampIntervalToEnd(interval))
TimeIntervals buffered = GetBuffered();
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
TimeInterval interval(
currentPosition,
currentPosition
+ media::TimeUnit::FromMicroseconds(DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED));
return buffered.ContainsStrict(ClampIntervalToEnd(interval))
? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
: MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
}
@ -308,12 +311,12 @@ MediaSourceDecoder::CanPlayThrough()
}
// If we have data up to the mediasource's duration or 30s ahead, we can
// assume that we can play without interruption.
TimeIntervals buffered = GetBuffered();
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
TimeUnit timeAhead =
std::min(duration, currentPosition + TimeUnit::FromSeconds(30));
TimeInterval interval(currentPosition,
timeAhead,
MediaSourceDemuxer::EOS_FUZZ);
return GetBuffered().Contains(ClampIntervalToEnd(interval));
TimeInterval interval(currentPosition, timeAhead);
return buffered.ContainsStrict(ClampIntervalToEnd(interval));
}
void
@ -336,8 +339,13 @@ MediaSourceDecoder::ClampIntervalToEnd(const TimeInterval& aInterval)
if (!mEnded) {
return aInterval;
}
TimeInterval interval(TimeUnit(), TimeUnit::FromSeconds(GetDuration()));
return aInterval.Intersection(interval);
TimeUnit duration = TimeUnit::FromSeconds(GetDuration());
if (duration < aInterval.mStart) {
return aInterval;
}
return TimeInterval(aInterval.mStart,
std::min(aInterval.mEnd, duration),
aInterval.mFuzz);
}
#undef MSE_DEBUG

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

@ -379,20 +379,28 @@ MediaSourceTrackDemuxer::BreakCycles()
RefPtr<MediaSourceTrackDemuxer::SeekPromise>
MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
{
typedef TrackBuffersManager::GetSampleResult Result;
TimeIntervals buffered = mManager->Buffered(mType);
// Fuzz factor represents a +/- threshold. So when seeking it allows the gap
// to be twice as big as the fuzz value. We only want to allow EOS_FUZZ gap.
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
TimeUnit seekTime = std::max(aTime - mPreRoll, TimeUnit::FromMicroseconds(0));
if (!buffered.Contains(seekTime)) {
if (!buffered.Contains(aTime)) {
if (mManager->IsEnded() && seekTime >= buffered.GetEnd()) {
// We're attempting to seek past the end time. Cap seekTime so that we seek
// to the last sample instead.
seekTime =
std::max(mManager->HighestStartTime(mType) - mPreRoll,
TimeUnit::FromMicroseconds(0));
}
if (!buffered.ContainsWithStrictEnd(seekTime)) {
if (!buffered.ContainsWithStrictEnd(aTime)) {
// We don't have the data to seek to.
return SeekPromise::CreateAndReject(
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
__func__);
}
// Theorically we should reject the promise with WAITING_FOR_DATA,
// Theoretically we should reject the promise with WAITING_FOR_DATA,
// however, to avoid unwanted regressions we assume that if at this time
// we don't have the wanted data it won't come later.
// Instead of using the pre-rolled time, use the earliest time available in
@ -401,13 +409,13 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
MOZ_ASSERT(index != TimeIntervals::NoIndex);
seekTime = buffered[index].mStart;
}
seekTime = mManager->Seek(mType, seekTime, MediaSourceDemuxer::EOS_FUZZ / 2);
bool error;
seekTime = mManager->Seek(mType, seekTime, MediaSourceDemuxer::EOS_FUZZ);
Result result;
RefPtr<MediaRawData> sample =
mManager->GetSample(mType,
media::TimeUnit(),
error);
MOZ_ASSERT(!error && sample);
result);
MOZ_ASSERT(result != Result::ERROR && sample);
mNextSample = Some(sample);
mReset = false;
{
@ -421,33 +429,39 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
RefPtr<MediaSourceTrackDemuxer::SamplesPromise>
MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
{
typedef TrackBuffersManager::GetSampleResult Result;
if (mReset) {
// If a seek (or reset) was recently performed, we ensure that the data
// we are about to retrieve is still available.
TimeIntervals buffered = mManager->Buffered(mType);
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
if (!buffered.Contains(TimeUnit::FromMicroseconds(0))) {
return SamplesPromise::CreateAndReject(
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
if (!buffered.Length() && mManager->IsEnded()) {
return SamplesPromise::CreateAndReject(DemuxerFailureReason::END_OF_STREAM,
__func__);
}
if (!buffered.ContainsWithStrictEnd(TimeUnit::FromMicroseconds(0))) {
return SamplesPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
__func__);
}
mReset = false;
}
bool error = false;
RefPtr<MediaRawData> sample;
if (mNextSample) {
sample = mNextSample.ref();
mNextSample.reset();
} else {
sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, error);
Result result;
sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, result);
if (!sample) {
if (error) {
if (result == Result::ERROR) {
return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
}
return SamplesPromise::CreateAndReject(
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
(result == Result::EOS && mManager->IsEnded())
? DemuxerFailureReason::END_OF_STREAM
: DemuxerFailureReason::WAITING_FOR_DATA, __func__);
}
}
RefPtr<SamplesHolder> samples = new SamplesHolder;
@ -466,8 +480,8 @@ MediaSourceTrackDemuxer::DoSkipToNextRandomAccessPoint(media::TimeUnit aTimeThre
uint32_t parsed = 0;
// Ensure that the data we are about to skip to is still available.
TimeIntervals buffered = mManager->Buffered(mType);
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
if (buffered.Contains(aTimeThreadshold)) {
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
if (buffered.ContainsWithStrictEnd(aTimeThreadshold)) {
bool found;
parsed = mManager->SkipToNextRandomAccessPoint(mType,
aTimeThreadshold,

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

@ -554,6 +554,15 @@ SourceBuffer::HighestStartTime()
: 0.0;
}
double
SourceBuffer::HighestEndTime()
{
MOZ_ASSERT(NS_IsMainThread());
return mTrackBuffersManager
? mTrackBuffersManager->HighestEndTime().ToSeconds()
: 0.0;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)

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

@ -118,6 +118,7 @@ public:
double GetBufferedStart();
double GetBufferedEnd();
double HighestStartTime();
double HighestEndTime();
// Runs the range removal algorithm as defined by the MSE spec.
void RangeRemoval(double aStart, double aEnd);

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

@ -191,6 +191,18 @@ SourceBufferList::HighestStartTime()
return highestStartTime;
}
double
SourceBufferList::HighestEndTime()
{
MOZ_ASSERT(NS_IsMainThread());
double highestEndTime = 0;
for (auto& sourceBuffer : mSourceBuffers) {
highestEndTime =
std::max(sourceBuffer->HighestEndTime(), highestEndTime);
}
return highestEndTime;
}
JSObject*
SourceBufferList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{

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

@ -86,6 +86,7 @@ public:
void ClearSimple();
double HighestStartTime();
double HighestEndTime();
private:
~SourceBufferList();

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

@ -300,11 +300,11 @@ TimeIntervals
TrackBuffersManager::Buffered()
{
MSE_DEBUG("");
MonitorAutoLock mon(mMonitor);
// http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
// 2. Let highest end time be the largest track buffer ranges end time across all the track buffers managed by this SourceBuffer object.
TimeUnit highestEndTime;
TimeUnit highestEndTime = HighestEndTime();
MonitorAutoLock mon(mMonitor);
nsTArray<TimeIntervals*> tracks;
if (HasVideo()) {
tracks.AppendElement(&mVideoBufferedRanges);
@ -312,9 +312,6 @@ TrackBuffersManager::Buffered()
if (HasAudio()) {
tracks.AppendElement(&mAudioBufferedRanges);
}
for (auto trackRanges : tracks) {
highestEndTime = std::max(trackRanges->GetEnd(), highestEndTime);
}
// 3. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
TimeIntervals intersection{TimeInterval(TimeUnit::FromSeconds(0), highestEndTime)};
@ -1896,6 +1893,13 @@ TrackBuffersManager::Buffered(TrackInfo::TrackType aTrack)
return GetTracksData(aTrack).mBufferedRanges;
}
const media::TimeUnit&
TrackBuffersManager::HighestStartTime(TrackInfo::TrackType aTrack)
{
MOZ_ASSERT(OnTaskQueue());
return GetTracksData(aTrack).mHighestStartTimestamp;
}
TimeIntervals
TrackBuffersManager::SafeBuffered(TrackInfo::TrackType aTrack) const
{
@ -1917,6 +1921,25 @@ TrackBuffersManager::HighestStartTime()
return highestStartTime;
}
TimeUnit
TrackBuffersManager::HighestEndTime()
{
MonitorAutoLock mon(mMonitor);
TimeUnit highestEndTime;
nsTArray<TimeIntervals*> tracks;
if (HasVideo()) {
tracks.AppendElement(&mVideoBufferedRanges);
}
if (HasAudio()) {
tracks.AppendElement(&mAudioBufferedRanges);
}
for (auto trackRanges : tracks) {
highestEndTime = std::max(trackRanges->GetEnd(), highestEndTime);
}
return highestEndTime;
}
const TrackBuffersManager::TrackBuffer&
TrackBuffersManager::GetTrackBuffer(TrackInfo::TrackType aTrack)
{
@ -1963,12 +1986,14 @@ TrackBuffersManager::Seek(TrackInfo::TrackType aTrack,
if (aTime != TimeUnit()) {
// Determine the interval of samples we're attempting to seek to.
TimeIntervals buffered = trackBuffer.mBufferedRanges;
// Fuzz factor is +/- aFuzz; as we want to only eliminate gaps
// that are less than aFuzz wide, we set a fuzz factor aFuzz/2.
buffered.SetFuzz(aFuzz / 2);
TimeIntervals::IndexType index = buffered.Find(aTime);
buffered.SetFuzz(aFuzz);
index = buffered.Find(aTime);
MOZ_ASSERT(index != TimeIntervals::NoIndex);
MOZ_ASSERT(index != TimeIntervals::NoIndex,
"We shouldn't be called if aTime isn't buffered");
TimeInterval target = buffered[index];
target.mFuzz = aFuzz;
i = FindSampleIndex(track, target);
}
@ -2061,8 +2086,10 @@ TrackBuffersManager::SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack,
// SkipToNextRandomAccessPoint will not count again the parsed sample as
// skipped.
if (aFound) {
trackData.mNextSampleTimecode = nextSampleTimecode;
trackData.mNextSampleTime = nextSampleTime;
trackData.mNextSampleTimecode =
TimeUnit::FromMicroseconds(track[i]->mTimecode);
trackData.mNextSampleTime =
TimeUnit::FromMicroseconds(track[i]->mTime);
trackData.mNextGetSampleIndex = Some(i);
} else if (i > 0) {
// Go back to the previous keyframe or the original position so the next
@ -2116,17 +2143,19 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
already_AddRefed<MediaRawData>
TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
const TimeUnit& aFuzz,
bool& aError)
GetSampleResult& aResult)
{
MOZ_ASSERT(OnTaskQueue());
auto& trackData = GetTracksData(aTrack);
const TrackBuffer& track = GetTrackBuffer(aTrack);
aError = false;
aResult = GetSampleResult::WAITING_FOR_DATA;
if (!track.Length()) {
aResult = GetSampleResult::EOS;
return nullptr;
}
if (trackData.mNextGetSampleIndex.isNothing() &&
trackData.mNextSampleTimecode == TimeUnit()) {
// First demux, get first sample.
@ -2134,6 +2163,10 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
}
if (trackData.mNextGetSampleIndex.isSome()) {
if (trackData.mNextGetSampleIndex.ref() >= track.Length()) {
aResult = GetSampleResult::EOS;
return nullptr;
}
const MediaRawData* sample =
GetSample(aTrack,
trackData.mNextGetSampleIndex.ref(),
@ -2146,18 +2179,44 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
RefPtr<MediaRawData> p = sample->Clone();
if (!p) {
aError = true;
aResult = GetSampleResult::ERROR;
return nullptr;
}
trackData.mNextGetSampleIndex.ref()++;
// Estimate decode timestamp of the next sample.
trackData.mNextSampleTimecode =
// Estimate decode timestamp and timestamp of the next sample.
TimeUnit nextSampleTimecode =
TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration);
trackData.mNextSampleTime =
TimeUnit nextSampleTime =
TimeUnit::FromMicroseconds(sample->GetEndTime());
const MediaRawData* nextSample =
GetSample(aTrack,
trackData.mNextGetSampleIndex.ref(),
nextSampleTimecode,
nextSampleTime,
aFuzz);
if (nextSample) {
// We have a valid next sample, can use exact values.
trackData.mNextSampleTimecode =
TimeUnit::FromMicroseconds(nextSample->mTimecode);
trackData.mNextSampleTime =
TimeUnit::FromMicroseconds(nextSample->mTime);
} else {
// Next sample isn't available yet. Use estimates.
trackData.mNextSampleTimecode = nextSampleTimecode;
trackData.mNextSampleTime = nextSampleTime;
}
aResult = GetSampleResult::NO_ERROR;
return p.forget();
}
if (trackData.mNextSampleTimecode.ToMicroseconds() >
track.LastElement()->mTimecode + track.LastElement()->mDuration) {
// The next element is past our last sample. We're done.
trackData.mNextGetSampleIndex = Some(uint32_t(track.Length()));
aResult = GetSampleResult::EOS;
return nullptr;
}
// Our previous index has been overwritten, attempt to find the new one.
int32_t pos = FindCurrentPosition(aTrack, aFuzz);
if (pos < 0) {
@ -2171,7 +2230,7 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
RefPtr<MediaRawData> p = sample->Clone();
if (!p) {
// OOM
aError = true;
aResult = GetSampleResult::ERROR;
return nullptr;
}
trackData.mNextGetSampleIndex = Some(uint32_t(pos)+1);
@ -2179,6 +2238,7 @@ TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration);
trackData.mNextSampleTime =
TimeUnit::FromMicroseconds(sample->GetEndTime());
aResult = GetSampleResult::NO_ERROR;
return p.forget();
}
@ -2190,6 +2250,23 @@ TrackBuffersManager::FindCurrentPosition(TrackInfo::TrackType aTrack,
auto& trackData = GetTracksData(aTrack);
const TrackBuffer& track = GetTrackBuffer(aTrack);
// Perform an exact search first.
for (uint32_t i = 0; i < track.Length(); i++) {
const RefPtr<MediaRawData>& sample = track[i];
TimeInterval sampleInterval{
TimeUnit::FromMicroseconds(sample->mTimecode),
TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration)};
if (sampleInterval.ContainsStrict(trackData.mNextSampleTimecode)) {
return i;
}
if (sampleInterval.mStart > trackData.mNextSampleTimecode) {
// Samples are ordered by timecode. There's no need to search
// any further.
break;
}
}
for (uint32_t i = 0; i < track.Length(); i++) {
const RefPtr<MediaRawData>& sample = track[i];
TimeInterval sampleInterval{
@ -2200,6 +2277,11 @@ TrackBuffersManager::FindCurrentPosition(TrackInfo::TrackType aTrack,
if (sampleInterval.ContainsWithStrictEnd(trackData.mNextSampleTimecode)) {
return i;
}
if (sampleInterval.mStart - aFuzz > trackData.mNextSampleTimecode) {
// Samples are ordered by timecode. There's no need to search
// any further.
break;
}
}
// We couldn't find our sample by decode timestamp. Attempt to find it using

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

@ -123,6 +123,7 @@ public:
// Buffered must conform to http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
media::TimeIntervals Buffered();
media::TimeUnit HighestStartTime();
media::TimeUnit HighestEndTime();
// Return the size of the data managed by this SourceBufferContentManager.
int64_t GetSize() const;
@ -139,6 +140,7 @@ public:
MediaInfo GetMetadata();
const TrackBuffer& GetTrackBuffer(TrackInfo::TrackType aTrack);
const media::TimeIntervals& Buffered(TrackInfo::TrackType);
const media::TimeUnit& HighestStartTime(TrackInfo::TrackType);
media::TimeIntervals SafeBuffered(TrackInfo::TrackType) const;
bool IsEnded() const
{
@ -151,9 +153,18 @@ public:
const media::TimeUnit& aTimeThreadshold,
const media::TimeUnit& aFuzz,
bool& aFound);
enum class GetSampleResult
{
NO_ERROR,
ERROR,
WAITING_FOR_DATA,
EOS
};
already_AddRefed<MediaRawData> GetSample(TrackInfo::TrackType aTrack,
const media::TimeUnit& aFuzz,
bool& aError);
GetSampleResult& aResult);
int32_t FindCurrentPosition(TrackInfo::TrackType aTrack,
const media::TimeUnit& aFuzz);
media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack,

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

@ -104,6 +104,8 @@ skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not
skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
[test_SeekedEvent_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
[test_SeekToEnd_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
[test_SeekTwice_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
[test_Sequence_mp4.html]
@ -126,6 +128,8 @@ skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not
skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized.
[test_WaitingOnMissingData_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
[test_WaitingOnMissingDataEnded_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
[test_WaitingToEndedTransition_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3

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

@ -0,0 +1,57 @@
<!DOCTYPE HTML>
<html>
<head>
<title>MSE: seeking to end of data with data gap.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="mediasource.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
runWithMSE(function(ms, el) {
once(ms, 'sourceopen').then(function() {
ok(true, "Receive a sourceopen event");
var videosb = ms.addSourceBuffer("video/mp4");
var audiosb = ms.addSourceBuffer("audio/mp4");
fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4')
.then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', range(1, 6), '.m4s'))
.then(fetchAndLoad.bind(null, audiosb, 'bipbop/bipbop_audio', ['init'], '.mp4'))
.then(function() {
is(videosb.buffered.length, 1, "continuous buffered range");
// Ensure we have at least 2s less audio than video.
audiosb.appendWindowEnd = videosb.buffered.end(0) - 2;
return fetchAndLoad(audiosb, 'bipbop/bipbop_audio', range(1, 6), '.m4s');
}).then(function() {
ms.endOfStream();
return Promise.all([once(el, "durationchange"), once(ms, "sourceended")]);
}).then(function() {
ok(true, "endOfStream completed");
// Seek to the middle of the gap where audio is missing. As we are in readyState = ended
// seeking must complete.
el.currentTime = videosb.buffered.end(0) / 2 + audiosb.buffered.end(0) / 2;
ok(el.currentTime - audiosb.buffered.end(0) > 1, "gap is big enough");
is(el.buffered.length, 1, "continuous buffered range");
is(el.buffered.end(0), videosb.buffered.end(0), "buffered range end is aligned with longest track");
ok(el.seeking, "element is now seeking");
ok(el.currentTime >= el.buffered.start(0) && el.currentTime <= el.buffered.end(0), "seeking time is in buffered range");
ok(el.currentTime > audiosb.buffered.end(0), "seeking point is not buffered in audio track");
return once(el, 'seeked');
}).then(function() {
ok(true, "we have successfully seeked");
// Now ensure that we can play to the end, even though we are missing data in one track.
el.play();
once(el, 'ended').then(SimpleTest.finish.bind(SimpleTest));
});
});
});
</script>
</pre>
</body>
</html>

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

@ -15,13 +15,12 @@ SimpleTest.waitForExplicitFinish();
runWithMSE(function(ms, el) {
var threshold = 0.5; // gap threshold in seconds.
// duration of a frame. The FFmpeg decoder can't calculate properly calculate the duration of the last frame.
var fuzz = 33322 / 1000000;
var fuzz = 0.000001; // fuzz when comparing double.
once(ms, 'sourceopen').then(function() {
ok(true, "Receive a sourceopen event");
var videosb = ms.addSourceBuffer("video/mp4");
var vchunks = [ {start: 0, end: 3.2033}, { start: 3.2033, end: 6.4066}];
var vchunks = [ {start: 0, end: 3.203333}, { start: 3.203333, end: 6.406666}];
fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4')
.then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', range(1, 5), '.m4s'))
@ -40,8 +39,10 @@ var fuzz = 33322 / 1000000;
}).then(function() {
return once(el, 'waiting');
}).then(function() {
// We're waiting for data at the end of the last segment.
isfuzzy(el.currentTime, vchunks[1].end + threshold, fuzz, "skipped the gap properly");
// We're waiting for data after the start of the last frame.
// 0.033333 is the duration of the last frame.
ok(el.currentTime >= vchunks[1].end - 0.033333 + threshold - fuzz
&& el.currentTime <= vchunks[1].end + threshold + fuzz, "skipped the gap properly: " + el.currentTime + " " + (vchunks[1].end + threshold));
is(el.buffered.length, 2, "buffered range has right length");
// Now we test that seeking will succeed despite the gap.
el.currentTime = el.buffered.end(0) + (threshold / 2);
@ -68,8 +69,10 @@ var fuzz = 33322 / 1000000;
}).then(function() {
return once(el, 'waiting');
}).then(function() {
// We're waiting for data at the end of the first segment as the gap is too big.
isfuzzy(el.currentTime, vchunks[0].end, fuzz, "stopped at the gap properly");
// We're waiting for data after the start of the last frame.
// 0.033333 is the duration of the last frame.
ok(el.currentTime >= vchunks[0].end - 0.033333 - fuzz
&& el.currentTime <= vchunks[0].end + fuzz, "stopped at the gap properly: " + el.currentTime + " " + vchunks[0].end);
SimpleTest.finish();
});
});

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

@ -71,9 +71,11 @@ runWithMSE(function(ms, el) {
return Promise.all([audiosb.updating ? once(audiosb, 'updateend') : Promise.resolve(),
videosb.updating ? once(videosb, 'updateend') : Promise.resolve()]);
}).then(function() {
ms.endOfStream();
once(el, 'ended').then(SimpleTest.finish.bind(SimpleTest));
info("waiting for play to complete");
el.play();
el.currentTime = el.buffered.start(0);
ms.endOfStream();
Promise.all([once(el, 'ended'), once(el, 'seeked')]).then(SimpleTest.finish.bind(SimpleTest));
});
});
});

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

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
<title>MSE: |waiting| event when source data is missing</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="mediasource.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test"><script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
runWithMSE(function(ms, el) {
el.controls = true;
once(ms, 'sourceopen').then(function() {
ok(true, "Receive a sourceopen event");
el.addEventListener("ended", function () {
ok(false, "ended should never fire");
SimpleTest.finish();
});
var videosb = ms.addSourceBuffer("video/mp4");
fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4')
.then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', range(1, 5), '.m4s'))
.then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', range(6, 8), '.m4s'))
.then(function() {
is(el.buffered.length, 2, "discontinuous buffered range");
ms.endOfStream();
return Promise.all([once(el, "durationchange"), once(ms, "sourceended")]);
}).then(function() {
// HTMLMediaElement fires 'waiting' if somebody invokes |play()| before the MDSM
// has notified it of available data. Make sure that we get 'playing' before
// we starting waiting for 'waiting'.
info("Invoking play()");
el.play();
return once(el, 'playing');
}).then(function() {
ok(true, "Video playing. It should play for a bit, then fire 'waiting'");
return once(el, 'waiting');
}).then(function() {
// waiting is fired when we start to play the last frame.
// 0.033334 is the duration of the last frame, + 0.000001 of fuzz.
// the next video frame, currentTime can be up to 1 frame's worth earlier than end of video.
isfuzzy(el.currentTime, videosb.buffered.end(0), 0.033334, "waiting was fired on gap");
SimpleTest.finish();
});
});
});
</script>
</pre>
</body>
</html>

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

@ -23,10 +23,6 @@
#include "prenv.h"
#include "nsXPCOMPrivate.h"
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
#include "nsAppDirectoryServiceDefs.h"
#endif
#include "nsExceptionHandler.h"
#include "nsDirectoryServiceDefs.h"
@ -612,20 +608,6 @@ AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
aCmdLine.push_back(path.get());
#endif
}
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
// Full path to the profile dir
nsCOMPtr<nsIFile> profileDir;
rv = directoryService->Get(NS_APP_USER_PROFILE_50_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(profileDir));
if (NS_SUCCEEDED(rv)) {
nsAutoCString path;
MOZ_ALWAYS_SUCCEEDS(profileDir->GetNativePath(path));
aCmdLine.push_back("-profile");
aCmdLine.push_back(path.get());
}
#endif
}
}
}

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

@ -43,7 +43,7 @@ public class FxAccountDeviceRegistrator implements BundleEventListener {
// The current version of the device registration, we use this to re-register
// devices after we update what we send on device registration.
public static final Integer DEVICE_REGISTRATION_VERSION = 1;
public static final Integer DEVICE_REGISTRATION_VERSION = 2;
private static FxAccountDeviceRegistrator instance;
private final WeakReference<Context> context;

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

@ -317,10 +317,6 @@ pref("media.wakelock_timeout", 2000);
// opened as top-level documents, as opposed to inside a media element.
pref("media.play-stand-alone", true);
// Whether we should delay actioning a "play()" JS function call and autoplay
// attribute until the media element's owner document is visible.
pref("media.block-play-until-visible", false);
pref("media.hardware-video-decoding.enabled", true);
pref("media.hardware-video-decoding.force-enabled", false);
@ -5539,6 +5535,7 @@ pref("dom.webkitBlink.dirPicker.enabled", true);
pref("dom.webkitBlink.filesystem.enabled", true);
#endif
pref("media.block-autoplay-until-in-foreground", true);
#ifdef MOZ_STYLO
// Is the Servo-backed style system enabled?
pref("layout.css.servo.enabled", true);

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

@ -41,8 +41,7 @@ typedef struct _MacSandboxInfo {
_MacSandboxInfo(const struct _MacSandboxInfo& other)
: type(other.type), level(other.level), pluginInfo(other.pluginInfo),
appPath(other.appPath), appBinaryPath(other.appBinaryPath),
appDir(other.appDir), appTempDir(other.appTempDir),
profileDir(other.profileDir) {}
appDir(other.appDir), appTempDir(other.appTempDir) {}
MacSandboxType type;
int32_t level;
MacSandboxPluginInfo pluginInfo;
@ -50,7 +49,6 @@ typedef struct _MacSandboxInfo {
std::string appBinaryPath;
std::string appDir;
std::string appTempDir;
std::string profileDir;
} MacSandboxInfo;
namespace mozilla {

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

@ -157,7 +157,6 @@ static const char contentSandboxRules[] =
"(define appBinaryPath \"%s\")\n"
"(define appDir \"%s\")\n"
"(define appTempDir \"%s\")\n"
"(define profileDir \"%s\")\n"
"(define home-path \"%s\")\n"
"\n"
"; Allow read access to standard system paths.\n"
@ -233,9 +232,6 @@ static const char contentSandboxRules[] =
" (define (home-literal home-relative-literal)\n"
" (resolving-literal (string-append home-path home-relative-literal)))\n"
"\n"
" (define (profile-subpath profile-relative-subpath)\n"
" (resolving-subpath (string-append profileDir profile-relative-subpath)))\n"
"\n"
" (define (container-regex container-relative-regex)\n"
" (resolving-regex (string-append \"^\" (regex-quote container-path) container-relative-regex)))\n"
" (define (container-subpath container-relative-subpath)\n"
@ -375,17 +371,16 @@ static const char contentSandboxRules[] =
" (allow file-read*\n"
" (home-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
" (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
" (profile-subpath \"/extensions\")\n"
" (profile-subpath \"/weave\"))\n"
" (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/extensions/\")\n"
" (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/weave/\"))\n"
"\n"
"; the following rules should be removed when printing and\n"
"; the following rules should be removed when printing and \n"
"; opening a file from disk are brokered through the main process\n"
" (if\n"
" (< sandbox-level 2)\n"
" (allow file*\n"
" (require-all\n"
" (require-not (home-subpath \"/Library\"))\n"
" (require-not (subpath profileDir))))\n"
" (require-not\n"
" (home-subpath \"/Library\")))\n"
" (allow file*\n"
" (require-all\n"
" (subpath home-path)\n"
@ -502,7 +497,6 @@ bool StartMacSandbox(MacSandboxInfo aInfo, std::string &aErrorMessage)
aInfo.appBinaryPath.c_str(),
aInfo.appDir.c_str(),
aInfo.appTempDir.c_str(),
aInfo.profileDir.c_str(),
getenv("HOME"));
} else {
fprintf(stderr,

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

@ -38,6 +38,8 @@ all-tests:
asan-tests:
- cppunit
- crashtest
- firefox-ui-functional-local
- firefox-ui-functional-remote
- gtest
- jittests
- jsreftest

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

@ -3,6 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import re
from firefox_ui_harness.testcases import FirefoxTestCase
from marionette_driver import Wait
@ -11,32 +12,31 @@ from marionette_driver import Wait
class TestSafeBrowsingInitialDownload(FirefoxTestCase):
test_data = [{
'platforms': ['linux', 'windows_nt', 'darwin'],
'files': [
# Phishing
"goog-badbinurl-shavar.pset",
"goog-badbinurl-shavar.sbstore",
"goog-malware-shavar.pset",
"goog-malware-shavar.sbstore",
"goog-phish-shavar.pset",
"goog-phish-shavar.sbstore",
"goog-unwanted-shavar.pset",
"goog-unwanted-shavar.sbstore",
'platforms': ['linux', 'windows_nt', 'darwin'],
'files': [
# Phishing
r'goog-badbinurl-shavar.pset',
r'goog-badbinurl-shavar.sbstore',
r'goog-malware-shavar.pset',
r'goog-malware-shavar.sbstore',
r'goog(pub)?-phish-shavar.pset',
r'goog(pub)?-phish-shavar.sbstore',
r'goog-unwanted-shavar.pset',
r'goog-unwanted-shavar.sbstore',
# Tracking Protections
"base-track-digest256.pset",
"base-track-digest256.sbstore",
"mozstd-trackwhite-digest256.pset",
"mozstd-trackwhite-digest256.sbstore"
]
},
{
'platforms': ['windows_nt'],
'files': [
"goog-downloadwhite-digest256.pset",
"goog-downloadwhite-digest256.sbstore"
]
}
# Tracking Protections
r'base-track-digest256.pset',
r'base-track-digest256.sbstore',
r'mozstd-trackwhite-digest256.pset',
r'mozstd-trackwhite-digest256.sbstore'
],
}, {
'platforms': ['windows_nt'],
'files': [
r'goog-downloadwhite-digest256.pset',
r'goog-downloadwhite-digest256.sbstore'
]
},
]
browser_prefs = {
@ -72,5 +72,5 @@ class TestSafeBrowsingInitialDownload(FirefoxTestCase):
continue
for item in data['files']:
wait.until(
lambda _: os.path.exists(os.path.join(self.sb_files_path, item)),
lambda _: [f for f in os.listdir(self.sb_files_path) if re.search(item, f)],
message='Safe Browsing File: {} not found!'.format(item))

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

@ -17,6 +17,8 @@ class GeckoInstance(object):
required_prefs = {
"browser.sessionstore.resume_from_crash": False,
"browser.shell.checkDefaultBrowser": False,
# Needed for branded builds to prevent opening a second tab on startup
"browser.startup.homepage_override.mstone": "ignore",
"browser.startup.page": 0,
"browser.tabs.remote.autostart.1": False,
"browser.tabs.remote.autostart.2": False,

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

@ -0,0 +1,94 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import pytest
from mock import Mock, MagicMock
from marionette_driver.marionette import Marionette
@pytest.fixture(scope='module')
def logger():
"""
Fake logger to help with mocking out other runner-related classes.
"""
import mozlog
return Mock(spec=mozlog.structuredlog.StructuredLogger)
@pytest.fixture
def mach_parsed_kwargs(logger):
"""
Parsed and verified dictionary used during simplest
call to mach marionette-test
"""
return {
'adb_path': None,
'addon': None,
'address': None,
'app': None,
'app_args': [],
'avd': None,
'avd_home': None,
'binary': u'/path/to/firefox',
'browsermob_port' : None,
'browsermob_script' : None,
'device_serial': None,
'e10s': True,
'emulator': False,
'emulator_bin': None,
'gecko_log': None,
'jsdebugger': False,
'log_errorsummary': None,
'log_html': None,
'log_mach': None,
'log_mach_buffer': None,
'log_mach_level': None,
'log_mach_verbose': None,
'log_raw': None,
'log_raw_level': None,
'log_tbpl': None,
'log_tbpl_buffer': None,
'log_tbpl_compact': None,
'log_tbpl_level': None,
'log_unittest': None,
'log_xunit': None,
'logger_name': 'Marionette-based Tests',
'prefs': {
'browser.tabs.remote.autostart': True,
'browser.tabs.remote.force-enable': True,
'extensions.e10sBlocksEnabling': False,
},
'prefs_args': None,
'prefs_files': None,
'profile': None,
'pydebugger': None,
'repeat': 0,
'server_root': None,
'shuffle': False,
'shuffle_seed': 2276870381009474531,
'socket_timeout': 360.0,
'sources': None,
'startup_timeout': 60,
'symbols_path': None,
'test_tags': None,
'tests': [u'/path/to/unit-tests.ini'],
'testvars': None,
'this_chunk': None,
'timeout': None,
'total_chunks': None,
'verbose': None,
'workspace': None,
'logger': logger,
}
@pytest.fixture
def mock_marionette(request):
""" Mock marionette instance """
marionette = MagicMock(spec=Marionette)
if 'has_crashed' in request.funcargnames:
marionette.check_for_crash.return_value = request.getfuncargvalue(
'has_crashed'
)
return marionette

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

@ -0,0 +1,32 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import pytest
from marionette.runtests import MarionetteArguments
@pytest.mark.parametrize("socket_timeout", ['A', '10', '1B-', '1C2', '44.35'])
def test_parse_arg_socket_timeout(socket_timeout):
argv = ['marionette', '--socket-timeout', socket_timeout]
parser = MarionetteArguments()
def _is_float_convertible(value):
try:
float(value)
return True
except:
return False
if not _is_float_convertible(socket_timeout):
with pytest.raises(SystemExit) as ex:
parser.parse_args(args=argv)
assert ex.value.code == 2
else:
args = parser.parse_args(args=argv)
assert hasattr(args, 'socket_timeout') and args.socket_timeout == float(socket_timeout)
if __name__ == '__main__':
import sys
sys.exit(pytest.main(['--verbose', __file__]))

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

@ -0,0 +1,110 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import pytest
from mock import Mock, patch, sentinel
from marionette.runtests import MarionetteTestRunner, MarionetteHarness, cli
# avoid importing MarionetteJSTestCase to prevent pytest from
# collecting and running it as part of this test suite
import marionette.marionette_test as marionette_test
@pytest.fixture
def harness_class(request):
"""
Mock based on MarionetteHarness whose run method just returns a number of
failures according to the supplied test parameter
"""
if 'num_fails_crashed' in request.funcargnames:
num_fails_crashed = request.getfuncargvalue('num_fails_crashed')
else:
num_fails_crashed = (0, 0)
harness_cls = Mock(spec=MarionetteHarness)
harness = harness_cls.return_value
if num_fails_crashed is None:
harness.run.side_effect = Exception
else:
harness.run.return_value = sum(num_fails_crashed)
return harness_cls
@pytest.fixture
def runner_class(request):
"""
Mock based on MarionetteTestRunner, wherein the runner.failed,
runner.crashed attributes are provided by a test parameter
"""
if 'num_fails_crashed' in request.funcargnames:
failures, crashed = request.getfuncargvalue('num_fails_crashed')
else:
failures = 0
crashed = 0
mock_runner_class = Mock(spec=MarionetteTestRunner)
runner = mock_runner_class.return_value
runner.failed = failures
runner.crashed = crashed
return mock_runner_class
@pytest.mark.parametrize(
"num_fails_crashed,exit_code",
[((0, 0), 0), ((1, 0), 10), ((0, 1), 10), (None, 1)],
)
def test_cli_exit_code(num_fails_crashed, exit_code, harness_class):
with pytest.raises(SystemExit) as err:
cli(harness_class=harness_class)
assert err.value.code == exit_code
@pytest.mark.parametrize("num_fails_crashed", [(0, 0), (1, 0), (1, 1)])
def test_call_harness_with_parsed_args_yields_num_failures(mach_parsed_kwargs,
runner_class,
num_fails_crashed):
with patch(
'marionette.runtests.MarionetteHarness.parse_args'
) as parse_args:
failed_or_crashed = MarionetteHarness(runner_class,
args=mach_parsed_kwargs).run()
parse_args.assert_not_called()
assert failed_or_crashed == sum(num_fails_crashed)
def test_call_harness_with_no_args_yields_num_failures(runner_class):
with patch(
'marionette.runtests.MarionetteHarness.parse_args',
return_value={'tests': []}
) as parse_args:
failed_or_crashed = MarionetteHarness(runner_class).run()
assert parse_args.call_count == 1
assert failed_or_crashed == 0
def test_args_passed_to_runner_class(mach_parsed_kwargs, runner_class):
arg_list = mach_parsed_kwargs.keys()
arg_list.remove('tests')
mach_parsed_kwargs.update([(a, getattr(sentinel, a)) for a in arg_list])
harness = MarionetteHarness(runner_class, args=mach_parsed_kwargs)
harness.process_args = Mock()
harness.run()
for arg in arg_list:
assert harness._runner_class.call_args[1][arg] is getattr(sentinel, arg)
def test_harness_sets_up_default_test_handlers(mach_parsed_kwargs):
"""
If the necessary TestCase is not in test_handlers,
tests are omitted silently
"""
harness = MarionetteHarness(args=mach_parsed_kwargs)
mach_parsed_kwargs.pop('tests')
runner = harness._runner_class(**mach_parsed_kwargs)
assert marionette_test.MarionetteTestCase in runner.test_handlers
assert marionette_test.MarionetteJSTestCase in runner.test_handlers
if __name__ == '__main__':
import sys
sys.exit(pytest.main(['--verbose', __file__]))

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

@ -2,132 +2,11 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import pytest
from mock import patch, Mock, DEFAULT, mock_open, MagicMock, sentinel
from marionette.runtests import (
MarionetteTestRunner,
MarionetteHarness,
MarionetteArguments,
cli
)
from marionette.runner import MarionetteTestResult
from marionette_driver.marionette import Marionette
from manifestparser import TestManifest
from mock import Mock, patch, mock_open, sentinel, DEFAULT
# avoid importing MarionetteJSTestCase to prevent pytest from
# collecting and running it as part of this test suite
import marionette.marionette_test as marionette_test
def _check_crash_counts(has_crashed, runner, mock_marionette):
if has_crashed:
assert mock_marionette.check_for_crash.call_count == 1
assert runner.crashed == 1
else:
assert runner.crashed == 0
@pytest.fixture
def mock_marionette(request):
""" Mock marionette instance """
marionette = MagicMock(spec=Marionette)
if 'has_crashed' in request.funcargnames:
marionette.check_for_crash.return_value = request.getfuncargvalue(
'has_crashed'
)
return marionette
@pytest.fixture
def empty_marionette_testcase():
""" Testable MarionetteTestCase class """
class EmptyTestCase(marionette_test.MarionetteTestCase):
def test_nothing(self):
pass
return EmptyTestCase
@pytest.fixture
def empty_marionette_test(mock_marionette, empty_marionette_testcase):
return empty_marionette_testcase(lambda: mock_marionette, 'test_nothing')
@pytest.fixture(scope='module')
def logger():
"""
Fake logger to help with mocking out other runner-related classes.
"""
import mozlog
return Mock(spec=mozlog.structuredlog.StructuredLogger)
@pytest.fixture
def mach_parsed_kwargs(logger):
"""
Parsed and verified dictionary used during simplest
call to mach marionette-test
"""
return {
'adb_path': None,
'addon': None,
'address': None,
'app': None,
'app_args': [],
'avd': None,
'avd_home': None,
'binary': u'/path/to/firefox',
'browsermob_port' : None,
'browsermob_script' : None,
'device_serial': None,
'e10s': True,
'emulator': False,
'emulator_bin': None,
'gecko_log': None,
'jsdebugger': False,
'log_errorsummary': None,
'log_html': None,
'log_mach': None,
'log_mach_buffer': None,
'log_mach_level': None,
'log_mach_verbose': None,
'log_raw': None,
'log_raw_level': None,
'log_tbpl': None,
'log_tbpl_buffer': None,
'log_tbpl_compact': None,
'log_tbpl_level': None,
'log_unittest': None,
'log_xunit': None,
'logger_name': 'Marionette-based Tests',
'package_name': None,
'prefs': {
'browser.tabs.remote.autostart': True,
'browser.tabs.remote.force-enable': True,
'extensions.e10sBlocksEnabling': False,
},
'prefs_args': None,
'prefs_files': None,
'profile': None,
'pydebugger': None,
'repeat': 0,
'server_root': None,
'shuffle': False,
'shuffle_seed': 2276870381009474531,
'socket_timeout': 360.0,
'sources': None,
'startup_timeout': 60,
'symbols_path': None,
'test_tags': None,
'tests': [u'/path/to/unit-tests.ini'],
'testvars': None,
'this_chunk': None,
'timeout': None,
'total_chunks': None,
'verbose': None,
'workspace': None,
'logger': logger,
}
from marionette.runtests import MarionetteTestRunner
import manifestparser
@pytest.fixture
@ -153,118 +32,6 @@ def mock_runner(runner, mock_marionette, monkeypatch):
return runner
@pytest.fixture
def harness_class(request):
"""
Mock based on MarionetteHarness whose run method just returns a number of
failures according to the supplied test parameter
"""
if 'num_fails_crashed' in request.funcargnames:
num_fails_crashed = request.getfuncargvalue('num_fails_crashed')
else:
num_fails_crashed = (0, 0)
harness_cls = Mock(spec=MarionetteHarness)
harness = harness_cls.return_value
if num_fails_crashed is None:
harness.run.side_effect = Exception
else:
harness.run.return_value = sum(num_fails_crashed)
return harness_cls
@pytest.fixture
def runner_class(request):
"""
Mock based on MarionetteTestRunner, wherein the runner.failed,
runner.crashed attributes are provided by a test parameter
"""
if 'num_fails_crashed' in request.funcargnames:
failures, crashed = request.getfuncargvalue('num_fails_crashed')
else:
failures = 0
crashed = 0
mock_runner_class = Mock(spec=MarionetteTestRunner)
runner = mock_runner_class.return_value
runner.failed = failures
runner.crashed = crashed
return mock_runner_class
@pytest.mark.parametrize(
"num_fails_crashed,exit_code",
[((0, 0), 0), ((1, 0), 10), ((0, 1), 10), (None, 1)],
)
def test_cli_exit_code(num_fails_crashed, exit_code, harness_class):
with pytest.raises(SystemExit) as err:
cli(harness_class=harness_class)
assert err.value.code == exit_code
@pytest.mark.parametrize("num_fails_crashed", [(0, 0), (1, 0), (1, 1)])
def test_call_harness_with_parsed_args_yields_num_failures(mach_parsed_kwargs,
runner_class,
num_fails_crashed):
with patch(
'marionette.runtests.MarionetteHarness.parse_args'
) as parse_args:
failed_or_crashed = MarionetteHarness(runner_class,
args=mach_parsed_kwargs).run()
parse_args.assert_not_called()
assert failed_or_crashed == sum(num_fails_crashed)
@pytest.mark.parametrize("sock_timeout_value", ['A', '10', '1B-', '1C2', '44.35'])
def test_parse_arg_socket_timeout_with_multiple_values(sock_timeout_value):
argv = ['marionette', '--socket-timeout', sock_timeout_value]
parser = MarionetteArguments()
def _is_float_convertible(value):
try:
float(value)
return True
except:
return False
if not _is_float_convertible(sock_timeout_value):
# should raising exception, since sock_timeout must be convertible to float.
with pytest.raises(SystemExit) as ex:
parser.parse_args(args=argv)
assert ex.value.code == 2
else:
# should pass without raising exception.
args = parser.parse_args(args=argv)
assert hasattr(args, 'socket_timeout') and args.socket_timeout == float(sock_timeout_value)
def test_call_harness_with_no_args_yields_num_failures(runner_class):
with patch(
'marionette.runtests.MarionetteHarness.parse_args',
return_value={'tests': []}
) as parse_args:
failed_or_crashed = MarionetteHarness(runner_class).run()
assert parse_args.call_count == 1
assert failed_or_crashed == 0
def test_args_passed_to_runner_class(mach_parsed_kwargs, runner_class):
arg_list = mach_parsed_kwargs.keys()
arg_list.remove('tests')
mach_parsed_kwargs.update([(a, getattr(sentinel, a)) for a in arg_list])
harness = MarionetteHarness(runner_class, args=mach_parsed_kwargs)
harness.process_args = Mock()
harness.run()
for arg in arg_list:
assert harness._runner_class.call_args[1][arg] is getattr(sentinel, arg)
def test_args_passed_to_driverclass(mock_runner):
built_kwargs = {'arg1': 'value1', 'arg2': 'value2'}
mock_runner._build_kwargs = Mock(return_value=built_kwargs)
with pytest.raises(IOError):
mock_runner.run_tests(['fake_tests.ini'])
assert mock_runner.driverclass.call_args[1] == built_kwargs
@pytest.fixture
def build_kwargs_using(mach_parsed_kwargs):
'''Helper function for test_build_kwargs_* functions'''
@ -307,6 +74,43 @@ def expected_driver_args(runner):
return expected
@pytest.fixture(params=['enabled', 'disabled', 'enabled_disabled', 'empty'])
def manifest_fixture(request):
'''
Fixture for the contents of mock_manifest, where a manifest
can include enabled tests, disabled tests, both, or neither (empty)
'''
included = []
if 'enabled' in request.param:
included += [(u'test_expected_pass.py', 'pass'),
(u'test_expected_fail.py', 'fail')]
if 'disabled' in request.param:
included += [(u'test_pass_disabled.py', 'pass', 'skip-if: true'),
(u'test_fail_disabled.py', 'fail', 'skip-if: true')]
keys = ('path', 'expected', 'disabled')
active_tests = [dict(zip(keys, values)) for values in included]
class ManifestFixture:
def __init__(self, name, tests):
self.filepath = "/path/to/fake/manifest.ini"
self.n_disabled = len([t for t in tests if 'disabled' in t])
self.n_enabled = len(tests) - self.n_disabled
mock_manifest = Mock(spec=manifestparser.TestManifest,
active_tests=Mock(return_value=tests))
self.mock_manifest = Mock(return_value=mock_manifest)
self.__repr__ = lambda: "<ManifestFixture {}>".format(name)
return ManifestFixture(request.param, active_tests)
def test_args_passed_to_driverclass(mock_runner):
built_kwargs = {'arg1': 'value1', 'arg2': 'value2'}
mock_runner._build_kwargs = Mock(return_value=built_kwargs)
with pytest.raises(IOError):
mock_runner.run_tests(['fake_tests.ini'])
assert mock_runner.driverclass.call_args[1] == built_kwargs
def test_build_kwargs_basic_args(build_kwargs_using):
'''Test the functionality of runner._build_kwargs:
make sure that basic arguments (those which should
@ -386,18 +190,6 @@ def test_build_kwargs_with_emulator_or_address(expected_driver_args, build_kwarg
expected_driver_args.assert_keys_not_in(built_kwargs)
def test_harness_sets_up_default_test_handlers(mach_parsed_kwargs):
"""
If the necessary TestCase is not in test_handlers,
tests are omitted silently
"""
harness = MarionetteHarness(args=mach_parsed_kwargs)
mach_parsed_kwargs.pop('tests')
runner = harness._runner_class(**mach_parsed_kwargs)
assert marionette_test.MarionetteTestCase in runner.test_handlers
assert marionette_test.MarionetteJSTestCase in runner.test_handlers
def test_parsing_testvars(mach_parsed_kwargs):
mach_parsed_kwargs.pop('tests')
testvars_json_loads = [
@ -435,28 +227,12 @@ def test_load_testvars_throws_expected_errors(mach_parsed_kwargs):
assert 'not properly formatted' in json_exc.value.message
@pytest.mark.parametrize("has_crashed", [True, False])
def test_crash_is_recorded_as_error(empty_marionette_test,
logger,
has_crashed):
""" Number of errors is incremented by stopTest iff has_crashed is true """
# collect results from the empty test
result = MarionetteTestResult(
marionette=empty_marionette_test._marionette_weakref(),
logger=logger, verbosity=None,
stream=None, descriptions=None,
)
result.startTest(empty_marionette_test)
assert len(result.errors) == 0
assert len(result.failures) == 0
assert result.testsRun == 1
assert result.shouldStop is False
result.stopTest(empty_marionette_test)
assert result.shouldStop == has_crashed
def _check_crash_counts(has_crashed, runner, mock_marionette):
if has_crashed:
assert len(result.errors) == 1
assert mock_marionette.check_for_crash.call_count == 1
assert runner.crashed == 1
else:
assert len(result.errors) == 0
assert runner.crashed == 0
@pytest.mark.parametrize("has_crashed", [True, False])
@ -511,36 +287,6 @@ def test_add_test_directory(runner):
assert len(runner.tests) == 4
@pytest.fixture(params=['enabled', 'disabled', 'enabled_disabled', 'empty'])
def manifest_fixture(request):
'''
Fixture for the contents of mock_manifest, where a manifest
can include enabled tests, disabled tests, both, or neither (empty)
'''
included = []
if 'enabled' in request.param:
included += [(u'test_expected_pass.py', 'pass'),
(u'test_expected_fail.py', 'fail')]
if 'disabled' in request.param:
included += [(u'test_pass_disabled.py', 'pass', 'skip-if: true'),
(u'test_fail_disabled.py', 'fail', 'skip-if: true')]
keys = ('path', 'expected', 'disabled')
active_tests = [dict(zip(keys, values)) for values in included]
class ManifestFixture:
def __init__(self, name, tests):
self.filepath = "/path/to/fake/manifest.ini"
self.n_disabled = len([t for t in tests if 'disabled' in t])
self.n_enabled = len(tests) - self.n_disabled
mock_manifest = Mock(spec=TestManifest, active_tests=Mock(return_value=tests))
self.mock_manifest = Mock(return_value=mock_manifest)
self.__repr__ = lambda: "<ManifestFixture {}>".format(name)
return ManifestFixture(request.param, active_tests)
@pytest.mark.parametrize("test_files_exist", [True, False])
def test_add_test_manifest(mock_runner, manifest_fixture, monkeypatch, test_files_exist):
monkeypatch.setattr('marionette.runner.base.TestManifest', manifest_fixture.mock_manifest)
@ -625,4 +371,4 @@ def test_catch_invalid_test_names(runner):
if __name__ == '__main__':
import sys
sys.exit(pytest.main(['--verbose', __file__]))
sys.exit(pytest.main(['--verbose', __file__]))

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

@ -0,0 +1,52 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import pytest
from marionette.runner import MarionetteTestResult
@pytest.fixture
def empty_marionette_testcase():
""" Testable MarionetteTestCase class """
from marionette.marionette_test import MarionetteTestCase
class EmptyTestCase(MarionetteTestCase):
def test_nothing(self):
pass
return EmptyTestCase
@pytest.fixture
def empty_marionette_test(mock_marionette, empty_marionette_testcase):
return empty_marionette_testcase(lambda: mock_marionette, 'test_nothing')
@pytest.mark.parametrize("has_crashed", [True, False])
def test_crash_is_recorded_as_error(empty_marionette_test,
logger,
has_crashed):
""" Number of errors is incremented by stopTest iff has_crashed is true """
# collect results from the empty test
result = MarionetteTestResult(
marionette=empty_marionette_test._marionette_weakref(),
logger=logger, verbosity=None,
stream=None, descriptions=None,
)
result.startTest(empty_marionette_test)
assert len(result.errors) == 0
assert len(result.failures) == 0
assert result.testsRun == 1
assert result.shouldStop is False
result.stopTest(empty_marionette_test)
assert result.shouldStop == has_crashed
if has_crashed:
assert len(result.errors) == 1
else:
assert len(result.errors) == 0
if __name__ == '__main__':
import sys
sys.exit(pytest.main(['--verbose', __file__]))

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

@ -7,8 +7,5 @@ config = {
"pip_index": False,
"download_symbols": "ondemand",
"download_minidump_stackwalk": True,
"tooltool_cache": "/builds/tooltool_cache",
}

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

@ -22,6 +22,12 @@ from mozharness.mozilla.vcstools import VCSToolsScript
# General command line arguments for Firefox ui tests
firefox_ui_tests_config_options = [
[["--allow-software-gl-layers"], {
"action": "store_true",
"dest": "allow_software_gl_layers",
"default": False,
"help": "Permits a software GL implementation (such as LLVMPipe) to use the GL compositor.",
}],
[['--dry-run'], {
'dest': 'dry_run',
'default': False,
@ -33,15 +39,6 @@ firefox_ui_tests_config_options = [
'default': False,
'help': 'Enable multi-process (e10s) mode when running tests.',
}],
[['--firefox-ui-branch'], {
'dest': 'firefox_ui_branch',
'help': 'which branch to use for firefox_ui_tests',
}],
[['--firefox-ui-repo'], {
'dest': 'firefox_ui_repo',
'default': 'https://github.com/mozilla/firefox-ui-tests.git',
'help': 'which firefox_ui_tests repo to use',
}],
[['--symbols-path=SYMBOLS_PATH'], {
'dest': 'symbols_path',
'help': 'absolute path to directory containing breakpad '
@ -51,12 +48,6 @@ firefox_ui_tests_config_options = [
'dest': 'tag',
'help': 'Subset of tests to run (local, remote).',
}],
[["--allow-software-gl-layers"], {
"action": "store_true",
"dest": "allow_software_gl_layers",
"default": False,
"help": "Permits a software GL implementation (such as LLVMPipe) to use the GL compositor.",
}],
] + copy.deepcopy(testing_config_options)
# Command line arguments for update tests

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

@ -4182,7 +4182,7 @@ PERFORMANCE OF THIS SOFTWARE.
<h1><a id="node-properties"></a>node-properties License</h1>
<p>This license applies to
<span class="path">devtools/client/shared/vendor/node-properties.js</span>.</p>
<span class="path">devtools/shared/node-properties/node-properties.js</span>.</p>
<pre>
The MIT License (MIT)
@ -4878,7 +4878,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<h1><a id="sprintf.js"></a>sprintf.js License</h1>
<p>This license applies to
<span class="path">devtools/client/shared/vendor/sprintf.js</span>.</p>
<span class="path">devtools/shared/sprintfjs/sprintf.js</span>.</p>
<pre>
Copyright (c) 2007-2016, Alexandru Marasteanu &lt;hello [at) alexei (dot] ro&gt;

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

@ -9,6 +9,10 @@ tags = audiochannel
support-files =
file_multipleAudio.html
[browser_autoscroll_disabled.js]
[browser_block_autoplay_media.js]
tags = audiochannel
support-files =
file_multipleAudio.html
[browser_bug295977_autoscroll_overflow.js]
[browser_bug451286.js]
skip-if = !e10s

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

@ -0,0 +1,87 @@
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_multipleAudio.html";
var SuspendedType = {
NONE_SUSPENDED : 0,
SUSPENDED_PAUSE : 1,
SUSPENDED_BLOCK : 2,
SUSPENDED_PAUSE_DISPOSABLE : 3
};
function* wait_for_tab_playing_event(tab, expectPlaying) {
if (tab.soundPlaying == expectPlaying) {
ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
} else {
yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
if (event.detail.changed.indexOf("soundplaying") >= 0) {
is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
return true;
}
return false;
});
}
}
function check_audio_suspended(suspendedType) {
var autoPlay = content.document.getElementById('autoplay');
if (!autoPlay) {
ok(false, "Can't get the audio element!");
}
is(autoPlay.computedSuspended, suspendedType,
"The suspeded state of autoplay audio is correct.");
}
add_task(function* setup_test_preference() {
yield new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [
["media.useAudioChannelService.testing", true],
["media.block-autoplay-until-in-foreground", true]
]}, resolve);
});
});
add_task(function* block_autoplay_media() {
info("- open new background tab1 -");
let tab1 = window.gBrowser.addTab("about:blank");
tab1.linkedBrowser.loadURI(PAGE);
yield BrowserTestUtils.browserLoaded(tab1.linkedBrowser);
info("- should block autoplay media for non-visited tab1 -");
yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
check_audio_suspended);
info("- open new background tab2 -");
let tab2 = window.gBrowser.addTab("about:blank");
tab2.linkedBrowser.loadURI(PAGE);
yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
info("- should block autoplay for non-visited tab2 -");
yield ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
check_audio_suspended);
info("- select tab1 as foreground tab -");
yield BrowserTestUtils.switchTab(window.gBrowser, tab1);
info("- media should be unblocked because the tab was visited -");
yield wait_for_tab_playing_event(tab1, true);
yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.NONE_SUSPENDED,
check_audio_suspended);
info("- open another new foreground tab3 -");
let tab3 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
"about:blank");
info("- should still play media from tab1 -");
yield wait_for_tab_playing_event(tab1, true);
yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.NONE_SUSPENDED,
check_audio_suspended);
info("- should still block media from tab2 -");
yield wait_for_tab_playing_event(tab2, false);
yield ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
check_audio_suspended);
info("- remove tabs -");
yield BrowserTestUtils.removeTab(tab1);
yield BrowserTestUtils.removeTab(tab2);
yield BrowserTestUtils.removeTab(tab3);
});

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

@ -143,14 +143,7 @@ function play_nonautoplay_audio_should_be_blocked(suspendedType) {
}
nonAutoPlay.play();
return new Promise(resolve => {
nonAutoPlay.onplay = function () {
nonAutoPlay.onplay = null;
is(nonAutoPlay.computedSuspended, suspendedType,
"The suspeded state of non-autoplay audio is correct.");
resolve();
}
});
ok(nonAutoPlay.paused, "The blocked audio can't be playback.");
}
function* suspended_pause(url, browser) {

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

@ -383,9 +383,13 @@
}
setTimeout(selectText, 0);
// Clear the text because we don't want the text appearing underneath the input.
this.view.setCellText(row, column, "");
// Save the original text so we can restore it after stoping editing.
input.setAttribute("data-original-text", input.value);
this._editingRow = row;
this._editingColumn = column;
this.setAttribute("editing", "true");
return true;
]]>
@ -402,16 +406,15 @@
var input = this.inputField;
var editingRow = this._editingRow;
var editingColumn = this._editingColumn;
var value = accept ? input.value : input.getAttribute("data-original-text");
this._editingRow = -1;
this._editingColumn = null;
if (accept) {
var value = input.value;
this.view.setCellText(editingRow, editingColumn, value);
}
this.view.setCellText(editingRow, editingColumn, value);
input.hidden = true;
input.value = "";
this.removeAttribute("editing");
input.removeAttribute("data-original-text");
]]>
</body>
</method>

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

@ -606,44 +606,13 @@ XRE_InitChildProcess(int aArgc,
case GeckoProcessType_Content: {
process = new ContentProcess(parentPID);
// If passed in grab the application path for xpcom init
bool foundAppdir = false;
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
// If passed in grab the profile path for sandboxing
bool foundProfile = false;
#endif
nsCString appDir;
for (int idx = aArgc; idx > 0; idx--) {
if (aArgv[idx] && !strcmp(aArgv[idx], "-appdir")) {
MOZ_ASSERT(!foundAppdir);
if (foundAppdir) {
continue;
}
nsCString appDir;
appDir.Assign(nsDependentCString(aArgv[idx+1]));
static_cast<ContentProcess*>(process.get())->SetAppDir(appDir);
foundAppdir = true;
}
#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
if (aArgv[idx] && !strcmp(aArgv[idx], "-profile")) {
MOZ_ASSERT(!foundProfile);
if (foundProfile) {
continue;
}
nsCString profile;
profile.Assign(nsDependentCString(aArgv[idx+1]));
static_cast<ContentProcess*>(process.get())->SetProfile(profile);
foundProfile = true;
}
if (foundProfile && foundAppdir) {
break;
}
#else
if (foundAppdir) {
break;
}
#endif /* XP_MACOSX && MOZ_CONTENT_SANDBOX */
}
}
break;