Merge inbound to central, a=merge

This commit is contained in:
Wes Kocher 2015-08-19 17:03:29 -07:00
Родитель 8cf0c01db1 d8e7c636ae
Коммит c625073a37
88 изменённых файлов: 2050 добавлений и 1426 удалений

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

@ -29,12 +29,16 @@ class ProxyAccessibleWrap : public AccessibleWrap
}
};
class HyperTextProxyAccessibleWrap : public ProxyAccessibleWrap,
public ia2AccessibleEditableText,
public ia2AccessibleHypertext
class HyperTextProxyAccessibleWrap : public HyperTextAccessibleWrap
{
HyperTextProxyAccessibleWrap(ProxyAccessible* aProxy) :
ProxyAccessibleWrap(aProxy) {}
HyperTextAccessibleWrap(nullptr, nullptr)
{
mType = eProxyType;
mBits.proxy = aProxy;
}
virtual void Shutdown() override { mBits.proxy = nullptr; }
};
template<typename T>

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

@ -13,9 +13,16 @@
#include "HyperTextAccessibleWrap.h"
#include "HyperTextAccessible-inl.h"
#include "ProxyWrappers.h"
#include "mozilla/ClearOnShutdown.h"
using namespace mozilla::a11y;
StaticRefPtr<HyperTextAccessibleWrap> ia2AccessibleText::sLastTextChangeAcc;
StaticAutoPtr<nsString> ia2AccessibleText::sLastTextChangeString;
uint32_t ia2AccessibleText::sLastTextChangeStart = 0;
uint32_t ia2AccessibleText::sLastTextChangeEnd = 0;
bool ia2AccessibleText::sLastTextChangeWasInsert = false;
// IAccessibleText
STDMETHODIMP
@ -572,21 +579,22 @@ ia2AccessibleText::GetModifiedText(bool aGetInsertedText,
if (!aText)
return E_INVALIDARG;
uint32_t startOffset = 0, endOffset = 0;
nsAutoString text;
if (!sLastTextChangeAcc)
return S_OK;
nsresult rv = GetModifiedText(aGetInsertedText, text,
&startOffset, &endOffset);
if (NS_FAILED(rv))
return GetHRESULT(rv);
if (aGetInsertedText != sLastTextChangeWasInsert)
return S_OK;
aText->start = startOffset;
aText->end = endOffset;
if (sLastTextChangeAcc != this)
return S_OK;
if (text.IsEmpty())
aText->start = sLastTextChangeStart;
aText->end = sLastTextChangeEnd;
if (sLastTextChangeString->IsEmpty())
return S_FALSE;
aText->text = ::SysAllocStringLen(text.get(), text.Length());
aText->text = ::SysAllocStringLen(sLastTextChangeString->get(), sLastTextChangeString->Length());
return aText->text ? S_OK : E_OUTOFMEMORY;
}
@ -608,3 +616,24 @@ ia2AccessibleText::GetGeckoTextBoundary(enum IA2TextBoundaryType aBoundaryType)
}
}
void
ia2AccessibleText::InitTextChangeData()
{
ClearOnShutdown(&sLastTextChangeAcc);
ClearOnShutdown(&sLastTextChangeString);
}
void
ia2AccessibleText::UpdateTextChangeData(HyperTextAccessibleWrap* aAcc,
bool aInsert, const nsString& aStr,
int32_t aStart, uint32_t aLen)
{
if (!sLastTextChangeString)
sLastTextChangeString = new nsString();
sLastTextChangeAcc = aAcc;
sLastTextChangeStart = aStart;
sLastTextChangeEnd = aStart + aLen;
sLastTextChangeWasInsert = aInsert;
*sLastTextChangeString = aStr;
}

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

@ -14,6 +14,7 @@
namespace mozilla {
namespace a11y {
class HyperTextAccessibleWrap;
class ia2AccessibleText: public IAccessibleText
{
@ -113,10 +114,17 @@ public:
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_oldText(
/* [retval][out] */ IA2TextSegment *oldText);
static void InitTextChangeData();
static void UpdateTextChangeData(HyperTextAccessibleWrap* aAcc, bool aInsert,
const nsString& aStr, int32_t aStart,
uint32_t aLen);
protected:
virtual nsresult GetModifiedText(bool aGetInsertedText, nsAString& aText,
uint32_t *aStartOffset,
uint32_t *aEndOffset) = 0;
static StaticRefPtr<HyperTextAccessibleWrap> sLastTextChangeAcc;
static StaticAutoPtr<nsString> sLastTextChangeString;
static bool sLastTextChangeWasInsert;
static uint32_t sLastTextChangeStart;
static uint32_t sLastTextChangeEnd;
private:
HRESULT GetModifiedText(bool aGetInsertedText, IA2TextSegment *aNewText);

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

@ -15,12 +15,6 @@
using namespace mozilla;
using namespace mozilla::a11y;
StaticRefPtr<Accessible> HyperTextAccessibleWrap::sLastTextChangeAcc;
StaticAutoPtr<nsString> HyperTextAccessibleWrap::sLastTextChangeString;
uint32_t HyperTextAccessibleWrap::sLastTextChangeStart = 0;
uint32_t HyperTextAccessibleWrap::sLastTextChangeEnd = 0;
bool HyperTextAccessibleWrap::sLastTextChangeWasInsert = false;
NS_IMPL_ISUPPORTS_INHERITED0(HyperTextAccessibleWrap,
HyperTextAccessible)
@ -59,44 +53,15 @@ HyperTextAccessibleWrap::HandleAccEvent(AccEvent* aEvent)
eventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED) {
Accessible* accessible = aEvent->GetAccessible();
if (accessible && accessible->IsHyperText()) {
sLastTextChangeAcc = accessible;
if (!sLastTextChangeString)
sLastTextChangeString = new nsString();
AccTextChangeEvent* event = downcast_accEvent(aEvent);
event->GetModifiedText(*sLastTextChangeString);
sLastTextChangeStart = event->GetStartOffset();
sLastTextChangeEnd = sLastTextChangeStart + event->GetLength();
sLastTextChangeWasInsert = event->IsTextInserted();
HyperTextAccessibleWrap* text =
static_cast<HyperTextAccessibleWrap*>(accessible->AsHyperText());
ia2AccessibleText::UpdateTextChangeData(text, event->IsTextInserted(),
event->ModifiedText(),
event->GetStartOffset(),
event->GetLength());
}
}
return HyperTextAccessible::HandleAccEvent(aEvent);
}
nsresult
HyperTextAccessibleWrap::GetModifiedText(bool aGetInsertedText,
nsAString& aText,
uint32_t* aStartOffset,
uint32_t* aEndOffset)
{
aText.Truncate();
*aStartOffset = 0;
*aEndOffset = 0;
if (!sLastTextChangeAcc)
return NS_OK;
if (aGetInsertedText != sLastTextChangeWasInsert)
return NS_OK;
if (sLastTextChangeAcc != this)
return NS_OK;
*aStartOffset = sLastTextChangeStart;
*aEndOffset = sLastTextChangeEnd;
aText.Append(*sLastTextChangeString);
return NS_OK;
}

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

@ -38,18 +38,6 @@ public:
protected:
~HyperTextAccessibleWrap() {}
virtual nsresult GetModifiedText(bool aGetInsertedText, nsAString& aText,
uint32_t *aStartOffset,
uint32_t *aEndOffset);
static StaticRefPtr<Accessible> sLastTextChangeAcc;
static StaticAutoPtr<nsString> sLastTextChangeString;
static bool sLastTextChangeWasInsert;
static uint32_t sLastTextChangeStart;
static uint32_t sLastTextChangeEnd;
friend void PlatformInit();
};
} // namespace a11y

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

@ -9,12 +9,11 @@
#include "AccEvent.h"
#include "Compatibility.h"
#include "HyperTextAccessibleWrap.h"
#include "ia2AccessibleText.h"
#include "nsWinUtils.h"
#include "mozilla/a11y/ProxyAccessible.h"
#include "ProxyWrappers.h"
#include "mozilla/ClearOnShutdown.h"
using namespace mozilla;
using namespace mozilla::a11y;
@ -24,8 +23,7 @@ a11y::PlatformInit()
Compatibility::Init();
nsWinUtils::MaybeStartWindowEmulation();
ClearOnShutdown(&HyperTextAccessibleWrap::sLastTextChangeAcc);
ClearOnShutdown(&HyperTextAccessibleWrap::sLastTextChangeString);
ia2AccessibleText::InitTextChangeData();
}
void
@ -74,7 +72,12 @@ a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
}
void
a11y::ProxyTextChangeEvent(ProxyAccessible*, const nsString&, int32_t, uint32_t,
bool, bool)
a11y::ProxyTextChangeEvent(ProxyAccessible* aText, const nsString& aStr,
int32_t aStart, uint32_t aLen, bool aInsert, bool)
{
AccessibleWrap* wrapper = WrapperFor(aText);
auto text = static_cast<HyperTextAccessibleWrap*>(wrapper->AsHyperText());
if (text) {
ia2AccessibleText::UpdateTextChangeData(text, aInsert, aStr, aStart, aLen);
}
}

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

@ -2567,6 +2567,9 @@
remoteBrowser._outerWindowIDBrowserMap.delete(aOtherBrowser.outerWindowID);
}
aOtherBrowser.docShellIsActive = (ourBrowser == this.selectedBrowser &&
window.windowState != window.STATE_MINIMIZED);
// Swap the docshells
ourBrowser.swapDocShells(aOtherBrowser);

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

@ -1,6 +1,7 @@
[DEFAULT]
head = head.js
tail =
tags = addons
firefox-appdir = browser
skip-if = toolkit == 'android' || toolkit == 'gonk'
support-files =

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

@ -13,6 +13,7 @@ support-files = data/**
[test_bug415367.js]
[test_bug519468.js]
[test_bug564667.js]
tags = addons
[test_bug848297.js]
[test_crlf.js]
[test_data_protocol_registration.js]

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

@ -51,9 +51,9 @@ Animation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
void
Animation::SetEffect(KeyframeEffectReadOnly* aEffect)
{
// FIXME: We should perform an early return if aEffect == mEffect but
// current nsAnimationManager::CheckAnimationRule is relying on this
// method updating timing even in that case.
if (mEffect == aEffect) {
return;
}
if (mEffect) {
mEffect->SetParentTime(Nullable<TimeDuration>());
}
@ -650,6 +650,16 @@ Animation::ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule,
}
}
void
Animation::NotifyEffectTimingUpdated()
{
MOZ_ASSERT(mEffect,
"We should only update timing effect when we have a target "
"effect");
UpdateTiming(Animation::SeekFlag::NoSeek,
Animation::SyncNotifyFlag::Async);
}
// http://w3c.github.io/web-animations/#play-an-animation
void
Animation::DoPlay(ErrorResult& aRv, LimitBehavior aLimitBehavior)

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

@ -297,6 +297,9 @@ public:
void ComposeStyle(nsRefPtr<AnimValuesStyleRule>& aStyleRule,
nsCSSPropertySet& aSetProperties,
bool& aNeedsRefreshes);
void NotifyEffectTimingUpdated();
protected:
void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime);
void SilentlySetPlaybackRate(double aPlaybackRate);

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

@ -87,6 +87,17 @@ KeyframeEffectReadOnly::SetParentTime(Nullable<TimeDuration> aParentTime)
mParentTime = aParentTime;
}
void
KeyframeEffectReadOnly::SetTiming(const AnimationTiming& aTiming,
Animation& aOwningAnimation)
{
if (mTiming == aTiming) {
return;
}
mTiming = aTiming;
aOwningAnimation.NotifyEffectTimingUpdated();
}
ComputedTiming
KeyframeEffectReadOnly::GetComputedTimingAt(
const Nullable<TimeDuration>& aLocalTime,

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

@ -242,6 +242,10 @@ public:
return mTiming;
}
// FIXME: Drop |aOwningAnimation| once we make AnimationEffects track their
// owning animation.
void SetTiming(const AnimationTiming& aTiming, Animation& aOwningAnimtion);
// Return the duration from the start the active interval to the point where
// the animation begins playback. This is zero unless the animation has
// a negative delay in which case it is the absolute value of the delay.

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

@ -1900,7 +1900,7 @@ GK_ATOM(ondevicelight, "ondevicelight")
// Audio channel events
GK_ATOM(onmozinterruptbegin, "onmozinterruptbegin")
GK_ATOM(onmozinterruptend, "onmozinterruptbegin")
GK_ATOM(onmozinterruptend, "onmozinterruptend")
//---------------------------------------------------------------------------
// Special atoms

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

@ -438,6 +438,8 @@ TabParent::Destroy()
return;
}
IMEStateManager::OnTabParentDestroying(this);
RemoveWindowListeners();
// If this fails, it's most likely due to a content-process crash,
@ -484,6 +486,8 @@ TabParent::Recv__delete__()
void
TabParent::ActorDestroy(ActorDestroyReason why)
{
// Even though TabParent::Destroy calls this, we need to do it here too in
// case of a crash.
IMEStateManager::OnTabParentDestroying(this);
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);

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

@ -14,7 +14,7 @@
#include "MediaDecoderStateMachine.h"
#include "MediaTimer.h"
#include "AudioSink.h"
#include "mediasink/DecodedAudioDataSink.h"
#include "nsTArray.h"
#include "MediaDecoder.h"
#include "MediaDecoderReader.h"
@ -1072,7 +1072,7 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
SetPlayStartTime(TimeStamp::Now());
MOZ_ASSERT(IsPlaying());
StartAudioThread();
StartAudioSink();
StartDecodedStream();
DispatchDecodeTasksIfNeeded();
@ -1461,7 +1461,7 @@ MediaDecoderStateMachine::Seek(SeekTarget aTarget)
return mPendingSeek.mPromise.Ensure(__func__);
}
void MediaDecoderStateMachine::StopAudioThread()
void MediaDecoderStateMachine::StopAudioSink()
{
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
@ -1754,7 +1754,7 @@ MediaDecoderStateMachine::RequestVideoData()
}
void
MediaDecoderStateMachine::StartAudioThread()
MediaDecoderStateMachine::StartAudioSink()
{
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
@ -1765,9 +1765,9 @@ MediaDecoderStateMachine::StartAudioThread()
if (HasAudio() && !mAudioSink) {
mAudioCompleted = false;
mAudioSink = new AudioSink(mAudioQueue,
GetMediaTime(), mInfo.mAudio,
mDecoder->GetAudioChannel());
mAudioSink = new DecodedAudioDataSink(mAudioQueue,
GetMediaTime(), mInfo.mAudio,
mDecoder->GetAudioChannel());
mAudioSinkPromise.Begin(
mAudioSink->Init()->Then(
@ -2369,12 +2369,12 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
// Play the remaining media. We want to run AdvanceFrame() at least
// once to ensure the current playback position is advanced to the
// end of the media, and so that we update the readyState.
MaybeStartPlayback();
if (VideoQueue().GetSize() > 1 ||
(HasAudio() && !mAudioCompleted) ||
(mAudioCaptured && !mDecodedStream->IsFinished()))
{
// Start playback if necessary to play the remaining media.
MaybeStartPlayback();
UpdateRenderedVideoFrames();
NS_ASSERTION(!IsPlaying() ||
mLogicallySeeking ||
@ -2409,7 +2409,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
// Stop audio sink after call to AudioEndTime() above, otherwise it will
// return an incorrect value due to a null mAudioSink.
StopAudioThread();
StopAudioSink();
StopDecodedStream();
}
@ -2439,7 +2439,7 @@ MediaDecoderStateMachine::Reset()
// Stop the audio thread. Otherwise, AudioSink might be accessing AudioQueue
// outside of the decoder monitor while we are clearing the queue and causes
// crash for no samples to be popped.
StopAudioThread();
StopAudioSink();
StopDecodedStream();
mVideoFrameEndTime = -1;
@ -3054,7 +3054,7 @@ MediaDecoderStateMachine::AudioEndTime() const
return mDecodedStream->AudioEndTime();
}
// Don't call this after mAudioSink becomes null since we can't distinguish
// "before StartAudioThread" and "after StopAudioThread" where mAudioSink
// "before StartAudioSink" and "after StopAudioSink" where mAudioSink
// is null in both cases.
MOZ_ASSERT(!mAudioCompleted);
return -1;
@ -3135,7 +3135,7 @@ void MediaDecoderStateMachine::DispatchAudioCaptured()
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
if (!self->mAudioCaptured) {
// Stop the audio sink if it's running.
self->StopAudioThread();
self->StopAudioSink();
self->mAudioCaptured = true;
// Start DecodedStream if we are already playing. Otherwise it will be
// handled in MaybeStartPlayback().
@ -3160,7 +3160,7 @@ void MediaDecoderStateMachine::DispatchAudioUncaptured()
// Start again the audio sink.
self->mAudioCaptured = false;
if (self->IsPlaying()) {
self->StartAudioThread();
self->StartAudioSink();
}
self->ScheduleStateMachine();
}

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

@ -99,8 +99,11 @@ hardware (via AudioStream).
namespace mozilla {
class AudioSegment;
namespace media {
class AudioSink;
}
class AudioSegment;
class TaskQueue;
extern PRLogModuleInfo* gMediaDecoderLog;
@ -510,13 +513,15 @@ protected:
// state machine thread.
void UpdateRenderedVideoFrames();
// Stops the audio thread. The decoder monitor must be held with exactly
// one lock count. Called on the state machine thread.
void StopAudioThread();
// Stops the audio sink and shut it down.
// The decoder monitor must be held with exactly one lock count.
// Called on the state machine thread.
void StopAudioSink();
// Starts the audio thread. The decoder monitor must be held with exactly
// one lock count. Called on the state machine thread.
void StartAudioThread();
// Create and start the audio sink.
// The decoder monitor must be held with exactly one lock count.
// Called on the state machine thread.
void StartAudioSink();
void StopDecodedStream();
@ -997,7 +1002,7 @@ private:
int64_t mFragmentEndTime;
// The audio sink resource. Used on state machine and audio threads.
RefPtr<AudioSink> mAudioSink;
RefPtr<media::AudioSink> mAudioSink;
// The reader, don't call its methods with the decoder monitor held.
// This is created in the state machine's constructor.

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

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#if !defined(AudioSink_h__)
#define AudioSink_h__
#include "mozilla/nsRefPtr.h"
#include "nsISupportsImpl.h"
namespace mozilla {
class MediaData;
template <class T> class MediaQueue;
namespace media {
/*
* Define basic APIs for derived class instance to operate or obtain
* information from it.
*/
class AudioSink {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioSink)
AudioSink(MediaQueue<MediaData>& aAudioQueue)
: mAudioQueue(aAudioQueue)
{}
// Return a promise which will be resolved when AudioSink finishes playing,
// or rejected if any error.
virtual nsRefPtr<GenericPromise> Init() = 0;
virtual int64_t GetEndTime() const = 0;
virtual int64_t GetPosition() = 0;
// Check whether we've pushed more frames to the audio
// hardware than it has played.
virtual bool HasUnplayedFrames() = 0;
// Shut down the AudioSink's resources.
virtual void Shutdown() = 0;
// Change audio playback setting.
virtual void SetVolume(double aVolume) = 0;
virtual void SetPlaybackRate(double aPlaybackRate) = 0;
virtual void SetPreservesPitch(bool aPreservesPitch) = 0;
// Change audio playback status pause/resume.
virtual void SetPlaying(bool aPlaying) = 0;
protected:
virtual ~AudioSink() {}
virtual MediaQueue<MediaData>& AudioQueue() const {
return mAudioQueue;
}
// To queue audio data (no matter it's plain or encoded or encrypted, depends
// on the subclass)
MediaQueue<MediaData>& mAudioQueue;
};
} // namespace media
} // namespace mozilla
#endif

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

@ -4,9 +4,9 @@
* 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/. */
#include "AudioSink.h"
#include "AudioStream.h"
#include "MediaQueue.h"
#include "DecodedAudioDataSink.h"
#include "VideoUtils.h"
#include "mozilla/CheckedInt.h"
@ -16,19 +16,23 @@ namespace mozilla {
extern PRLogModuleInfo* gMediaDecoderLog;
#define SINK_LOG(msg, ...) \
MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \
("DecodedAudioDataSink=%p " msg, this, ##__VA_ARGS__))
#define SINK_LOG_V(msg, ...) \
MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, \
("DecodedAudioDataSink=%p " msg, this, ##__VA_ARGS__))
namespace media {
// The amount of audio frames that is used to fuzz rounding errors.
static const int64_t AUDIO_FUZZ_FRAMES = 1;
AudioSink::AudioSink(MediaQueue<MediaData>& aAudioQueue,
int64_t aStartTime,
const AudioInfo& aInfo,
dom::AudioChannel aChannel)
: mAudioQueue(aAudioQueue)
, mMonitor("AudioSink::mMonitor")
DecodedAudioDataSink::DecodedAudioDataSink(MediaQueue<MediaData>& aAudioQueue,
int64_t aStartTime,
const AudioInfo& aInfo,
dom::AudioChannel aChannel)
: AudioSink(aAudioQueue)
, mMonitor("DecodedAudioDataSink::mMonitor")
, mState(AUDIOSINK_STATE_INIT)
, mAudioLoopScheduled(false)
, mStartTime(aStartTime)
@ -42,14 +46,14 @@ AudioSink::AudioSink(MediaQueue<MediaData>& aAudioQueue,
}
void
AudioSink::SetState(State aState)
DecodedAudioDataSink::SetState(State aState)
{
AssertOnAudioThread();
mPendingState = Some(aState);
}
void
AudioSink::DispatchTask(already_AddRefed<nsIRunnable>&& event)
DecodedAudioDataSink::DispatchTask(already_AddRefed<nsIRunnable>&& event)
{
DebugOnly<nsresult> rv = mThread->Dispatch(Move(event), NS_DISPATCH_NORMAL);
// There isn't much we can do if Dispatch() fails.
@ -58,7 +62,7 @@ AudioSink::DispatchTask(already_AddRefed<nsIRunnable>&& event)
}
void
AudioSink::OnAudioQueueEvent()
DecodedAudioDataSink::OnAudioQueueEvent()
{
AssertOnAudioThread();
if (!mAudioLoopScheduled) {
@ -67,17 +71,17 @@ AudioSink::OnAudioQueueEvent()
}
void
AudioSink::ConnectListener()
DecodedAudioDataSink::ConnectListener()
{
AssertOnAudioThread();
mPushListener = AudioQueue().PushEvent().Connect(
mThread, this, &AudioSink::OnAudioQueueEvent);
mThread, this, &DecodedAudioDataSink::OnAudioQueueEvent);
mFinishListener = AudioQueue().FinishEvent().Connect(
mThread, this, &AudioSink::OnAudioQueueEvent);
mThread, this, &DecodedAudioDataSink::OnAudioQueueEvent);
}
void
AudioSink::DisconnectListener()
DecodedAudioDataSink::DisconnectListener()
{
AssertOnAudioThread();
mPushListener.Disconnect();
@ -85,22 +89,22 @@ AudioSink::DisconnectListener()
}
void
AudioSink::ScheduleNextLoop()
DecodedAudioDataSink::ScheduleNextLoop()
{
AssertOnAudioThread();
if (mAudioLoopScheduled) {
return;
}
mAudioLoopScheduled = true;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &DecodedAudioDataSink::AudioLoop);
DispatchTask(r.forget());
}
void
AudioSink::ScheduleNextLoopCrossThread()
DecodedAudioDataSink::ScheduleNextLoopCrossThread()
{
AssertNotOnAudioThread();
nsRefPtr<AudioSink> self = this;
nsRefPtr<DecodedAudioDataSink> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () {
// Do nothing if there is already a pending task waiting for its turn.
if (!self->mAudioLoopScheduled) {
@ -111,7 +115,7 @@ AudioSink::ScheduleNextLoopCrossThread()
}
nsRefPtr<GenericPromise>
AudioSink::Init()
DecodedAudioDataSink::Init()
{
nsRefPtr<GenericPromise> p = mEndPromise.Ensure(__func__);
nsresult rv = NS_NewNamedThread("Media Audio",
@ -128,7 +132,7 @@ AudioSink::Init()
}
int64_t
AudioSink::GetPosition()
DecodedAudioDataSink::GetPosition()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
@ -143,7 +147,7 @@ AudioSink::GetPosition()
}
bool
AudioSink::HasUnplayedFrames()
DecodedAudioDataSink::HasUnplayedFrames()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
// Experimentation suggests that GetPositionInFrames() is zero-indexed,
@ -152,7 +156,7 @@ AudioSink::HasUnplayedFrames()
}
void
AudioSink::Shutdown()
DecodedAudioDataSink::Shutdown()
{
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
@ -160,7 +164,7 @@ AudioSink::Shutdown()
mAudioStream->Cancel();
}
}
nsRefPtr<AudioSink> self = this;
nsRefPtr<DecodedAudioDataSink> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
self->mStopAudioThread = true;
if (!self->mAudioLoopScheduled) {
@ -184,10 +188,10 @@ AudioSink::Shutdown()
}
void
AudioSink::SetVolume(double aVolume)
DecodedAudioDataSink::SetVolume(double aVolume)
{
AssertNotOnAudioThread();
nsRefPtr<AudioSink> self = this;
nsRefPtr<DecodedAudioDataSink> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
if (self->mState == AUDIOSINK_STATE_PLAYING) {
self->mAudioStream->SetVolume(aVolume);
@ -197,11 +201,11 @@ AudioSink::SetVolume(double aVolume)
}
void
AudioSink::SetPlaybackRate(double aPlaybackRate)
DecodedAudioDataSink::SetPlaybackRate(double aPlaybackRate)
{
AssertNotOnAudioThread();
MOZ_ASSERT(aPlaybackRate != 0, "Don't set the playbackRate to 0 on AudioStream");
nsRefPtr<AudioSink> self = this;
nsRefPtr<DecodedAudioDataSink> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
if (self->mState == AUDIOSINK_STATE_PLAYING) {
self->mAudioStream->SetPlaybackRate(aPlaybackRate);
@ -211,10 +215,10 @@ AudioSink::SetPlaybackRate(double aPlaybackRate)
}
void
AudioSink::SetPreservesPitch(bool aPreservesPitch)
DecodedAudioDataSink::SetPreservesPitch(bool aPreservesPitch)
{
AssertNotOnAudioThread();
nsRefPtr<AudioSink> self = this;
nsRefPtr<DecodedAudioDataSink> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
if (self->mState == AUDIOSINK_STATE_PLAYING) {
self->mAudioStream->SetPreservesPitch(aPreservesPitch);
@ -224,10 +228,10 @@ AudioSink::SetPreservesPitch(bool aPreservesPitch)
}
void
AudioSink::SetPlaying(bool aPlaying)
DecodedAudioDataSink::SetPlaying(bool aPlaying)
{
AssertNotOnAudioThread();
nsRefPtr<AudioSink> self = this;
nsRefPtr<DecodedAudioDataSink> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
if (self->mState != AUDIOSINK_STATE_PLAYING ||
self->mPlaying == aPlaying) {
@ -249,7 +253,7 @@ AudioSink::SetPlaying(bool aPlaying)
}
nsresult
AudioSink::InitializeAudioStream()
DecodedAudioDataSink::InitializeAudioStream()
{
// AudioStream initialization can block for extended periods in unusual
// circumstances, so we take care to drop the decoder monitor while
@ -269,7 +273,7 @@ AudioSink::InitializeAudioStream()
}
void
AudioSink::Drain()
DecodedAudioDataSink::Drain()
{
AssertOnAudioThread();
MOZ_ASSERT(mPlaying && !mAudioStream->IsPaused());
@ -280,7 +284,7 @@ AudioSink::Drain()
}
void
AudioSink::Cleanup()
DecodedAudioDataSink::Cleanup()
{
AssertOnAudioThread();
mEndPromise.Resolve(true, __func__);
@ -290,13 +294,13 @@ AudioSink::Cleanup()
}
bool
AudioSink::ExpectMoreAudioData()
DecodedAudioDataSink::ExpectMoreAudioData()
{
return AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished();
}
bool
AudioSink::WaitingForAudioToPlay()
DecodedAudioDataSink::WaitingForAudioToPlay()
{
AssertOnAudioThread();
// Return true if we're not playing, and we're not shutting down, or we're
@ -308,7 +312,7 @@ AudioSink::WaitingForAudioToPlay()
}
bool
AudioSink::IsPlaybackContinuing()
DecodedAudioDataSink::IsPlaybackContinuing()
{
AssertOnAudioThread();
// If we're shutting down, captured, or at EOS, break out and exit the audio
@ -321,7 +325,7 @@ AudioSink::IsPlaybackContinuing()
}
void
AudioSink::AudioLoop()
DecodedAudioDataSink::AudioLoop()
{
AssertOnAudioThread();
mAudioLoopScheduled = false;
@ -386,7 +390,7 @@ AudioSink::AudioLoop()
}
bool
AudioSink::PlayAudio()
DecodedAudioDataSink::PlayAudio()
{
// See if there's a gap in the audio. If there is, push silence into the
// audio hardware, so we can play across the gap.
@ -420,7 +424,7 @@ AudioSink::PlayAudio()
}
void
AudioSink::FinishAudioLoop()
DecodedAudioDataSink::FinishAudioLoop()
{
AssertOnAudioThread();
MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
@ -433,7 +437,7 @@ AudioSink::FinishAudioLoop()
}
uint32_t
AudioSink::PlaySilence(uint32_t aFrames)
DecodedAudioDataSink::PlaySilence(uint32_t aFrames)
{
// Maximum number of bytes we'll allocate and write at once to the audio
// hardware when the audio stream contains missing frames and we're
@ -452,7 +456,7 @@ AudioSink::PlaySilence(uint32_t aFrames)
}
uint32_t
AudioSink::PlayFromAudioQueue()
DecodedAudioDataSink::PlayFromAudioQueue()
{
AssertOnAudioThread();
NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
@ -475,7 +479,7 @@ AudioSink::PlayFromAudioQueue()
}
void
AudioSink::StartAudioStreamPlaybackIfNeeded()
DecodedAudioDataSink::StartAudioStreamPlaybackIfNeeded()
{
// This value has been chosen empirically.
const uint32_t MIN_WRITE_BEFORE_START_USECS = 200000;
@ -488,7 +492,7 @@ AudioSink::StartAudioStreamPlaybackIfNeeded()
}
void
AudioSink::WriteSilence(uint32_t aFrames)
DecodedAudioDataSink::WriteSilence(uint32_t aFrames)
{
uint32_t numSamples = aFrames * mInfo.mChannels;
nsAutoTArray<AudioDataValue, 1000> buf;
@ -500,7 +504,7 @@ AudioSink::WriteSilence(uint32_t aFrames)
}
int64_t
AudioSink::GetEndTime() const
DecodedAudioDataSink::GetEndTime() const
{
CheckedInt64 playedUsecs = FramesToUsecs(mWritten, mInfo.mRate) + mStartTime;
if (!playedUsecs.isValid()) {
@ -511,15 +515,16 @@ AudioSink::GetEndTime() const
}
void
AudioSink::AssertOnAudioThread()
DecodedAudioDataSink::AssertOnAudioThread()
{
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
}
void
AudioSink::AssertNotOnAudioThread()
DecodedAudioDataSink::AssertNotOnAudioThread()
{
MOZ_ASSERT(NS_GetCurrentThread() != mThread);
}
} // namespace media
} // namespace mozilla

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

@ -3,9 +3,10 @@
/* 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/. */
#if !defined(AudioSink_h__)
#define AudioSink_h__
#if !defined(DecodedAudioDataSink_h__)
#define DecodedAudioDataSink_h__
#include "AudioSink.h"
#include "MediaInfo.h"
#include "mozilla/nsRefPtr.h"
#include "nsISupportsImpl.h"
@ -17,41 +18,37 @@
#include "mozilla/ReentrantMonitor.h"
namespace mozilla {
namespace media {
class AudioData;
class AudioStream;
template <class T> class MediaQueue;
class AudioSink {
class DecodedAudioDataSink : public AudioSink {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioSink)
AudioSink(MediaQueue<MediaData>& aAudioQueue,
int64_t aStartTime,
const AudioInfo& aInfo,
dom::AudioChannel aChannel);
DecodedAudioDataSink(MediaQueue<MediaData>& aAudioQueue,
int64_t aStartTime,
const AudioInfo& aInfo,
dom::AudioChannel aChannel);
// Return a promise which will be resolved when AudioSink finishes playing,
// or rejected if any error.
nsRefPtr<GenericPromise> Init();
// Return a promise which will be resolved when DecodedAudioDataSink
// finishes playing, or rejected if any error.
nsRefPtr<GenericPromise> Init() override;
/*
* All public functions below are thread-safe.
*/
int64_t GetPosition();
int64_t GetEndTime() const;
int64_t GetPosition() override;
int64_t GetEndTime() const override;
// Check whether we've pushed more frames to the audio hardware than it has
// played.
bool HasUnplayedFrames();
bool HasUnplayedFrames() override;
// Shut down the AudioSink's resources.
void Shutdown();
// Shut down the DecodedAudioDataSink's resources.
void Shutdown() override;
void SetVolume(double aVolume);
void SetPlaybackRate(double aPlaybackRate);
void SetPreservesPitch(bool aPreservesPitch);
void SetPlaying(bool aPlaying);
void SetVolume(double aVolume) override;
void SetPlaybackRate(double aPlaybackRate) override;
void SetPreservesPitch(bool aPreservesPitch) override;
void SetPlaying(bool aPlaying) override;
private:
enum State {
@ -62,7 +59,7 @@ private:
AUDIOSINK_STATE_ERROR
};
~AudioSink() {}
virtual ~DecodedAudioDataSink() {}
void DispatchTask(already_AddRefed<nsIRunnable>&& event);
void SetState(State aState);
@ -119,10 +116,6 @@ private:
void StartAudioStreamPlaybackIfNeeded();
void WriteSilence(uint32_t aFrames);
MediaQueue<MediaData>& AudioQueue() const {
return mAudioQueue;
}
ReentrantMonitor& GetReentrantMonitor() const {
return mMonitor;
}
@ -134,7 +127,6 @@ private:
void AssertOnAudioThread();
void AssertNotOnAudioThread();
MediaQueue<MediaData>& mAudioQueue;
mutable ReentrantMonitor mMonitor;
// There members are accessed on the audio thread only.
@ -168,7 +160,7 @@ private:
const AudioInfo mInfo;
dom::AudioChannel mChannel;
const dom::AudioChannel mChannel;
bool mStopAudioThread;
@ -180,6 +172,7 @@ private:
MediaEventListener mFinishListener;
};
} // namespace media
} // namespace mozilla
#endif

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

@ -0,0 +1,13 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
UNIFIED_SOURCES += [
'DecodedAudioDataSink.cpp',
]
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True

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

@ -25,6 +25,7 @@ DIRS += [
'gmp-plugin',
'gmp-plugin-openh264',
'imagecapture',
'mediasink',
'mediasource',
'ogg',
'platforms',
@ -193,7 +194,6 @@ UNIFIED_SOURCES += [
'AudioChannelFormat.cpp',
'AudioCompactor.cpp',
'AudioSegment.cpp',
'AudioSink.cpp',
'AudioStream.cpp',
'AudioStreamTrack.cpp',
'AudioTrack.cpp',

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

@ -23,16 +23,18 @@ MessagePortChild::RecvStopSendingDataConfirmed()
bool
MessagePortChild::RecvEntangled(nsTArray<MessagePortMessage>&& aMessages)
{
MOZ_ASSERT(mPort);
mPort->Entangled(aMessages);
if (mPort) {
mPort->Entangled(aMessages);
}
return true;
}
bool
MessagePortChild::RecvReceiveData(nsTArray<MessagePortMessage>&& aMessages)
{
MOZ_ASSERT(mPort);
mPort->MessagesReceived(aMessages);
if (mPort) {
mPort->MessagesReceived(aMessages);
}
return true;
}

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

@ -2,6 +2,7 @@
skip-if = buildapp == 'mulet' || toolkit == 'android' || toolkit == 'gonk'
head = head_plugins.js
tail =
tags = addons
[test_allowed_types.js]
skip-if = appname == "thunderbird"

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

@ -76,6 +76,10 @@ this.RequestSyncService = {
_timers: {},
_pendingRequests: {},
// This array contains functions to be executed after the scheduling of the
// current task or immediately if there are not scheduling in progress.
_afterSchedulingTasks: [],
// Initialization of the RequestSyncService.
init: function() {
debug("init");
@ -94,16 +98,14 @@ this.RequestSyncService = {
// Any incoming message will be stored and processed when the async
// operation is completed.
let self = this;
this.dbTxn("readonly", function(aStore) {
aStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
self.addRegistration(cursor.value);
cursor.continue();
this.addRegistration(cursor.value, cursor.continue);
}
}
},
}.bind(this),
function() {
debug("initialization done");
},
@ -127,11 +129,10 @@ this.RequestSyncService = {
this.close();
// Removing all the registrations will delete the pending timers.
let self = this;
this.forEachRegistration(function(aObj) {
let key = self.principalToKey(aObj.principal);
self.removeRegistrationInternal(aObj.data.task, key);
});
let key = this.principalToKey(aObj.principal);
this.removeRegistrationInternal(aObj.data.task, key);
}.bind(this));
},
observe: function(aSubject, aTopic, aData) {
@ -139,15 +140,21 @@ this.RequestSyncService = {
switch (aTopic) {
case 'xpcom-shutdown':
this.shutdown();
this.executeAfterScheduling(function() {
this.shutdown();
}.bind(this));
break;
case 'clear-origin-data':
this.clearData(aData);
this.executeAfterScheduling(function() {
this.clearData(aData);
}.bind(this));
break;
case 'wifi-state-changed':
this.wifiStateChanged(aSubject == 'enabled');
this.executeAfterScheduling(function() {
this.wifiStateChanged(aSubject == 'enabled');
}.bind(this));
break;
default:
@ -208,7 +215,7 @@ this.RequestSyncService = {
},
// Add a task to the _registrations map and create the timer if it's needed.
addRegistration: function(aObj) {
addRegistration: function(aObj, aCb) {
debug('addRegistration');
let key = this.principalToKey(aObj.principal);
@ -216,8 +223,12 @@ this.RequestSyncService = {
this._registrations[key] = {};
}
this.scheduleTimer(aObj);
this._registrations[key][aObj.data.task] = aObj;
this.scheduleTimer(aObj, function() {
this._registrations[key][aObj.data.task] = aObj;
if (aCb) {
aCb();
}
}.bind(this));
},
// Remove a task from the _registrations map and delete the timer if it's
@ -289,31 +300,45 @@ this.RequestSyncService = {
switch (aMessage.name) {
case "RequestSync:Register":
this.register(aMessage.target, aMessage.data, principal);
this.executeAfterScheduling(function() {
this.register(aMessage.target, aMessage.data, principal);
}.bind(this));
break;
case "RequestSync:Unregister":
this.unregister(aMessage.target, aMessage.data, principal);
this.executeAfterScheduling(function() {
this.unregister(aMessage.target, aMessage.data, principal);
}.bind(this));
break;
case "RequestSync:Registrations":
this.registrations(aMessage.target, aMessage.data, principal);
this.executeAfterScheduling(function() {
this.registrations(aMessage.target, aMessage.data, principal);
}.bind(this));
break;
case "RequestSync:Registration":
this.registration(aMessage.target, aMessage.data, principal);
this.executeAfterScheduling(function() {
this.registration(aMessage.target, aMessage.data, principal);
}.bind(this));
break;
case "RequestSyncManager:Registrations":
this.managerRegistrations(aMessage.target, aMessage.data, principal);
this.executeAfterScheduling(function() {
this.managerRegistrations(aMessage.target, aMessage.data, principal);
}.bind(this));
break;
case "RequestSyncManager:SetPolicy":
this.managerSetPolicy(aMessage.target, aMessage.data, principal);
this.executeAfterScheduling(function() {
this.managerSetPolicy(aMessage.target, aMessage.data, principal);
}.bind(this));
break;
case "RequestSyncManager:RunTask":
this.managerRunTask(aMessage.target, aMessage.data, principal);
this.executeAfterScheduling(function() {
this.managerRunTask(aMessage.target, aMessage.data, principal);
}.bind(this));
break;
default:
@ -389,9 +414,10 @@ this.RequestSyncService = {
aStore.put(data, data.dbKey);
},
function() {
self.addRegistration(data);
aTarget.sendAsyncMessage("RequestSync:Register:Return",
{ requestID: aData.requestID });
self.addRegistration(data, function() {
aTarget.sendAsyncMessage("RequestSync:Register:Return",
{ requestID: aData.requestID });
});
},
function() {
aTarget.sendAsyncMessage("RequestSync:Register:Return",
@ -524,9 +550,10 @@ this.RequestSyncService = {
}
this.updateObjectInDB(toSave, function() {
self.scheduleTimer(toSave);
aTarget.sendAsyncMessage("RequestSyncManager:SetPolicy:Return",
{ requestID: aData.requestID });
self.scheduleTimer(toSave, function() {
aTarget.sendAsyncMessage("RequestSyncManager:SetPolicy:Return",
{ requestID: aData.requestID });
});
});
},
@ -600,26 +627,57 @@ this.RequestSyncService = {
},
// Creation of the timer for a particular task object.
scheduleTimer: function(aObj) {
scheduleTimer: function(aObj, aCb) {
debug("scheduleTimer");
aCb = aCb || function() {};
this.removeTimer(aObj);
// A registration can be already inactive if it was 1 shot.
if (!aObj.active) {
aCb();
return;
}
if (aObj.data.state == RSYNC_STATE_DISABLED) {
aCb();
return;
}
// WifiOnly check.
if (aObj.data.state == RSYNC_STATE_WIFIONLY && !this._wifi) {
aCb();
return;
}
this.createTimer(aObj);
if (this.scheduling) {
dump("ERROR!! RequestSyncService - ScheduleTimer called into ScheduleTimer.\n");
aCb();
return;
}
this.scheduling = true;
this.createTimer(aObj, function() {
this.scheduling = false;
while (this._afterSchedulingTasks.length) {
var cb = this._afterSchedulingTasks.shift();
cb();
}
aCb();
}.bind(this));
},
executeAfterScheduling: function(aCb) {
if (!this.scheduling) {
aCb();
return;
}
this._afterSchedulingTasks.push(aCb);
},
timeout: function(aObj) {
@ -728,17 +786,15 @@ this.RequestSyncService = {
{ requestID: pendingRequests[i].requestID });
}
let self = this;
this.updateObjectInDB(this._activeTask, function() {
// SchedulerTimer creates a timer and a nsITimer cannot be cloned. This
// is the reason why this operation has to be done after storing the task
// into IDB.
if (!self._activeTask.data.oneShot) {
self.scheduleTimer(self._activeTask);
if (!this._activeTask.data.oneShot) {
this.scheduleTimer(this._activeTask, function() {
this.processNextTask();
}.bind(this));
} else {
this.processNextTask();
}
self.processNextTask();
});
}.bind(this));
},
processNextTask: function() {
@ -836,36 +892,37 @@ this.RequestSyncService = {
wifiStateChanged: function(aEnabled) {
debug("onWifiStateChanged");
this._wifi = aEnabled;
if (!this._wifi) {
// Disable all the wifiOnly tasks.
let self = this;
this.forEachRegistration(function(aObj) {
if (aObj.data.state == RSYNC_STATE_WIFIONLY && self.hasTimer(aObj)) {
self.removeTimer(aObj);
if (aObj.data.state == RSYNC_STATE_WIFIONLY && this.hasTimer(aObj)) {
this.removeTimer(aObj);
// It can be that this task has been already schedulated.
self.removeTaskFromQueue(aObj);
this.removeTaskFromQueue(aObj);
}
});
}.bind(this));
return;
}
// Enable all the tasks.
let self = this;
this.forEachRegistration(function(aObj) {
if (aObj.active && !self.hasTimer(aObj)) {
if (aObj.active && !this.hasTimer(aObj)) {
if (!aObj.data.wifiOnly) {
dump("ERROR - Found a disabled task that is not wifiOnly.");
}
self.scheduleTimer(aObj);
this.scheduleTimer(aObj);
}
});
}.bind(this));
},
createTimer: function(aObj) {
createTimer: function(aObj, aCb) {
aCb = aCb || function() {};
let interval = aObj.data.minInterval;
if (aObj.data.overwrittenMinInterval > 0) {
interval = aObj.data.overwrittenMinInterval;
@ -875,7 +932,11 @@ this.RequestSyncService = {
{ date: new Date(Date.now() + interval * 1000),
ignoreTimezone: false },
() => this.timeout(aObj),
aTimerId => this._timers[aObj.dbKey] = aTimerId);
function(aTimerId) {
this._timers[aObj.dbKey] = aTimerId;
aCb();
}.bind(this),
() => aCb());
},
hasTimer: function(aObj) {

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

@ -17,11 +17,9 @@ skip-if = os == "android" || toolkit == "gonk"
[test_basic_app.html]
skip-if = os == "android" || buildapp == 'b2g'
[test_wakeUp.html]
#run-if = buildapp == 'b2g' && toolkit == 'gonk'
skip-if = true
run-if = buildapp == 'b2g' && toolkit == 'gonk'
[test_runNow.html]
#run-if = buildapp == 'b2g' && toolkit == 'gonk'
skip-if = true
run-if = buildapp == 'b2g' && toolkit == 'gonk'
[test_promise.html]
skip-if = os == "android" || toolkit == "gonk"
[test_bug1151082.html]

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

@ -63,6 +63,7 @@
#include "mozilla/dom/PromiseDebugging.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/StructuredClone.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
@ -2395,8 +2396,9 @@ InterfaceRequestor::GetAnyLiveTabChild()
nsCOMPtr<nsITabChild> tabChild =
do_QueryReferent(mTabChildList.LastElement());
// Does this tab child still exist? If so, return it. We are done.
if (tabChild) {
// Does this tab child still exist? If so, return it. We are done. If the
// PBrowser actor is no longer useful, don't bother returning this tab.
if (tabChild && !static_cast<TabChild*>(tabChild.get())->IsDestroyed()) {
return tabChild.forget();
}

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

@ -1958,6 +1958,12 @@ XMLHttpRequest::Open(const nsACString& aMethod, const nsAString& aUrl,
return;
}
// We have been released in one of the nested Open() calls.
if (!mProxy) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
--mProxy->mOpenCount;
mProxy->mIsSyncXHR = !aAsync;
}

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

@ -1793,6 +1793,7 @@ void CheckIfRenderTargetViewNeedsRecreating(ID3D11Device *device)
// match the clear
if (resultColor != 0xffffff00) {
gfxCriticalNote << "RenderTargetViewNeedsRecreating";
gANGLESupportsD3D11 = false;
}
keyedMutex->ReleaseSync(0);

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

@ -315,20 +315,18 @@ FatalError(const char* aProtocolName, const char* aMsg,
formattedMessage.AppendLiteral("]: \"");
formattedMessage.AppendASCII(aMsg);
if (aIsParent) {
formattedMessage.AppendLiteral("\". Killing child side as a result.");
#ifdef MOZ_CRASHREPORTER
// We're going to crash the parent process because at this time
// there's no other really nice way of getting a minidump out of
// this process if we're off the main thread.
formattedMessage.AppendLiteral("\". Intentionally crashing.");
NS_ERROR(formattedMessage.get());
if (aOtherPid != kInvalidProcessId && aOtherPid != base::GetCurrentProcId()) {
ScopedProcessHandle otherProcessHandle;
if (base::OpenProcessHandle(aOtherPid, &otherProcessHandle.rwget())) {
if (!base::KillProcess(otherProcessHandle,
base::PROCESS_END_KILLED_BY_USER, false)) {
NS_ERROR("May have failed to kill child!");
}
} else {
NS_ERROR("Failed to open child process when attempting kill.");
}
}
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCFatalErrorProtocol"),
nsDependentCString(aProtocolName));
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCFatalErrorMsg"),
nsDependentCString(aMsg));
#endif
MOZ_CRASH("IPC FatalError in the parent process!");
} else {
formattedMessage.AppendLiteral("\". abort()ing as a result.");
NS_RUNTIMEABORT(formattedMessage.get());

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

@ -41,6 +41,5 @@ CPOWTimer::~CPOWTimer()
return;
}
js::PerformanceData* performance = js::GetPerformanceData(runtime);
performance->totalCPOWTime += endInterval - startInterval_;
js::AddCPOWPerformanceDelta(runtime, endInterval - startInterval_);
}

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

@ -5331,6 +5331,7 @@ class AutoStopwatch;
// Container for performance data
// All values are monotonic.
// All values are updated after running to completion.
struct PerformanceData {
// Number of times we have spent at least 2^n consecutive
// milliseconds executing code in this group.
@ -5397,30 +5398,68 @@ struct PerformanceGroup {
// An id unique to this runtime.
const uint64_t uid;
// The number of cycles spent in this group during this iteration
// of the event loop. Note that cycles are not a reliable measure,
// especially over short intervals. See Runtime.cpp for a more
// complete discussion on the imprecision of cycle measurement.
uint64_t recentCycles;
// The number of times this group has been activated during this
// iteration of the event loop.
uint64_t recentTicks;
// The number of milliseconds spent doing CPOW during this
// iteration of the event loop.
uint64_t recentCPOW;
// The current iteration of the event loop.
uint64_t iteration() const {
return iteration_;
}
// `true` if an instance of `AutoStopwatch` is already monitoring
// the performance of this performance group for this iteration
// of the event loop, `false` otherwise.
bool hasStopwatch(uint64_t iteration) const {
return stopwatch_ != nullptr && iteration_ == iteration;
bool hasStopwatch(uint64_t it) const {
return stopwatch_ != nullptr && iteration_ == it;
}
// `true` if a specific instance of `AutoStopwatch` is already monitoring
// the performance of this performance group for this iteration
// of the event loop, `false` otherwise.
bool hasStopwatch(uint64_t it, const AutoStopwatch* stopwatch) const {
return stopwatch_ == stopwatch && iteration_ == it;
}
// Mark that an instance of `AutoStopwatch` is monitoring
// the performance of this group for a given iteration.
void acquireStopwatch(uint64_t iteration, const AutoStopwatch* stopwatch) {
iteration_ = iteration;
void acquireStopwatch(uint64_t it, const AutoStopwatch* stopwatch) {
if (iteration_ != it) {
// Any data that pretends to be recent is actually bound
// to an older iteration and therefore stale.
resetRecentData();
}
iteration_ = it;
stopwatch_ = stopwatch;
}
// Mark that no `AutoStopwatch` is monitoring the
// performance of this group for the iteration.
void releaseStopwatch(uint64_t iteration, const AutoStopwatch* stopwatch) {
if (iteration_ != iteration)
void releaseStopwatch(uint64_t it, const AutoStopwatch* stopwatch) {
if (iteration_ != it)
return;
MOZ_ASSERT(stopwatch == stopwatch_ || stopwatch_ == nullptr);
stopwatch_ = nullptr;
}
// Get rid of any data that pretends to be recent.
void resetRecentData() {
recentCycles = 0;
recentTicks = 0;
recentCPOW = 0;
}
// Refcounting. For use with mozilla::RefPtr.
void AddRef();
void Release();
@ -5448,10 +5487,9 @@ private:
// The hash key for this PerformanceGroup.
void* const key_;
// A reference counter.
// Refcounter.
uint64_t refCount_;
// `true` if this PerformanceGroup may be shared by several
// compartments, `false` if it is dedicated to a single
// compartment.
@ -5511,12 +5549,19 @@ struct PerformanceGroupHolder {
};
/**
* Reset any stopwatch currently measuring.
* Commit any Performance Monitoring data.
*
* This function is designed to be called when we process a new event.
* Until `FlushMonitoring` has been called, all PerformanceMonitoring data is invisible
* to the outside world and can cancelled with a call to `ResetMonitoring`.
*/
extern JS_PUBLIC_API(void)
ResetStopwatches(JSRuntime*);
FlushPerformanceMonitoring(JSRuntime*);
/**
* Cancel any measurement that hasn't been committed.
*/
extern JS_PUBLIC_API(void)
ResetPerformanceMonitoring(JSRuntime*);
/**
* Turn on/off stopwatch-based CPU monitoring.
@ -5541,11 +5586,17 @@ GetStopwatchIsMonitoringPerCompartment(JSRuntime*);
extern JS_PUBLIC_API(bool)
IsStopwatchActive(JSRuntime*);
// Extract the CPU rescheduling data.
extern JS_PUBLIC_API(void)
GetPerfMonitoringTestCpuRescheduling(JSRuntime*, uint64_t* stayed, uint64_t* moved);
/**
* Access the performance information stored in a compartment.
* Add a number of microseconds to the time spent waiting on CPOWs
* since process start.
*/
extern JS_PUBLIC_API(PerformanceData*)
GetPerformanceData(JSRuntime*);
extern JS_PUBLIC_API(void)
AddCPOWPerformanceDelta(JSRuntime*, uint64_t delta);
typedef bool
(PerformanceStatsWalker)(JSContext* cx,

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

@ -56,14 +56,10 @@
#include "vm/ScopeObject-inl.h"
#include "vm/Stack-inl.h"
#if defined(XP_MACOSX)
#include <mach/mach.h>
#elif defined(XP_UNIX)
#include <sys/resource.h>
#elif defined(XP_WIN)
#if defined(XP_WIN)
#include <processthreadsapi.h>
#include <windows.h>
#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
#endif // defined(XP_WIN)
using namespace js;
using namespace js::gc;
@ -395,25 +391,47 @@ class AutoStopwatch final
bool isMonitoringCPOW_;
// Timestamps captured while starting the stopwatch.
uint64_t userTimeStart_;
uint64_t systemTimeStart_;
uint64_t cyclesStart_;
uint64_t CPOWTimeStart_;
// The performance group shared by this compartment and possibly
// others, or `nullptr` if another AutoStopwatch is already in
// charge of monitoring that group.
mozilla::RefPtr<js::PerformanceGroup> sharedGroup_;
// The CPU on which we started the measure. Defined only
// if `isMonitoringJank_` is `true`.
#if defined(XP_WIN) && WINVER >= _WIN32_WINNT_VISTA
struct cpuid_t {
WORD group_;
BYTE number_;
cpuid_t(WORD group, BYTE number)
: group_(group),
number_(number)
{ }
cpuid_t()
: group_(0),
number_(0)
{ }
};
#elif defined(XP_LINUX)
typedef int cpuid_t;
#else
typedef struct {} cpuid_t;
#endif // defined(XP_WIN) || defined(XP_LINUX)
// The toplevel group, representing the entire process, or `nullptr`
// if another AutoStopwatch is already in charge of monitoring that group.
mozilla::RefPtr<js::PerformanceGroup> topGroup_;
cpuid_t cpuStart_;
// The performance group specific to this compartment, or
// `nullptr` if another AutoStopwatch is already in charge of
// monitoring that group.
mozilla::RefPtr<js::PerformanceGroup> ownGroup_;
// The performance group shared by this compartment and possibly
// others, or `nullptr` if another AutoStopwatch is already in
// charge of monitoring that group.
mozilla::RefPtr<js::PerformanceGroup> sharedGroup_;
public:
// The toplevel group, representing the entire process, or `nullptr`
// if another AutoStopwatch is already in charge of monitoring that group.
mozilla::RefPtr<js::PerformanceGroup> topGroup_;
// The performance group specific to this compartment, or
// `nullptr` if another AutoStopwatch is already in charge of
// monitoring that group.
mozilla::RefPtr<js::PerformanceGroup> ownGroup_;
public:
// If the stopwatch is active, constructing an instance of
// AutoStopwatch causes it to become the current owner of the
// stopwatch.
@ -424,8 +442,7 @@ class AutoStopwatch final
, iteration_(0)
, isMonitoringJank_(false)
, isMonitoringCPOW_(false)
, userTimeStart_(0)
, systemTimeStart_(0)
, cyclesStart_(0)
, CPOWTimeStart_(0)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
@ -435,7 +452,7 @@ class AutoStopwatch final
return;
JSRuntime* runtime = cx_->runtime();
iteration_ = runtime->stopwatch.iteration;
iteration_ = runtime->stopwatch.iteration();
sharedGroup_ = acquireGroup(compartment->performanceMonitoring.getSharedGroup(cx));
if (sharedGroup_)
@ -449,14 +466,15 @@ class AutoStopwatch final
return;
}
// Now that we are sure that JS code is being executed,
// initialize the stopwatch for this iteration, lazily.
runtime->stopwatch.start();
enter();
}
~AutoStopwatch()
{
if (!sharedGroup_ && !ownGroup_) {
// We are not in charge of monitoring anything.
// (isMonitoringForTop_ implies isMonitoringForGroup_,
// so we do not need to check it)
return;
}
@ -465,32 +483,32 @@ class AutoStopwatch final
return;
JSRuntime* runtime = cx_->runtime();
if (iteration_ != runtime->stopwatch.iteration) {
if (iteration_ != runtime->stopwatch.iteration()) {
// We have entered a nested event loop at some point.
// Any information we may have is obsolete.
return;
}
// Finish and commit measures
exit();
releaseGroup(sharedGroup_);
releaseGroup(topGroup_);
releaseGroup(ownGroup_);
// Finish and commit measures
exit();
}
private:
void enter() {
JSRuntime* runtime = cx_->runtime();
if (runtime->stopwatch.isMonitoringCPOW()) {
CPOWTimeStart_ = runtime->stopwatch.performance.getOwnGroup()->data.totalCPOWTime;
CPOWTimeStart_ = runtime->stopwatch.totalCPOWTime;
isMonitoringCPOW_ = true;
}
if (runtime->stopwatch.isMonitoringJank()) {
if (this->getTimes(runtime, &userTimeStart_, &systemTimeStart_)) {
isMonitoringJank_ = true;
}
cyclesStart_ = this->getCycles();
cpuStart_ = this->getCPU();
isMonitoringJank_ = true;
}
}
@ -498,29 +516,37 @@ class AutoStopwatch final
void exit() {
JSRuntime* runtime = cx_->runtime();
uint64_t userTimeDelta = 0;
uint64_t systemTimeDelta = 0;
uint64_t cyclesDelta = 0;
if (isMonitoringJank_ && runtime->stopwatch.isMonitoringJank()) {
// We were monitoring jank when we entered and we still are.
uint64_t userTimeEnd, systemTimeEnd;
if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd)) {
// We make no attempt to recover from this error. If
// we bail out here, we lose nothing of value, plus
// I'm nearly sure that this error cannot happen in
// practice.
return;
// If possible, discard results when we don't end on the
// same CPU as we started. Note that we can be
// rescheduled to another CPU beween `getCycles()` and
// `getCPU()`. We hope that this will happen rarely
// enough that the impact on our statistics will remain
// limited.
const cpuid_t cpuEnd = this->getCPU();
if (isSameCPU(cpuStart_, cpuEnd)) {
const uint64_t cyclesEnd = getCycles();
cyclesDelta = getDelta(cyclesEnd, cyclesStart_);
}
userTimeDelta = userTimeEnd - userTimeStart_;
systemTimeDelta = systemTimeEnd - systemTimeStart_;
#if (defined(XP_WIN) && WINVER >= _WIN32_WINNT_VISTA) || defined(XP_LINUX)
if (isSameCPU(cpuStart_, cpuEnd))
runtime->stopwatch.testCpuRescheduling.stayed += 1;
else
runtime->stopwatch.testCpuRescheduling.moved += 1;
#endif // defined(XP_WIN) || defined(XP_LINUX)
}
uint64_t CPOWTimeDelta = 0;
if (isMonitoringCPOW_ && runtime->stopwatch.isMonitoringCPOW()) {
// We were monitoring CPOW when we entered and we still are.
CPOWTimeDelta = runtime->stopwatch.performance.getOwnGroup()->data.totalCPOWTime - CPOWTimeStart_;
const uint64_t CPOWTimeEnd = runtime->stopwatch.totalCPOWTime;
CPOWTimeDelta = getDelta(CPOWTimeEnd, CPOWTimeStart_);
}
commitDeltasToGroups(userTimeDelta, systemTimeDelta, CPOWTimeDelta);
addToGroups(cyclesDelta, CPOWTimeDelta);
}
// Attempt to acquire a group
@ -547,121 +573,85 @@ class AutoStopwatch final
group->releaseStopwatch(iteration_, this);
}
void commitDeltasToGroups(uint64_t userTimeDelta, uint64_t systemTimeDelta,
uint64_t CPOWTimeDelta) const {
applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, sharedGroup_);
applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, topGroup_);
applyDeltas(userTimeDelta, systemTimeDelta, CPOWTimeDelta, ownGroup_);
// Add recent changes to all the groups owned by this stopwatch.
// Mark the groups as changed recently.
void addToGroups(uint64_t cyclesDelta, uint64_t CPOWTimeDelta) {
addToGroup(cyclesDelta, CPOWTimeDelta, sharedGroup_);
addToGroup(cyclesDelta, CPOWTimeDelta, topGroup_);
addToGroup(cyclesDelta, CPOWTimeDelta, ownGroup_);
}
void applyDeltas(uint64_t userTimeDelta, uint64_t systemTimeDelta,
uint64_t CPOWTimeDelta, PerformanceGroup* group) const {
// Add recent changes to a single group. Mark the group as changed recently.
void addToGroup(uint64_t cyclesDelta, uint64_t CPOWTimeDelta, PerformanceGroup* group) {
if (!group)
return;
group->data.ticks++;
MOZ_ASSERT(group->hasStopwatch(iteration_, this));
uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
group->data.totalUserTime += userTimeDelta;
group->data.totalSystemTime += systemTimeDelta;
group->data.totalCPOWTime += CPOWTimeDelta;
// Update an array containing the number of times we have missed
// at least 2^0 successive ms, 2^1 successive ms, ...
// 2^i successive ms.
// Duration of one frame, i.e. 16ms in museconds
size_t i = 0;
uint64_t duration = 1000;
for (i = 0, duration = 1000;
i < ArrayLength(group->data.durations) && duration < totalTimeDelta;
++i, duration *= 2)
{
group->data.durations[i]++;
if (group->recentTicks == 0) {
// First time we meet this group during the tick,
// mark it as needing updates.
JSRuntime* runtime = cx_->runtime();
runtime->stopwatch.addChangedGroup(group);
}
group->recentTicks++;
group->recentCycles += cyclesDelta;
group->recentCPOW += CPOWTimeDelta;
}
// Get the OS-reported time spent in userland/systemland, in
// microseconds. On most platforms, this data is per-thread,
// but on some platforms we need to fall back to per-process.
bool getTimes(JSRuntime* runtime, uint64_t* userTime, uint64_t* systemTime) const {
MOZ_ASSERT(userTime);
MOZ_ASSERT(systemTime);
// Perform a subtraction for a quantity that should be monotonic
// but is not guaranteed to be so.
//
// If `start <= end`, return `end - start`.
// Otherwise, return `0`.
uint64_t getDelta(const uint64_t end, const uint64_t start) const
{
if (start >= end)
return 0;
return end - start;
}
#if defined(XP_MACOSX)
// On MacOS X, to get we per-thread data, we need to
// reach into the kernel.
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
thread_basic_info_data_t info;
mach_port_t port = mach_thread_self();
kern_return_t err =
thread_info(/* [in] targeted thread*/ port,
/* [in] nature of information*/ THREAD_BASIC_INFO,
/* [out] thread information */ (thread_info_t)&info,
/* [inout] number of items */ &count);
// We do not need ability to communicate with the thread, so
// let's release the port.
mach_port_deallocate(mach_task_self(), port);
if (err != KERN_SUCCESS)
return false;
*userTime = info.user_time.microseconds + info.user_time.seconds * 1000000;
*systemTime = info.system_time.microseconds + info.system_time.seconds * 1000000;
#elif defined(XP_UNIX)
struct rusage rusage;
#if defined(RUSAGE_THREAD)
// Under Linux, we can obtain per-thread statistics
int err = getrusage(RUSAGE_THREAD, &rusage);
// Return the value of the Timestamp Counter, as provided by the CPU.
// 0 on platforms for which we do not have access to a Timestamp Counter.
uint64_t getCycles() const
{
#if defined(MOZ_HAVE_RDTSC)
return ReadTimestampCounter();
#else
// Under other Unices, we need to do with more noisy
// per-process statistics.
int err = getrusage(RUSAGE_SELF, &rusage);
#endif // defined(RUSAGE_THREAD)
if (err)
return false;
*userTime = rusage.ru_utime.tv_usec + rusage.ru_utime.tv_sec * 1000000;
*systemTime = rusage.ru_stime.tv_usec + rusage.ru_stime.tv_sec * 1000000;
#elif defined(XP_WIN)
// Under Windows, we can obtain per-thread statistics,
// although experience seems to suggest that they are
// not very good under Windows XP.
FILETIME creationFileTime; // Ignored
FILETIME exitFileTime; // Ignored
FILETIME kernelFileTime;
FILETIME userFileTime;
BOOL success = GetThreadTimes(GetCurrentThread(),
&creationFileTime, &exitFileTime,
&kernelFileTime, &userFileTime);
if (!success)
return false;
ULARGE_INTEGER kernelTimeInt;
ULARGE_INTEGER userTimeInt;
kernelTimeInt.LowPart = kernelFileTime.dwLowDateTime;
kernelTimeInt.HighPart = kernelFileTime.dwHighDateTime;
// Convert 100 ns to 1 us, make sure that the result is monotonic
*systemTime = runtime->stopwatch.systemTimeFix.monotonize(kernelTimeInt.QuadPart / 10);
userTimeInt.LowPart = userFileTime.dwLowDateTime;
userTimeInt.HighPart = userFileTime.dwHighDateTime;
// Convert 100 ns to 1 us, make sure that the result is monotonic
*userTime = runtime->stopwatch.userTimeFix.monotonize(userTimeInt.QuadPart / 10);
#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
return true;
return 0;
#endif // defined(MOZ_HAVE_RDTSC)
}
private:
// Return the identifier of the current CPU, on platforms for which we have
// access to the current CPU.
cpuid_t inline getCPU() const
{
#if defined(XP_WIN) && WINVER >= _WIN32_WINNT_VISTA
PROCESSOR_NUMBER proc;
GetCurrentProcessorNumberEx(&proc);
cpuid_t result(proc.Group, proc.Number);
return result;
#elif defined(XP_LINUX)
return sched_getcpu();
#else
return {};
#endif // defined(XP_WIN) || defined(XP_LINUX)
}
// Compare two CPU identifiers.
bool inline isSameCPU(const cpuid_t& a, const cpuid_t& b) const
{
#if defined(XP_WIN) && WINVER >= _WIN32_WINNT_VISTA
return a.group_ == b.group_ && a.number_ == b.number_;
#elif defined(XP_LINUX)
return a == b;
#else
return true;
#endif
}
private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
};
@ -678,9 +668,9 @@ js::RunScript(JSContext* cx, RunState& state)
{
JS_CHECK_RECURSION(cx, return false);
#if defined(NIGHTLY_BUILD)
#if defined(NIGHTLY_BUILD) && defined(MOZ_HAVE_RDTSC)
js::AutoStopwatch stopwatch(cx);
#endif // defined(NIGHTLY_BUILD)
#endif // defined(NIGHTLY_BUILD) && defined(MOZ_HAVE_RDTSC)
SPSEntryMarker marker(cx->runtime(), state.script());

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

@ -12,6 +12,15 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/ThreadLocal.h"
#if defined(XP_MACOSX)
#include <mach/mach.h>
#elif defined(XP_UNIX)
#include <sys/resource.h>
#elif defined(XP_WIN)
#include <processthreadsapi.h>
#include <windows.h>
#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
#include <locale.h>
#include <string.h>
@ -872,12 +881,299 @@ JS::IsProfilingEnabledForRuntime(JSRuntime* runtime)
return runtime->spsProfiler.enabled();
}
void
js::ResetStopwatches(JSRuntime* rt)
JS_PUBLIC_API(void)
js::FlushPerformanceMonitoring(JSRuntime* runtime)
{
MOZ_ASSERT(rt);
rt->stopwatch.reset();
MOZ_ASSERT(runtime);
return runtime->stopwatch.commit();
}
JS_PUBLIC_API(void)
js::ResetPerformanceMonitoring(JSRuntime* runtime)
{
MOZ_ASSERT(runtime);
return runtime->stopwatch.reset();
}
void
JSRuntime::Stopwatch::reset()
{
// All ongoing measures are dependent on the current iteration#.
// By incrementing it, we mark all data as stale. Stale data will
// be overwritten progressively during the execution.
++iteration_;
touchedGroups.clear();
}
void
JSRuntime::Stopwatch::start()
{
if (!isMonitoringJank_) {
return;
}
if (iteration_ == startedAtIteration_) {
// The stopwatch is already started for this iteration.
return;
}
startedAtIteration_ = iteration_;
if (!getResources(&userTimeStart_, &systemTimeStart_))
return;
}
// Commit the data that has been collected during the iteration
// into the actual `PerformanceData`.
//
// We use the proportion of cycles-spent-in-group over
// cycles-spent-in-toplevel-group as an approximation to allocate
// system (kernel) time and user (CPU) time to each group. Note
// that cycles are not an exact measure:
//
// 1. if the computer has gone to sleep, the clock may be reset to 0;
// 2. if the process is moved between CPUs/cores, it may end up on a CPU
// or core with an unsynchronized clock;
// 3. the mapping between clock cycles and walltime varies with the current
// frequency of the CPU;
// 4. other threads/processes using the same CPU will also increment
// the counter.
//
// ** Effect of 1. (computer going to sleep)
//
// We assume that this will happen very seldom. Since the final numbers
// are bounded by the CPU time and Kernel time reported by `getresources`,
// the effect will be contained to a single iteration of the event loop.
//
// ** Effect of 2. (moving between CPUs/cores)
//
// On platforms that support it, we only measure the number of cycles
// if we start and end execution of a group on the same
// CPU/core. While there is a small window (a few cycles) during which
// the thread can be migrated without us noticing, we expect that this
// will happen rarely enough that this won't affect the statistics
// meaningfully.
//
// On other platforms, assuming that the probability of jumping
// between CPUs/cores during a given (real) cycle is constant, and
// that the distribution of differences between clocks is even, the
// probability that the number of cycles reported by a measure is
// modified by X cycles should be a gaussian distribution, with groups
// with longer execution having a larger amplitude than groups with
// shorter execution. Since we discard measures that result in a
// negative number of cycles, this distribution is actually skewed
// towards over-estimating the number of cycles of groups that already
// have many cycles and under-estimating the number of cycles that
// already have fewer cycles.
//
// Since the final numbers are bounded by the CPU time and Kernel time
// reported by `getresources`, we accept this bias.
//
// ** Effect of 3. (mapping between clock cycles and walltime)
//
// Assuming that this is evenly distributed, we expect that this will
// eventually balance out.
//
// ** Effect of 4. (cycles increase with system activity)
//
// Assuming that, within an iteration of the event loop, this happens
// unformly over time, this will skew towards over-estimating the number
// of cycles of groups that already have many cycles and under-estimating
// the number of cycles that already have fewer cycles.
//
// Since the final numbers are bounded by the CPU time and Kernel time
// reported by `getresources`, we accept this bias.
//
// ** Big picture
//
// Computing the number of cycles is fast and should be accurate
// enough in practice. Alternatives (such as calling `getresources`
// all the time or sampling from another thread) are very expensive
// in system calls and/or battery and not necessarily more accurate.
void
JSRuntime::Stopwatch::commit()
{
#if !defined(MOZ_HAVE_RDTSC)
// The AutoStopwatch is only executed if `MOZ_HAVE_RDTSC`.
return;
#endif // !defined(MOZ_HAVE_RDTSC)
if (!isMonitoringJank_) {
// Either we have not started monitoring or monitoring has
// been cancelled during the iteration.
return;
}
if (startedAtIteration_ != iteration_) {
// No JS code has been monitored during this iteration.
return;
}
uint64_t userTimeStop, systemTimeStop;
if (!getResources(&userTimeStop, &systemTimeStop))
return;
// `getResources` is not guaranteed to be monotonic, so round up
// any negative result to 0 milliseconds.
uint64_t userTimeDelta = 0;
if (userTimeStop > userTimeStart_)
userTimeDelta = userTimeStop - userTimeStart_;
uint64_t systemTimeDelta = 0;
if (systemTimeStop > systemTimeStart_)
systemTimeDelta = systemTimeStop - systemTimeStart_;
mozilla::RefPtr<js::PerformanceGroup> group = performance.getOwnGroup();
const uint64_t totalRecentCycles = group->recentCycles;
mozilla::Vector<mozilla::RefPtr<js::PerformanceGroup>> recentGroups;
touchedGroups.swap(recentGroups);
MOZ_ASSERT(recentGroups.length() > 0);
// We should only reach this stage if `group` has had some activity.
MOZ_ASSERT(group->recentTicks > 0);
for (mozilla::RefPtr<js::PerformanceGroup>* iter = recentGroups.begin(); iter != recentGroups.end(); ++iter) {
transferDeltas(userTimeDelta, systemTimeDelta, totalRecentCycles, *iter);
}
// Make sure that `group` was treated along with the other items of `recentGroups`.
MOZ_ASSERT(group->recentTicks == 0);
// Finally, reset immediately, to make sure that we're not hit by the
// end of a nested event loop (which would cause `commit` to be called
// twice in succession).
reset();
}
void
JSRuntime::Stopwatch::transferDeltas(uint64_t totalUserTimeDelta, uint64_t totalSystemTimeDelta,
uint64_t totalCyclesDelta, js::PerformanceGroup* group) {
const uint64_t ticksDelta = group->recentTicks;
const uint64_t cpowTimeDelta = group->recentCPOW;
const uint64_t cyclesDelta = group->recentCycles;
group->resetRecentData();
// We have now performed all cleanup and may `return` at any time without fear of leaks.
if (group->iteration() != iteration_) {
// Stale data, don't commit.
return;
}
// When we add a group as changed, we immediately set its
// `recentTicks` from 0 to 1. If we have `ticksDelta == 0` at
// this stage, we have already called `resetRecentData` but we
// haven't removed it from the list.
MOZ_ASSERT(ticksDelta != 0);
MOZ_ASSERT(cyclesDelta <= totalCyclesDelta);
if (cyclesDelta == 0 || totalCyclesDelta == 0) {
// Nothing useful, don't commit.
return;
}
double proportion = (double)cyclesDelta / (double)totalCyclesDelta;
MOZ_ASSERT(proportion <= 1);
const uint64_t userTimeDelta = proportion * totalUserTimeDelta;
const uint64_t systemTimeDelta = proportion * totalSystemTimeDelta;
group->data.totalUserTime += userTimeDelta;
group->data.totalSystemTime += systemTimeDelta;
group->data.totalCPOWTime += cpowTimeDelta;
group->data.ticks += ticksDelta;
const uint64_t totalTimeDelta = userTimeDelta + systemTimeDelta;
size_t i = 0;
uint64_t duration = 1000; // 1ms in µs
for (i = 0, duration = 1000;
i < mozilla::ArrayLength(group->data.durations) && duration < totalTimeDelta;
++i, duration *= 2) {
group->data.durations[i]++;
}
}
// Get the OS-reported time spent in userland/systemland, in
// microseconds. On most platforms, this data is per-thread,
// but on some platforms we need to fall back to per-process.
// Data is not guaranteed to be monotonic.
bool
JSRuntime::Stopwatch::getResources(uint64_t* userTime,
uint64_t* systemTime) const {
MOZ_ASSERT(userTime);
MOZ_ASSERT(systemTime);
#if defined(XP_MACOSX)
// On MacOS X, to get we per-thread data, we need to
// reach into the kernel.
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
thread_basic_info_data_t info;
mach_port_t port = mach_thread_self();
kern_return_t err =
thread_info(/* [in] targeted thread*/ port,
/* [in] nature of information*/ THREAD_BASIC_INFO,
/* [out] thread information */ (thread_info_t)&info,
/* [inout] number of items */ &count);
// We do not need ability to communicate with the thread, so
// let's release the port.
mach_port_deallocate(mach_task_self(), port);
if (err != KERN_SUCCESS)
return false;
*userTime = info.user_time.microseconds + info.user_time.seconds * 1000000;
*systemTime = info.system_time.microseconds + info.system_time.seconds * 1000000;
#elif defined(XP_UNIX)
struct rusage rusage;
#if defined(RUSAGE_THREAD)
// Under Linux, we can obtain per-thread statistics
int err = getrusage(RUSAGE_THREAD, &rusage);
#else
// Under other Unices, we need to do with more noisy
// per-process statistics.
int err = getrusage(RUSAGE_SELF, &rusage);
#endif // defined(RUSAGE_THREAD)
if (err)
return false;
*userTime = rusage.ru_utime.tv_usec + rusage.ru_utime.tv_sec * 1000000;
*systemTime = rusage.ru_stime.tv_usec + rusage.ru_stime.tv_sec * 1000000;
#elif defined(XP_WIN)
// Under Windows, we can obtain per-thread statistics,
// although experience seems to suggest that they are
// not very good under Windows XP.
FILETIME creationFileTime; // Ignored
FILETIME exitFileTime; // Ignored
FILETIME kernelFileTime;
FILETIME userFileTime;
BOOL success = GetThreadTimes(GetCurrentThread(),
&creationFileTime, &exitFileTime,
&kernelFileTime, &userFileTime);
if (!success)
return false;
ULARGE_INTEGER kernelTimeInt;
kernelTimeInt.LowPart = kernelFileTime.dwLowDateTime;
kernelTimeInt.HighPart = kernelFileTime.dwHighDateTime;
// Convert 100 ns to 1 us.
*systemTime = kernelTimeInt.QuadPart / 10;
ULARGE_INTEGER userTimeInt;
userTimeInt.LowPart = userFileTime.dwLowDateTime;
userTimeInt.HighPart = userFileTime.dwHighDateTime;
// Convert 100 ns to 1 us.
*userTime = userTimeInt.QuadPart / 10;
#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
return true;
}
bool
js::SetStopwatchIsMonitoringJank(JSRuntime* rt, bool value)
@ -912,6 +1208,13 @@ js::GetStopwatchIsMonitoringPerCompartment(JSRuntime* rt)
return rt->stopwatch.isMonitoringPerCompartment();
}
void
js::GetPerfMonitoringTestCpuRescheduling(JSRuntime* rt, uint64_t* stayed, uint64_t* moved)
{
*stayed = rt->stopwatch.testCpuRescheduling.stayed;
*moved = rt->stopwatch.testCpuRescheduling.moved;
}
js::PerformanceGroupHolder::~PerformanceGroupHolder()
{
unlink();
@ -961,39 +1264,46 @@ js::PerformanceGroupHolder::getSharedGroup(JSContext* cx)
} else {
sharedGroup_ = runtime_->new_<PerformanceGroup>(cx, key);
if (!sharedGroup_)
return nullptr;
return nullptr;
runtime_->stopwatch.groups().add(ptr, key, sharedGroup_);
}
return sharedGroup_;
}
PerformanceData*
js::GetPerformanceData(JSRuntime* rt)
void
js::AddCPOWPerformanceDelta(JSRuntime* rt, uint64_t delta)
{
return &rt->stopwatch.performance.getOwnGroup()->data;
rt->stopwatch.totalCPOWTime += delta;
}
js::PerformanceGroup::PerformanceGroup(JSRuntime* rt)
: uid(rt->stopwatch.uniqueId()),
recentCycles(0),
recentTicks(0),
recentCPOW(0),
runtime_(rt),
stopwatch_(nullptr),
iteration_(0),
key_(nullptr),
refCount_(0),
isSharedGroup_(false)
{ }
{
}
js::PerformanceGroup::PerformanceGroup(JSContext* cx, void* key)
: uid(cx->runtime()->stopwatch.uniqueId()),
runtime_(cx->runtime()),
stopwatch_(nullptr),
iteration_(0),
key_(key),
refCount_(0),
isSharedGroup_(true)
{ }
js::PerformanceGroup::PerformanceGroup(JSContext* cx, void* key)
: uid(cx->runtime()->stopwatch.uniqueId()),
recentCycles(0),
recentTicks(0),
recentCPOW(0),
runtime_(cx->runtime()),
stopwatch_(nullptr),
iteration_(0),
key_(key),
refCount_(0),
isSharedGroup_(true)
{
}
void
js::PerformanceGroup::AddRef()

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

@ -15,6 +15,7 @@
#include "mozilla/Scoped.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Vector.h"
#include <setjmp.h>
@ -1518,17 +1519,6 @@ struct JSRuntime : public JS::shadow::Runtime,
*/
js::PerformanceGroupHolder performance;
/**
* The number of times we have entered the event loop.
* Used to reset counters whenever we enter the loop,
* which may be caused either by having completed the
* previous run of the event loop, or by entering a
* nested loop.
*
* Always incremented by 1, may safely overflow.
*/
uint64_t iteration;
/**
* Callback used to ask the embedding to determine in which
* Performance Group the current execution belongs. Typically, this is
@ -1541,31 +1531,56 @@ struct JSRuntime : public JS::shadow::Runtime,
*/
JSCurrentPerfGroupCallback currentPerfGroupCallback;
/**
* The number of the current iteration of the event loop.
*/
uint64_t iteration() {
return iteration_;
}
explicit Stopwatch(JSRuntime* runtime)
: performance(runtime)
, iteration(0)
, currentPerfGroupCallback(nullptr)
, totalCPOWTime(0)
, isMonitoringJank_(false)
, isMonitoringCPOW_(false)
, isMonitoringPerCompartment_(false)
, iteration_(0)
, startedAtIteration_(0)
, idCounter_(0)
{ }
/**
* Reset the stopwatch.
*
* This method is meant to be called whenever we start processing
* an event, to ensure that stop any ongoing measurement that would
* otherwise provide irrelevant results.
* This method is meant to be called whenever we start
* processing an event, to ensure that we stop any ongoing
* measurement that would otherwise provide irrelevant
* results.
*/
void reset() {
++iteration;
}
void reset();
/**
* Start the stopwatch.
*
* This method is meant to be called once we know that the
* current event contains JavaScript code to execute. Calling
* this several times during the same iteration is idempotent.
*/
void start();
/**
* Commit the performance data collected since the last call
* to `start()`, unless `reset()` has been called since then.
*/
void commit();
/**
* Activate/deactivate stopwatch measurement of jank.
*
* Noop if `value` is `true` and the stopwatch is already active,
* or if `value` is `false` and the stopwatch is already inactive.
* Noop if `value` is `true` and the stopwatch is already
* measuring jank, or if `value` is `false` and the stopwatch
* is not measuring jank.
*
* Otherwise, any pending measurements are dropped, but previous
* measurements remain stored.
@ -1588,6 +1603,18 @@ struct JSRuntime : public JS::shadow::Runtime,
return isMonitoringJank_;
}
/**
* Activate/deactivate stopwatch measurement per compartment.
*
* Noop if `value` is `true` and the stopwatch is already
* measuring per compartment, or if `value` is `false` and the
* stopwatch is not measuring per compartment.
*
* Otherwise, any pending measurements are dropped, but previous
* measurements remain stored.
*
* May return `false` if the underlying hashtable cannot be allocated.
*/
bool setIsMonitoringPerCompartment(bool value) {
if (isMonitoringPerCompartment_ != value)
reset();
@ -1606,8 +1633,25 @@ struct JSRuntime : public JS::shadow::Runtime,
/**
* Activate/deactivate stopwatch measurement of CPOW.
*
* Noop if `value` is `true` and the stopwatch is already
* measuring CPOW, or if `value` is `false` and the stopwatch
* is not measuring CPOW.
*
* Otherwise, any pending measurements are dropped, but previous
* measurements remain stored.
*
* May return `false` if the underlying hashtable cannot be allocated.
*/
bool setIsMonitoringCPOW(bool value) {
if (isMonitoringCPOW_ != value)
reset();
if (value && !groups_.initialized()) {
if (!groups_.init(128))
return false;
}
isMonitoringCPOW_ = value;
return true;
}
@ -1623,46 +1667,119 @@ struct JSRuntime : public JS::shadow::Runtime,
return idCounter_++;
}
// Some systems have non-monotonic clocks. While we cannot
// improve the precision, we can make sure that our measures
// are monotonic nevertheless. We do this by storing the
// result of the latest call to the clock and making sure
// that the next timestamp is greater or equal.
struct MonotonicTimeStamp {
MonotonicTimeStamp()
: latestGood_(0)
{}
inline uint64_t monotonize(uint64_t stamp)
{
if (stamp <= latestGood_)
return latestGood_;
latestGood_ = stamp;
return stamp;
}
private:
uint64_t latestGood_;
/**
* Mark a group as changed during the current iteration.
*
* Recent data from this group will be post-processed and
* committed at the end of the iteration.
*/
void addChangedGroup(js::PerformanceGroup* group) {
MOZ_ASSERT(group->recentTicks == 0);
touchedGroups.append(group);
}
// The total amount of time spent waiting on CPOWs since the
// start of the process, in microseconds.
uint64_t totalCPOWTime;
// Data extracted by the AutoStopwatch to determine how often
// we reschedule the process to a different CPU during the
// execution of JS.
//
// Warning: These values are incremented *only* on platforms
// that offer a syscall/libcall to check on which CPU a
// process is currently executed.
struct TestCpuRescheduling
{
// Incremented once we have finished executing code
// in a group, if the CPU on which we started
// execution is the same as the CPU on which
// we finished.
uint64_t stayed;
// Incremented once we have finished executing code
// in a group, if the CPU on which we started
// execution is different from the CPU on which
// we finished.
uint64_t moved;
TestCpuRescheduling()
: stayed(0),
moved(0)
{ }
};
MonotonicTimeStamp systemTimeFix;
MonotonicTimeStamp userTimeFix;
TestCpuRescheduling testCpuRescheduling;
private:
Stopwatch(const Stopwatch&) = delete;
Stopwatch& operator=(const Stopwatch&) = delete;
// Commit a piece of data to a single group.
// `totalUserTimeDelta`, `totalSystemTimeDelta`, `totalCyclesDelta`
// represent the outer measures, taken for the entire runtime.
void transferDeltas(uint64_t totalUserTimeDelta,
uint64_t totalSystemTimeDelta,
uint64_t totalCyclesDelta,
js::PerformanceGroup* destination);
// Query the OS for the time spent in CPU/kernel since process
// launch.
bool getResources(uint64_t* userTime, uint64_t* systemTime) const;
private:
Groups groups_;
friend struct js::PerformanceGroupHolder;
/**
* `true` if stopwatch monitoring is active, `false` otherwise.
* `true` if stopwatch monitoring is active for Jank, `false` otherwise.
*/
bool isMonitoringJank_;
/**
* `true` if stopwatch monitoring is active for CPOW, `false` otherwise.
*/
bool isMonitoringCPOW_;
/**
* `true` if the stopwatch should udpdate data per-compartment, in
* addition to data per-group.
*/
bool isMonitoringPerCompartment_;
/**
* The number of times we have entered the event loop.
* Used to reset counters whenever we enter the loop,
* which may be caused either by having completed the
* previous run of the event loop, or by entering a
* nested loop.
*
* Always incremented by 1, may safely overflow.
*/
uint64_t iteration_;
/**
* The iteration at which the stopwatch was last started.
*
* Used both to avoid starting the stopwatch several times
* during the same event loop and to avoid committing stale
* stopwatch results.
*/
uint64_t startedAtIteration_;
/**
* A counter used to generate unique identifiers for groups.
*/
uint64_t idCounter_;
/**
* The timestamps returned by `getResources()` during the call to
* `start()` in the current iteration of the event loop.
*/
uint64_t userTimeStart_;
uint64_t systemTimeStart_;
/**
* Performance groups used during the current event.
*
* They are cleared by `commit()` and `reset()`.
*/
mozilla::Vector<mozilla::RefPtr<js::PerformanceGroup>> touchedGroups;
};
Stopwatch stopwatch;
};

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

@ -35,7 +35,7 @@
#include "nsCycleCollectionNoteRootCallback.h"
#include "nsCycleCollector.h"
#include "nsScriptLoader.h"
#include "jsfriendapi.h"
#include "jsapi.h"
#include "jsprf.h"
#include "js/MemoryMetrics.h"
#include "mozilla/dom/GeneratedAtomList.h"
@ -3602,7 +3602,10 @@ XPCJSRuntime::BeforeProcessTask(bool aMightBlock)
// Start the slow script timer.
mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
mSlowScriptSecondHalf = false;
js::ResetStopwatches(Get()->Runtime());
// As we may be entering a nested event loop, we need to
// cancel any ongoing performance measurement.
js::ResetPerformanceMonitoring(Get()->Runtime());
// Push a null JSContext so that we don't see any script during
// event processing.
@ -3624,6 +3627,10 @@ XPCJSRuntime::AfterProcessTask(uint32_t aNewRecursionDepth)
CycleCollectedJSRuntime::AfterProcessTask(aNewRecursionDepth);
// Now that we are certain that the event is complete,
// we can flush any ongoing performance measurement.
js::FlushPerformanceMonitoring(Get()->Runtime());
PopNullJSContext();
}

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

@ -9,6 +9,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
@ -453,6 +454,15 @@ GetSuitableScale(float aMaxScale, float aMinScale,
// transform animation, unless that would make us rasterize something
// larger than the screen. But we never want to go smaller than the
// minimum scale over the animation.
if (FuzzyEqualsMultiplicative(displayVisibleRatio, aMaxScale, .01f)) {
// Using aMaxScale may make us rasterize something a fraction larger than
// the screen. However, if aMaxScale happens to be the final scale of a
// transform animation it is better to use aMaxScale so that for the
// fraction of a second before we delayerize the composited texture it has
// a better chance of being pixel aligned and composited without resampling
// (avoiding visually clunky delayerization).
return aMaxScale;
}
return std::max(std::min(aMaxScale, displayVisibleRatio), aMinScale);
}

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

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html class="reftest-print">
<head>
<meta charset="UTF-8">
<style type="text/css">
#menu { position: fixed; left: 0px; top: 0px; }
</style>
</head>
<body>
<svg id="canvas" width="2427" height="2295.5" version="1.1" xmlns="http://www.w3.org/2000/svg"></svg>
<div id="menu">
<input id="chooseSize" type="range">
</div>
</body>
</html>

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

@ -62,3 +62,5 @@ load 960277-2.html
load 997709-1.html
load 1102791.html
load 1140216.html
load 1182414.html

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

@ -265,15 +265,18 @@ nsMeterFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
bool
nsMeterFrame::ShouldUseNativeStyle() const
{
nsIFrame* barFrame = mBarDiv->GetPrimaryFrame();
// Use the native style if these conditions are satisfied:
// - both frames use the native appearance;
// - neither frame has author specified rules setting the border or the
// background.
return StyleDisplay()->mAppearance == NS_THEME_METERBAR &&
mBarDiv->GetPrimaryFrame()->StyleDisplay()->mAppearance == NS_THEME_METERBAR_CHUNK &&
!PresContext()->HasAuthorSpecifiedRules(this,
NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND) &&
!PresContext()->HasAuthorSpecifiedRules(mBarDiv->GetPrimaryFrame(),
barFrame &&
barFrame->StyleDisplay()->mAppearance == NS_THEME_METERBAR_CHUNK &&
!PresContext()->HasAuthorSpecifiedRules(barFrame,
NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND);
}

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

@ -271,15 +271,18 @@ nsProgressFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
bool
nsProgressFrame::ShouldUseNativeStyle() const
{
nsIFrame* barFrame = mBarDiv->GetPrimaryFrame();
// Use the native style if these conditions are satisfied:
// - both frames use the native appearance;
// - neither frame has author specified rules setting the border or the
// background.
return StyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR &&
mBarDiv->GetPrimaryFrame()->StyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR_CHUNK &&
!PresContext()->HasAuthorSpecifiedRules(this,
NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND) &&
!PresContext()->HasAuthorSpecifiedRules(mBarDiv->GetPrimaryFrame(),
barFrame &&
barFrame->StyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR_CHUNK &&
!PresContext()->HasAuthorSpecifiedRules(barFrame,
NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND);
}

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

@ -837,15 +837,22 @@ nsRangeFrame::GetType() const
bool
nsRangeFrame::ShouldUseNativeStyle() const
{
nsIFrame* trackFrame = mTrackDiv->GetPrimaryFrame();
nsIFrame* progressFrame = mProgressDiv->GetPrimaryFrame();
nsIFrame* thumbFrame = mThumbDiv->GetPrimaryFrame();
return (StyleDisplay()->mAppearance == NS_THEME_RANGE) &&
!PresContext()->HasAuthorSpecifiedRules(this,
(NS_AUTHOR_SPECIFIED_BORDER |
NS_AUTHOR_SPECIFIED_BACKGROUND)) &&
!PresContext()->HasAuthorSpecifiedRules(mTrackDiv->GetPrimaryFrame(),
trackFrame &&
!PresContext()->HasAuthorSpecifiedRules(trackFrame,
STYLES_DISABLING_NATIVE_THEMING) &&
!PresContext()->HasAuthorSpecifiedRules(mProgressDiv->GetPrimaryFrame(),
progressFrame &&
!PresContext()->HasAuthorSpecifiedRules(progressFrame,
STYLES_DISABLING_NATIVE_THEMING) &&
!PresContext()->HasAuthorSpecifiedRules(mThumbDiv->GetPrimaryFrame(),
thumbFrame &&
!PresContext()->HasAuthorSpecifiedRules(thumbFrame,
STYLES_DISABLING_NATIVE_THEMING);
}

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

@ -557,9 +557,9 @@ skip-if(B2G||Mulet) == 363858-1.html 363858-1-ref.html # Initial mulet triage: p
skip-if(B2G||Mulet) == 363858-2.html 363858-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) == 363858-3.html 363858-3-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) == 363858-4.html 363858-4-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
fuzzy-if(OSX>=1008,45,2) fuzzy-if(winWidget,37,1) == 363858-5a.html 363858-5-ref.html
fuzzy-if(OSX>=1008,45,2) fuzzy-if(winWidget,114,1) == 363858-5a.html 363858-5-ref.html
== 363858-5b.html 363858-5-ref.html
fuzzy-if(OSX>=1008,45,2) fuzzy-if(winWidget,37,1) == 363858-6a.html 363858-6-ref.html
fuzzy-if(OSX>=1008,45,2) fuzzy-if(winWidget,114,1) == 363858-6a.html 363858-6-ref.html
== 363858-6b.html 363858-6-ref.html
== 363874.html 363874-ref.html
== 363874-max-width.html 363874-max-width-ref.html

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

@ -16,7 +16,7 @@
== placeholder-3.html placeholder-overridden-ref.html
== placeholder-4.html placeholder-overridden-ref.html
== placeholder-5.html placeholder-visible-ref.html
fuzzy-if(winWidget,160,7) fuzzy-if(asyncPan&&!layersGPUAccelerated,146,299) == placeholder-6.html placeholder-overflow-ref.html
fuzzy-if(winWidget,160,10) fuzzy-if(asyncPan&&!layersGPUAccelerated,146,299) == placeholder-6.html placeholder-overflow-ref.html
skip-if(B2G||Mulet) == placeholder-6-textarea.html placeholder-overflow-textarea-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
# needs-focus == placeholder-7.html placeholder-focus-ref.html
# needs-focus == placeholder-8.html placeholder-focus-ref.html

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

@ -455,14 +455,8 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
animationChanged =
oldEffect->Timing() != newEffect->Timing() ||
oldEffect->Properties() != newEffect->Properties();
oldEffect->Timing() = newEffect->Timing();
oldEffect->SetTiming(newEffect->Timing(), *oldAnim);
oldEffect->Properties() = newEffect->Properties();
// FIXME: Currently assigning to KeyframeEffect::Timing() does not
// update the corresponding Animation (which may, for example, no
// longer be finished). Until we introduce proper setters for
// properties on effects, we need to manually cause the owning
// Animation to update its timing by setting the effect again.
oldAnim->SetEffect(oldEffect);
}
// Reset compositor state so animation will be re-synchronized.
@ -507,10 +501,6 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
newAnim = nullptr;
newAnimations.ReplaceElementAt(newIdx, oldAnim);
collection->mAnimations.RemoveElementAt(oldIdx);
// We've touched the old animation's timing properties, so this
// could update the old animation's relevance.
oldAnim->UpdateRelevance();
}
}
} else {

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

@ -549,34 +549,42 @@ operator!=(U* aLhs, const nsRefPtr<T>& aRhs)
return const_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
}
// Comparing an |nsRefPtr| to |nullptr|
namespace detail {
class nsRefPtrZero;
} // namespace detail
// Comparing an |nsRefPtr| to |0|
template <class T>
inline bool
operator==(const nsRefPtr<T>& aLhs, decltype(nullptr))
operator==(const nsRefPtr<T>& aLhs, ::detail::nsRefPtrZero* aRhs)
// specifically to allow |smartPtr == 0|
{
return aLhs.get() == nullptr;
return static_cast<const void*>(aLhs.get()) == reinterpret_cast<const void*>(aRhs);
}
template <class T>
inline bool
operator==(decltype(nullptr), const nsRefPtr<T>& aRhs)
operator==(::detail::nsRefPtrZero* aLhs, const nsRefPtr<T>& aRhs)
// specifically to allow |0 == smartPtr|
{
return nullptr == aRhs.get();
return reinterpret_cast<const void*>(aLhs) == static_cast<const void*>(aRhs.get());
}
template <class T>
inline bool
operator!=(const nsRefPtr<T>& aLhs, decltype(nullptr))
operator!=(const nsRefPtr<T>& aLhs, ::detail::nsRefPtrZero* aRhs)
// specifically to allow |smartPtr != 0|
{
return aLhs.get() != nullptr;
return static_cast<const void*>(aLhs.get()) != reinterpret_cast<const void*>(aRhs);
}
template <class T>
inline bool
operator!=(decltype(nullptr), const nsRefPtr<T>& aRhs)
operator!=(::detail::nsRefPtrZero* aLhs, const nsRefPtr<T>& aRhs)
// specifically to allow |0 != smartPtr|
{
return nullptr != aRhs.get();
return reinterpret_cast<const void*>(aLhs) != static_cast<const void*>(aRhs.get());
}
/*****************************************************************************/

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

@ -1 +1 @@
NSPR_4_10_9_BETA3
NSPR_4_10_9_RTM

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

@ -10,3 +10,4 @@
*/
#error "Do not include this header file."

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

@ -31,11 +31,11 @@ PR_BEGIN_EXTERN_C
** The format of the version string is
** "<major version>.<minor version>[.<patch level>] [<Beta>]"
*/
#define PR_VERSION "4.10.9 Beta"
#define PR_VERSION "4.10.9"
#define PR_VMAJOR 4
#define PR_VMINOR 10
#define PR_VPATCH 9
#define PR_BETA PR_TRUE
#define PR_BETA PR_FALSE
/*
** PRVersionCheck

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

@ -67,7 +67,7 @@ class TierStatus(object):
def __init__(self, resources):
"""Accepts a SystemResourceMonitor to record results against."""
self.tiers = OrderedDict()
self.active_tiers = set()
self.tier_status = OrderedDict()
self.resources = resources
def set_tiers(self, tiers):
@ -78,29 +78,23 @@ class TierStatus(object):
finish_time=None,
duration=None,
)
self.tier_status[tier] = None
def begin_tier(self, tier):
"""Record that execution of a tier has begun."""
self.tier_status[tier] = 'active'
t = self.tiers[tier]
# We should ideally use a monotonic clock here. Unfortunately, we won't
# have one until Python 3.
t['begin_time'] = time.time()
self.resources.begin_phase(tier)
self.active_tiers.add(tier)
def finish_tier(self, tier):
"""Record that execution of a tier has finished."""
self.tier_status[tier] = 'finished'
t = self.tiers[tier]
t['finish_time'] = time.time()
t['duration'] = self.resources.finish_phase(tier)
self.active_tiers.remove(tier)
def tier_status(self):
for tier, state in self.tiers.items():
active = tier in self.active_tiers
finished = state['finish_time'] is not None
yield tier, active, finished
def tiered_resource_usage(self):
"""Obtains an object containing resource usage for tiers.

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

@ -5,6 +5,7 @@
from __future__ import absolute_import, print_function, unicode_literals
import argparse
import itertools
import json
import logging
import operator
@ -125,21 +126,17 @@ class BuildProgressFooter(object):
# terminal is a blessings.Terminal.
self._t = terminal
self._fh = sys.stdout
self._monitor = monitor
def _clear_lines(self, n):
self._fh.write(self._t.move_x(0))
self._fh.write(self._t.clear_eos())
self.tiers = monitor.tiers.tier_status.viewitems()
def clear(self):
"""Removes the footer from the current terminal."""
self._clear_lines(1)
self._fh.write(self._t.move_x(0))
self._fh.write(self._t.clear_eos())
def draw(self):
"""Draws this footer in the terminal."""
tiers = self._monitor.tiers
if not tiers.tiers:
if not self.tiers:
return
# The drawn terminal looks something like:
@ -148,15 +145,15 @@ class BuildProgressFooter(object):
# This is a list of 2-tuples of (encoding function, input). None means
# no encoding. For a full reason on why we do things this way, read the
# big comment below.
parts = [('bold', 'TIER'), ':', ' ']
for tier, active, finished in tiers.tier_status():
if active:
parts.extend([('underline_yellow', tier), ' '])
elif finished:
parts.extend([('green', tier), ' '])
parts = [('bold', 'TIER:')]
append = parts.append
for tier, status in self.tiers:
if status is None:
append(tier)
elif status == 'finished':
append(('green', tier))
else:
parts.extend([tier, ' '])
append(('underline_yellow', tier))
# We don't want to write more characters than the current width of the
# terminal otherwise wrapping may result in weird behavior. We can't
@ -168,30 +165,25 @@ class BuildProgressFooter(object):
written = 0
write_pieces = []
for part in parts:
if isinstance(part, tuple):
func, arg = part
try:
func, part = part
encoded = getattr(self._t, func)(part)
except ValueError:
encoded = part
if written + len(arg) > max_width:
write_pieces.append(arg[0:max_width - written])
written += len(arg)
break
len_part = len(part)
len_spaces = len(write_pieces)
if written + len_part + len_spaces > max_width:
write_pieces.append(part[0:max_width - written - len_spaces])
written += len_part
break
encoded = getattr(self._t, func)(arg)
write_pieces.append(encoded)
written += len_part
write_pieces.append(encoded)
written += len(arg)
else:
if written + len(part) > max_width:
write_pieces.append(part[0:max_width - written])
written += len(part)
break
write_pieces.append(part)
written += len(part)
with self._t.location():
self._t.move(self._t.height-1,0)
self._fh.write(''.join(write_pieces))
self._fh.flush()
self._fh.write(' '.join(write_pieces))
class BuildOutputManager(LoggingMixin):

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

@ -54,6 +54,7 @@ run-sequentially = hardcoded ports
run-sequentially = hardcoded ports
[test_cert_blocklist.js]
skip-if = buildapp == "b2g"
tags = addons
[test_ocsp_stapling_expired.js]
run-sequentially = hardcoded ports
skip-if = (toolkit == 'gonk' && debug) # Bug 1029775
@ -78,6 +79,7 @@ run-sequentially = hardcoded ports
[test_signed_apps.js]
[test_signed_apps-marketplace.js]
[test_signed_dir.js]
tags = addons
[test_cert_eku-CA_EP.js]
[test_cert_eku-CA_EP_NS_OS_SA_TS.js]

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

@ -1 +1 @@
NSS_3_20_RC0
NSS_3_20_RTM

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

@ -10,4 +10,3 @@
*/
#error "Do not include this header file."

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

@ -8,6 +8,7 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_healthreporter.js]
[test_provider_addons.js]
skip-if = buildapp == 'mulet'
tags = addons
[test_provider_appinfo.js]
[test_provider_crashes.js]
skip-if = !crashreporter

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

@ -36,6 +36,7 @@ support-files =
# We have a number of other libraries that are pretty much standalone.
[test_addon_utils.js]
run-sequentially = Restarts server, can't change pref.
tags = addons
[test_httpd_sync_server.js]
[test_jpakeclient.js]
# Bug 618233: this test produces random failures on Windows 7.
@ -130,10 +131,14 @@ skip-if = os == "android"
# Finally, we test each engine.
[test_addons_engine.js]
run-sequentially = Hardcoded port in static files.
tags = addons
[test_addons_reconciler.js]
tags = addons
[test_addons_store.js]
run-sequentially = Hardcoded port in static files.
tags = addons
[test_addons_tracker.js]
tags = addons
[test_bookmark_batch_fail.js]
[test_bookmark_engine.js]
[test_bookmark_invalid.js]

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

@ -2,11 +2,22 @@
# 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 itertools
from marionette_driver import errors
from marionette import marionette_test
from marionette.marionette_test import MarionetteTestCase as TC
class TestHandleError(marionette_test.MarionetteTestCase):
class TestProtocol1Errors(TC):
def setUp(self):
TC.setUp(self)
self.op = self.marionette.protocol
self.marionette.protocol = 1
def tearDown(self):
self.marionette.protocol = self.op
TC.tearDown(self)
def test_malformed_packet(self):
for t in [{}, {"error": None}]:
with self.assertRaisesRegexp(errors.MarionetteException, "Malformed packet"):
@ -29,3 +40,51 @@ class TestHandleError(marionette_test.MarionetteTestCase):
def test_unknown_error_status(self):
with self.assertRaises(errors.MarionetteException):
self.marionette._handle_error({"error": {"status": "barbera"}})
class TestProtocol2Errors(TC):
def setUp(self):
TC.setUp(self)
self.op = self.marionette.protocol
self.marionette.protocol = 2
def tearDown(self):
self.marionette.protocol = self.op
TC.tearDown(self)
def test_malformed_packet(self):
req = ["error", "message", "stacktrace"]
ps = []
for p in [p for i in range(0, len(req) + 1) for p in itertools.permutations(req, i)]:
ps.append(dict((x, None) for x in p))
for p in filter(lambda p: len(p) < 3, ps):
self.assertRaises(KeyError, self.marionette._handle_error, p)
def test_known_error_code(self):
with self.assertRaises(errors.NoSuchElementException):
self.marionette._handle_error(
{"error": errors.NoSuchElementException.code[0],
"message": None,
"stacktrace": None})
def test_known_error_status(self):
with self.assertRaises(errors.NoSuchElementException):
self.marionette._handle_error(
{"error": errors.NoSuchElementException.status,
"message": None,
"stacktrace": None})
def test_unknown_error_code(self):
with self.assertRaises(errors.MarionetteException):
self.marionette._handle_error(
{"error": 123456,
"message": None,
"stacktrace": None})
def test_unknown_error_status(self):
with self.assertRaises(errors.MarionetteException):
self.marionette._handle_error(
{"error": "barbera",
"message": None,
"stacktrace": None})

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

@ -15,7 +15,7 @@ class TestTearDownContext(MarionetteTestCase):
MarionetteTestCase.tearDown(self)
def get_context(self):
return self.marionette._send_message('getContext', 'value')
return self.marionette._send_message("getContext", key="value")
def test_skipped_teardown_ok(self):
raise SkipTest("This should leave our teardown method in chrome context")

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

@ -14,13 +14,13 @@ class TestSetContext(MarionetteTestCase):
self.chrome = self.marionette.CONTEXT_CHROME
self.content = self.marionette.CONTEXT_CONTENT
test_url = self.marionette.absolute_url('empty.html')
test_url = self.marionette.absolute_url("empty.html")
self.marionette.navigate(test_url)
self.marionette.set_context(self.content)
self.assertEquals(self.get_context(), self.content)
def get_context(self):
return self.marionette._send_message('getContext', 'value')
return self.marionette._send_message("getContext", key="value")
def test_set_different_context_using_with_block(self):
with self.marionette.using_context(self.chrome):

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

@ -1,21 +1,19 @@
"""
Copyright 2011 Software Freedom Conservancy.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
# Copyright 2015 Mozilla Foundation
# Copyright 2011 Software Freedom Conservancy.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class ApplicationCache(object):
UNCACHED = 0
IDLE = 1
CHECKING = 2
@ -28,4 +26,4 @@ class ApplicationCache(object):
@property
def status(self):
return self.driver._send_message('getAppCacheStatus', 'value')
return self.driver._send_message("getAppCacheStatus", key="value")

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -10,11 +10,11 @@ import time
class MarionetteTransport(object):
""" The Marionette socket client. This speaks the same protocol
as the remote debugger inside Gecko, in which messages are
always preceded by the message length and a colon, e.g.,
"""The Marionette socket client. This speaks the same protocol
as the remote debugger inside Gecko, in which messages are always
preceded by the message length and a colon, e.g.:
20:{'command': 'test'}
20:{"command": "test"}
"""
max_packet_length = 4096
@ -25,26 +25,25 @@ class MarionetteTransport(object):
self.port = port
self.socket_timeout = socket_timeout
self.sock = None
self.traits = None
self.applicationType = None
self.actor = 'root'
self.protocol = 1
self.application_type = None
def _recv_n_bytes(self, n):
""" Convenience method for receiving exactly n bytes from
self.sock (assuming it's open and connected).
"""Convenience method for receiving exactly n bytes from self.sock
(assuming it's open and connected).
"""
data = ''
data = ""
while len(data) < n:
chunk = self.sock.recv(n - len(data))
if chunk == '':
if chunk == "":
break
data += chunk
return data
def receive(self):
""" Receive the next complete response from the server, and return
it as a dict. Each response from the server is prepended by
len(message) + ':'.
"""Receive the next complete response from the server, and
return it as a JSON structure. Each response from the server
is prepended by len(message) + ":".
"""
assert(self.sock)
now = time.time()
@ -69,8 +68,10 @@ class MarionetteTransport(object):
raise socket.timeout('connection timed out after %d s' % self.socket_timeout)
def connect(self):
""" Connect to the server and process the hello message we expect
to receive in response.
"""Connect to the server and process the hello message we expect
to receive in response.
Return a tuple of the protocol level and the application type.
"""
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(self.socket_timeout)
@ -82,27 +83,22 @@ class MarionetteTransport(object):
self.sock = None
raise
self.sock.settimeout(2.0)
hello = self.receive()
self.traits = hello.get('traits')
self.applicationType = hello.get('applicationType')
self.protocol = hello.get("marionetteProtocol", 1)
self.application_type = hello.get("applicationType")
# get the marionette actor id
response = self.send({'to': 'root', 'name': 'getMarionetteID'})
self.actor = response['id']
return (self.protocol, self.application_type)
def send(self, msg):
""" Send a message on the socket, prepending it with len(msg) + ':'.
"""
def send(self, data):
"""Send a message on the socket, prepending it with len(msg) + ":"."""
if not self.sock:
self.connect()
if 'to' not in msg:
msg['to'] = self.actor
data = json.dumps(msg)
data = '%s:%s' % (len(data), data)
data = "%s:%s" % (len(data), data)
for packet in [data[i:i + self.max_packet_length] for i in
range(0, len(data), self.max_packet_length)]:
try:
try:
self.sock.send(packet)
except IOError as e:
if e.errno == errno.EPIPE:
@ -110,12 +106,10 @@ class MarionetteTransport(object):
else:
raise e
response = self.receive()
return response
return self.receive()
def close(self):
""" Close the socket.
"""
"""Close the socket."""
if self.sock:
self.sock.close()
self.sock = None

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

@ -2,7 +2,6 @@
# 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 json
from unittest import UnittestFormatter
from xunit import XUnitFormatter
from html import HTMLFormatter
@ -10,5 +9,11 @@ from machformatter import MachFormatter
from tbplformatter import TbplFormatter
from errorsummary import ErrorSummaryFormatter
try:
import ujson as json
except ImportError:
import json
def JSONFormatter():
return lambda x: json.dumps(x) + "\n"

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

@ -109,7 +109,10 @@ config = {
},
"all_xpcshell_suites": {
"xpcshell": ["--manifest=tests/xpcshell/tests/all-test-dirs.list",
"%(abs_app_dir)s/" + XPCSHELL_NAME]
"%(abs_app_dir)s/" + XPCSHELL_NAME],
"xpcshell-addons": ["--manifest=tests/xpcshell/tests/all-test-dirs.list",
"--tag=addons",
"%(abs_app_dir)s/" + XPCSHELL_NAME]
},
"all_cppunittest_suites": {
"cppunittest": ['tests/cppunittest']

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

@ -90,7 +90,10 @@ config = {
},
"all_xpcshell_suites": {
"xpcshell": ["--manifest=tests/xpcshell/tests/all-test-dirs.list",
"%(abs_app_dir)s/" + XPCSHELL_NAME]
"%(abs_app_dir)s/" + XPCSHELL_NAME],
"xpcshell-addons": ["--manifest=tests/xpcshell/tests/all-test-dirs.list",
"--tag=addons",
"%(abs_app_dir)s/" + XPCSHELL_NAME]
},
"all_cppunittest_suites": {
"cppunittest": ['tests/cppunittest']

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

@ -105,7 +105,10 @@ config = {
},
"all_xpcshell_suites": {
"xpcshell": ["--manifest=tests/xpcshell/tests/all-test-dirs.list",
"%(abs_app_dir)s/" + XPCSHELL_NAME]
"%(abs_app_dir)s/" + XPCSHELL_NAME],
"xpcshell-addons": ["--manifest=tests/xpcshell/tests/all-test-dirs.list",
"--tag=addons",
"%(abs_app_dir)s/" + XPCSHELL_NAME]
},
"all_cppunittest_suites": {
"cppunittest": ['tests/cppunittest']

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

@ -1405,10 +1405,11 @@ or run without that action (ie: --no-{action})"
templates.extend(contents['l10n'])
else:
templates = contents['routes']
index = self.config.get('taskcluster_index', 'index.garbage.staging')
routes = []
for template in templates:
fmt = {
'index': 'index.garbage.staging.mshal-testing', # TODO
'index': index,
'project': self.buildbot_config['properties']['branch'],
'head_rev': self.query_revision(),
'build_product': self.config['stage_product'],
@ -1427,7 +1428,6 @@ or run without that action (ie: --no-{action})"
self.log_obj,
)
index = self.config.get('taskcluster_index', 'index.garbage.staging')
# TODO: Bug 1165980 - these should be in tree
routes.extend([
"%s.buildbot.branches.%s.%s" % (index, self.branch, self.stage_platform),

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

@ -1028,9 +1028,7 @@ class DesktopSingleLocale(LocalesMixin, ReleaseMixin, MockMixin, BuildbotMixin,
routes = []
for template in templates:
fmt = {
# TODO: Bug 1133074
#index = self.config.get('taskcluster_index', 'index.garbage.staging')
'index': 'index.garbage.staging.mshal-testing',
'index': self.config.get('taskcluster_index', 'index.garbage.staging'),
'project': branch,
'head_rev': revision,
'build_product': self.config['stage_product'],

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

@ -287,7 +287,7 @@ class Graph(object):
# Template parameters used when expanding the graph
parameters = dict(gaia_info().items() + {
'index': 'index.garbage.staging.mshal-testing', #TODO
'index': 'index',
'project': project,
'pushlog_id': params.get('pushlog_id', 0),
'docker_image': docker_image,

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

@ -2,27 +2,35 @@
type: testharness
prefs: [media.mediasource.enabled:true]
[Test invalid MIME format "video/webm"]
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1191833
expected: FAIL
[Test invalid MIME format "video/webm;"]
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1191833
expected: FAIL
[Test invalid MIME format "video/webm;codecs"]
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1191833
expected: FAIL
[Test invalid MIME format "video/webm;codecs="]
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1191833
expected: FAIL
[Test invalid MIME format "video/webm;codecs=""]
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1191833
expected: FAIL
[Test invalid MIME format "video/webm;codecs="""]
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1191833
expected: FAIL
[Test valid WebM type "AUDIO/WEBM;CODECS="vorbis""]
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1191833
expected: FAIL
[Test invalid mismatch between major type and codec ID "audio/webm;codecs="vp8""]
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1191833
expected: FAIL
[Test valid MP4 type "audio/mp4;codecs="mp4a.67""]

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

@ -4,7 +4,6 @@
# 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/.
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
FAIL_ON_WARNINGS = True

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

@ -21,6 +21,10 @@
#include "nsIDOMWindow.h"
#include "nsGlobalWindow.h"
#include "mozilla/unused.h"
#include "mozilla/Services.h"
#include "mozilla/Telemetry.h"
#if defined(XP_WIN)
#include "windows.h"
#else
@ -440,7 +444,7 @@ NS_IMETHODIMP nsPerformanceSnapshot::GetProcessData(nsIPerformanceStats * *aProc
}
NS_IMPL_ISUPPORTS(nsPerformanceStatsService, nsIPerformanceStatsService)
NS_IMPL_ISUPPORTS(nsPerformanceStatsService, nsIPerformanceStatsService, nsIObserver)
nsPerformanceStatsService::nsPerformanceStatsService()
#if defined(XP_WIN)
@ -448,7 +452,13 @@ nsPerformanceStatsService::nsPerformanceStatsService()
#else
: mProcessId(getpid())
#endif
, mProcessStayed(0)
, mProcessMoved(0)
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
mozilla::unused << obs->AddObserver(this, "profile-before-shutdown", false);
}
}
nsPerformanceStatsService::~nsPerformanceStatsService()
@ -507,8 +517,28 @@ NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerforman
return rv;
}
js::GetPerfMonitoringTestCpuRescheduling(JS_GetRuntime(cx), &mProcessStayed, &mProcessMoved);
snapshot.forget(aSnapshot);
return NS_OK;
}
/* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
NS_IMETHODIMP nsPerformanceStatsService::Observe(nsISupports *, const char *, const char16_t *)
{
// Upload telemetry
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
mozilla::unused << obs->RemoveObserver(this, "profile-before-shutdown");
}
if (mProcessStayed + mProcessMoved == 0) {
// Nothing to report.
return NS_OK;
}
const uint32_t proportion = ( 100 * mProcessStayed ) / ( mProcessStayed + mProcessMoved );
mozilla::Telemetry::Accumulate("PERF_MONITORING_TEST_CPU_RESCHEDULING_PROPORTION_MOVED", proportion);
return NS_OK;
}

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

@ -6,13 +6,16 @@
#ifndef nsPerformanceStats_h
#define nsPerformanceStats_h
#include "nsIObserver.h"
#include "nsIPerformanceStats.h"
class nsPerformanceStatsService : public nsIPerformanceStatsService
class nsPerformanceStatsService : public nsIPerformanceStatsService, nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPERFORMANCESTATSSERVICE
NS_DECL_NSIOBSERVER
nsPerformanceStatsService();
@ -20,6 +23,8 @@ private:
virtual ~nsPerformanceStatsService();
const uint64_t mProcessId;
uint64_t mProcessStayed;
uint64_t mProcessMoved;
protected:
};

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

@ -1,169 +0,0 @@
"use strict";
const {utils: Cu, interfaces: Ci, classes: Cc} = Components;
Cu.import("resource://gre/modules/Task.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/PerformanceStats.jsm", this);
function run_test() {
run_next_test();
}
let promiseStatistics = Task.async(function*(name) {
yield Promise.resolve(); // Make sure that we wait until
// statistics have been updated.
let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].
getService(Ci.nsIPerformanceStatsService);
let snapshot = service.getSnapshot();
let componentsData = [];
let componentsEnum = snapshot.getComponentsData().enumerate();
while (componentsEnum.hasMoreElements()) {
let data = componentsEnum.getNext().QueryInterface(Ci.nsIPerformanceStats);
let normalized = JSON.parse(JSON.stringify(data));
componentsData.push(data);
}
return {
processData: JSON.parse(JSON.stringify(snapshot.getProcessData())),
componentsData
};
});
let promiseSetMonitoring = Task.async(function*(to) {
let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].
getService(Ci.nsIPerformanceStatsService);
service.isMonitoringJank = to;
service.isMonitoringCPOW = to;
yield Promise.resolve();
});
let promiseSetPerCompartment = Task.async(function*(to) {
let service = Cc["@mozilla.org/toolkit/performance-stats-service;1"].
getService(Ci.nsIPerformanceStatsService);
service.isMonitoringPerCompartment = to;
yield Promise.resolve();
});
function getBuiltinStatistics(name, snapshot) {
let stats = snapshot.componentsData.find(stats =>
stats.isSystem && !stats.addonId
);
do_print(`Built-in statistics for ${name} were ${stats?"":"not "}found`);
do_print(JSON.stringify(snapshot.componentsData, null, "\t"));
return stats;
}
function burnCPU(ms) {
do_print("Burning CPU");
let counter = 0;
let ignored = [];
let start = Date.now();
while (Date.now() - start < ms) {
ignored.push(0);
ignored.shift();
++counter;
}
do_print("Burning CPU over, after " + counter + " iterations");
}
function ensureEquals(snap1, snap2, name) {
for (let k of Object.keys(snap1.processData)) {
if (k == "ticks") {
// Ticks monitoring cannot be deactivated
continue;
}
Assert.equal(snap1.processData[k], snap2.processData[k], `Same process data value ${k} (${name})`)
}
let stats1 = snap1.componentsData.sort((a, b) => a.name <= b.name);
let stats2 = snap2.componentsData.sort((a, b) => a.name <= b.name);
Assert.equal(stats1.length, stats2.length, `Same number of components (${name})`);
for (let i = 0; i < stats1.length; ++i) {
for (let k of Object.keys(stats1[i])) {
if (k == "ticks") {
// Ticks monitoring cannot be deactivated
continue;
}
Assert.equal(stats1[i][k], stats1[i][k], `Same component data value ${i} ${k} (${name})`)
}
}
}
function hasLowPrecision() {
let [sysName, sysVersion] = [Services.sysinfo.getPropertyAsAString("name"), Services.sysinfo.getPropertyAsDouble("version")];
do_print(`Running ${sysName} version ${sysVersion}`);
if (sysName == "Windows_NT" && sysVersion < 6) {
do_print("Running old Windows, need to deactivate tests due to bad precision.");
return true;
}
if (sysName == "Linux" && sysVersion <= 2.6) {
do_print("Running old Linux, need to deactivate tests due to bad precision.");
return true;
}
do_print("This platform has good precision.")
return false;
}
add_task(function* test_measure() {
let skipPrecisionTests = hasLowPrecision();
yield promiseSetPerCompartment(false);
do_print("Burn CPU without the stopwatch");
yield promiseSetMonitoring(false);
let stats0 = yield promiseStatistics("Initial state");
burnCPU(300);
let stats1 = yield promiseStatistics("Initial state + burn, without stopwatch");
do_print("Burn CPU with the stopwatch");
yield promiseSetMonitoring(true);
burnCPU(300);
let stats2 = yield promiseStatistics("Second burn, with stopwatch");
do_print("Burn CPU without the stopwatch again")
yield promiseSetMonitoring(false);
let stats3 = yield promiseStatistics("Before third burn, without stopwatch");
burnCPU(300);
let stats4 = yield promiseStatistics("After third burn, without stopwatch");
ensureEquals(stats0, stats1, "Initial state vs. Initial state + burn, without stopwatch");
let process1 = stats1.processData;
let process2 = stats2.processData;
let process3 = stats3.processData;
let process4 = stats4.processData;
if (skipPrecisionTests) {
do_print("Skipping totalUserTime check under Windows XP, as timer is not always updated by the OS.")
} else {
Assert.ok(process2.totalUserTime - process1.totalUserTime >= 10000, `At least 10ms counted for process time (${process2.totalUserTime - process1.totalUserTime})`);
}
Assert.equal(process2.totalCPOWTime, process1.totalCPOWTime, "We haven't used any CPOW time during the first burn");
Assert.equal(process4.totalUserTime, process3.totalUserTime, "After deactivating the stopwatch, we didn't count any time");
Assert.equal(process4.totalCPOWTime, process3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time");
let builtin1 = getBuiltinStatistics("Built-ins 1", stats1) || { totalUserTime: 0, totalCPOWTime: 0 };
let builtin2 = getBuiltinStatistics("Built-ins 2", stats2);
let builtin3 = getBuiltinStatistics("Built-ins 3", stats3);
let builtin4 = getBuiltinStatistics("Built-ins 4", stats4);
Assert.notEqual(builtin2, null, "Found the statistics for built-ins 2");
Assert.notEqual(builtin3, null, "Found the statistics for built-ins 3");
Assert.notEqual(builtin4, null, "Found the statistics for built-ins 4");
if (skipPrecisionTests) {
do_print("Skipping totalUserTime check under Windows XP, as timer is not always updated by the OS.")
} else {
Assert.ok(builtin2.totalUserTime - builtin1.totalUserTime >= 10000, `At least 10ms counted for built-in statistics (${builtin2.totalUserTime - builtin1.totalUserTime})`);
}
Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "We haven't used any CPOW time during the first burn for the built-in");
Assert.equal(builtin2.totalCPOWTime, builtin1.totalCPOWTime, "No CPOW for built-in statistics");
Assert.equal(builtin4.totalUserTime, builtin3.totalUserTime, "After deactivating the stopwatch, we didn't count any time for the built-in");
Assert.equal(builtin4.totalCPOWTime, builtin3.totalCPOWTime, "After deactivating the stopwatch, we didn't count any CPOW time for the built-in");
// Ideally, we should be able to look for test_compartments.js, but
// it doesn't have its own compartment.
for (let stats of [stats1, stats2, stats3, stats4]) {
Assert.ok(!stats.componentsData.find(x => x.name.includes("Task.jsm")), "At this stage, Task.jsm doesn't show up in the components data");
}
yield promiseSetPerCompartment(true);
burnCPU(300);
let stats5 = yield promiseStatistics("With per-compartment monitoring");
Assert.ok(stats5.componentsData.find(x => x.name.includes("Task.jsm")), "With per-compartment monitoring, test_compartments.js shows up");
});

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

@ -1,6 +0,0 @@
[DEFAULT]
head=
tail=
[test_compartments.js]
skip-if = toolkit == 'gonk' # Fails on b2g emulator, bug 1147664

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

@ -63,13 +63,17 @@ support-files =
[test_async.js]
[test_async_app.js]
[test_async_addon.js]
tags = addons
[test_async_addon_no_override.js]
tags = addons
[test_async_distribution.js]
[test_async_profile_engine.js]
[test_sync.js]
[test_sync_app.js]
[test_sync_addon.js]
tags = addons
[test_sync_addon_no_override.js]
tags = addons
[test_sync_distribution.js]
[test_sync_fallback.js]
[test_sync_delay_fallback.js]

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

@ -8934,5 +8934,13 @@
"high": "30",
"n_buckets": "29",
"description": "The number of times AddIceCandidate failed on a given PeerConnection, given that ICE failed."
},
"PERF_MONITORING_TEST_CPU_RESCHEDULING_PROPORTION_MOVED": {
"alert_emails": ["dteller@mozilla.com"],
"expires_in_version": "44",
"kind": "linear",
"high": "100",
"n_buckets": "20",
"description": "Proportion (%) of reschedulings of the main process to another CPU during the execution of code inside a JS compartment. Updated while we are measuring jank."
}
}

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

@ -23,9 +23,11 @@ generated-files =
[test_nsITelemetry.js]
[test_SubsessionChaining.js]
tags = addons
[test_TelemetryEnvironment.js]
# Bug 1144395: crash on Android 4.3
skip-if = android_version == "18"
tags = addons
[test_PingAPI.js]
skip-if = os == "android"
[test_TelemetryFlagClear.js]
@ -37,20 +39,26 @@ skip-if = os == "android"
# fail-if = os == "android"
# Bug 1144395: crash on Android 4.3
skip-if = android_version == "18"
tags = addons
[test_TelemetryController_idle.js]
[test_TelemetryControllerShutdown.js]
tags = addons
[test_TelemetryStopwatch.js]
[test_TelemetryControllerBuildID.js]
# Bug 1144395: crash on Android 4.3
skip-if = android_version == "18"
[test_TelemetrySendOldPings.js]
skip-if = os == "android" # Disabled due to intermittent orange on Android
tags = addons
[test_TelemetrySession.js]
# Bug 1144395: crash on Android 4.3
skip-if = android_version == "18"
tags = addons
[test_ThreadHangStats.js]
run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high
[test_TelemetrySend.js]
[test_ChildHistograms.js]
skip-if = os == "android"
tags = addons
[test_TelemetryReportingPolicy.js]
tags = addons

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

@ -1,6 +1,7 @@
# The file is shared between the two main xpcshell manifest files.
[DEFAULT]
skip-if = toolkit == 'android' || toolkit == 'gonk'
tags = addons
[test_AddonRepository.js]
# Bug 676992: test consistently hangs on Android

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

@ -4,5 +4,6 @@ tail =
firefox-appdir = browser
skip-if = toolkit == 'android' || toolkit == 'gonk'
dupe-manifest =
tags = addons
[include:xpcshell-shared.ini]

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

@ -1,5 +1,6 @@
[DEFAULT]
skip-if = buildapp == 'mulet' || toolkit == 'android' || toolkit == 'gonk'
tags = addons
head = head_addons.js
tail =
firefox-appdir = browser

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

@ -157,18 +157,6 @@ NS_IMETHODIMP nsDeviceContextSpecGTK::GetSurfaceForPrinter(gfxASurface **aSurfac
// There is nothing to detect on Print Preview, use PS.
format = nsIPrintSettings::kOutputFormatPS;
} else {
const gchar* fmtGTK = gtk_print_settings_get(mGtkPrintSettings, GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT);
if (fmtGTK) {
if (nsDependentCString(fmtGTK).EqualsIgnoreCase("pdf")) {
format = nsIPrintSettings::kOutputFormatPDF;
} else {
format = nsIPrintSettings::kOutputFormatPS;
}
}
}
// If we haven't found the format at this point, we're sunk. :(
if (format == nsIPrintSettings::kOutputFormatNative) {
return NS_ERROR_FAILURE;
}
}

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

@ -200,17 +200,44 @@ nsPrintSettingsGTK::SetGtkPrinter(GtkPrinter *aPrinter)
g_object_unref(mGTKPrinter);
mGTKPrinter = (GtkPrinter*) g_object_ref(aPrinter);
}
// Prior to gtk 2.24, gtk_printer_accepts_pdf() and
// gtk_printer_accepts_ps() always returned true regardless of the
// printer's capability.
bool shouldTrustGTK =
(gtk_major_version > 2 ||
(gtk_major_version == 2 && gtk_minor_version >= 24));
bool acceptsPDF = shouldTrustGTK && gtk_printer_accepts_pdf(mGTKPrinter);
NS_IMETHODIMP nsPrintSettingsGTK::GetOutputFormat(int16_t *aOutputFormat)
{
NS_ENSURE_ARG_POINTER(aOutputFormat);
SetOutputFormat(acceptsPDF ? nsIPrintSettings::kOutputFormatPDF
: nsIPrintSettings::kOutputFormatPS);
int16_t format;
nsresult rv = nsPrintSettings::GetOutputFormat(&format);
if (NS_FAILED(rv)) {
return rv;
}
if (format == nsIPrintSettings::kOutputFormatNative) {
const gchar* fmtGTK =
gtk_print_settings_get(mPrintSettings,
GTK_PRINT_SETTINGS_OUTPUT_FILE_FORMAT);
if (fmtGTK) {
if (nsDependentCString(fmtGTK).EqualsIgnoreCase("pdf")) {
format = nsIPrintSettings::kOutputFormatPDF;
} else {
format = nsIPrintSettings::kOutputFormatPS;
}
} else if (GTK_IS_PRINTER(mGTKPrinter)) {
// Prior to gtk 2.24, gtk_printer_accepts_pdf() and
// gtk_printer_accepts_ps() always returned true regardless of the
// printer's capability.
bool shouldTrustGTK =
(gtk_major_version > 2 ||
(gtk_major_version == 2 && gtk_minor_version >= 24));
bool acceptsPDF = shouldTrustGTK && gtk_printer_accepts_pdf(mGTKPrinter);
format = acceptsPDF ? nsIPrintSettings::kOutputFormatPDF
: nsIPrintSettings::kOutputFormatPS;
}
}
*aOutputFormat = format;
return NS_OK;
}
/**

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

@ -113,6 +113,8 @@ public:
NS_IMETHOD GetDuplex(int32_t *aDuplex) override;
NS_IMETHOD SetDuplex(int32_t aDuplex) override;
NS_IMETHOD GetOutputFormat(int16_t *aOutputFormat) override;
protected:
virtual ~nsPrintSettingsGTK();

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

@ -566,6 +566,8 @@ GetSysFontInfo(HDC aHDC, LookAndFeel::FontID anID,
case LookAndFeel::eFont_Widget:
case LookAndFeel::eFont_Dialog:
case LookAndFeel::eFont_Button:
case LookAndFeel::eFont_Field:
case LookAndFeel::eFont_List:
// XXX It's not clear to me whether this is exactly the right
// set of LookAndFeel values to map to the dialog font; we may
// want to add or remove cases here after reviewing the visual

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

@ -55,18 +55,19 @@ static bool sLoggingBuffered = true;
static bool sLoggingLogcat = true;
#endif // defined(ANDROID)
nsConsoleService::MessageElement::~MessageElement()
{
}
nsConsoleService::nsConsoleService()
: mMessages(nullptr)
, mCurrent(0)
, mFull(false)
: mCurrentSize(0)
, mDeliveringMessage(false)
, mLock("nsConsoleService.mLock")
{
// XXX grab this from a pref!
// hm, but worry about circularity, bc we want to be able to report
// prefs errs...
mBufferSize = 250;
mMaximumSize = 250;
}
@ -75,53 +76,46 @@ nsConsoleService::ClearMessagesForWindowID(const uint64_t innerID)
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
// Remove the messages related to this window
for (uint32_t i = 0; i < mBufferSize && mMessages[i]; i++) {
// Only messages implementing nsIScriptError interface exposes the inner window ID
nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(mMessages[i]);
for (MessageElement* e = mMessages.getFirst(); e != nullptr; ) {
// Only messages implementing nsIScriptError interface expose the
// inner window ID.
nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(e->Get());
if (!scriptError) {
e = e->getNext();
continue;
}
uint64_t innerWindowID;
nsresult rv = scriptError->GetInnerWindowID(&innerWindowID);
if (NS_FAILED(rv) || innerWindowID != innerID) {
e = e->getNext();
continue;
}
// Free this matching message!
NS_RELEASE(mMessages[i]);
MessageElement* next = e->getNext();
e->remove();
delete e;
mCurrentSize--;
MOZ_ASSERT(mCurrentSize < mMaximumSize);
uint32_t j = i;
// Now shift all the following messages
// XXXkhuey this is not an efficient way to iterate through an array ...
for (; j < mBufferSize - 1 && mMessages[j + 1]; j++) {
mMessages[j] = mMessages[j + 1];
}
// Nullify the current slot
mMessages[j] = nullptr;
mCurrent = j;
// The array is no longer full
mFull = false;
// Ensure the next iteration handles the messages we just shifted down
i--;
e = next;
}
}
void
nsConsoleService::ClearMessages()
{
while (!mMessages.isEmpty()) {
MessageElement* e = mMessages.popFirst();
delete e;
}
mCurrentSize = 0;
}
nsConsoleService::~nsConsoleService()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
uint32_t i = 0;
while (i < mBufferSize && mMessages[i]) {
NS_RELEASE(mMessages[i]);
i++;
}
if (mMessages) {
free(mMessages);
}
ClearMessages();
}
class AddConsolePrefWatchers : public nsRunnable
@ -157,15 +151,6 @@ private:
nsresult
nsConsoleService::Init()
{
mMessages = (nsIConsoleMessage**)
moz_xmalloc(mBufferSize * sizeof(nsIConsoleMessage*));
if (!mMessages) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Array elements should be 0 initially for circular buffer algorithm.
memset(mMessages, 0, mBufferSize * sizeof(nsIConsoleMessage*));
NS_DispatchToMainThread(new AddConsolePrefWatchers(this));
return NS_OK;
@ -241,11 +226,7 @@ nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage,
}
nsRefPtr<LogMessageRunnable> r;
nsIConsoleMessage* retiredMessage;
if (sLoggingBuffered) {
NS_ADDREF(aMessage); // early, in case it's same as replaced below.
}
nsCOMPtr<nsIConsoleMessage> retiredMessage;
/*
* Lock while updating buffer, and while taking snapshot of
@ -311,18 +292,16 @@ nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage,
}
#endif
/*
* If there's already a message in the slot we're about to replace,
* we've wrapped around, and we need to release the old message. We
* save a pointer to it, so we can release below outside the lock.
*/
retiredMessage = mMessages[mCurrent];
if (sLoggingBuffered) {
mMessages[mCurrent++] = aMessage;
if (mCurrent == mBufferSize) {
mCurrent = 0; // wrap around.
mFull = true;
MessageElement* e = new MessageElement(aMessage);
mMessages.insertBack(e);
if (mCurrentSize != mMaximumSize) {
mCurrentSize++;
} else {
MessageElement* p = mMessages.popFirst();
MOZ_ASSERT(p);
retiredMessage = p->forget();
delete p;
}
}
@ -377,21 +356,15 @@ nsConsoleService::GetMessageArray(uint32_t* aCount,
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsIConsoleMessage** messageArray;
/*
* Lock the whole method, as we don't want anyone mucking with mCurrent or
* mFull while we're copying out the buffer.
*/
MutexAutoLock lock(mLock);
if (mCurrent == 0 && !mFull) {
if (mMessages.isEmpty()) {
/*
* Make a 1-length output array so that nobody gets confused,
* and return a count of 0. This should result in a 0-length
* array object when called from script.
*/
messageArray = (nsIConsoleMessage**)
nsIConsoleMessage** messageArray = (nsIConsoleMessage**)
moz_xmalloc(sizeof(nsIConsoleMessage*));
*messageArray = nullptr;
*aMessages = messageArray;
@ -400,32 +373,21 @@ nsConsoleService::GetMessageArray(uint32_t* aCount,
return NS_OK;
}
uint32_t resultSize = mFull ? mBufferSize : mCurrent;
messageArray =
(nsIConsoleMessage**)moz_xmalloc((sizeof(nsIConsoleMessage*))
* resultSize);
MOZ_ASSERT(mCurrentSize <= mMaximumSize);
nsIConsoleMessage** messageArray =
static_cast<nsIConsoleMessage**>(moz_xmalloc(sizeof(nsIConsoleMessage*)
* mCurrentSize));
if (!messageArray) {
*aMessages = nullptr;
*aCount = 0;
return NS_ERROR_FAILURE;
}
uint32_t i = 0;
for (MessageElement* e = mMessages.getFirst(); e != nullptr; e = e->getNext()) {
nsCOMPtr<nsIConsoleMessage> m = e->Get();
m.forget(&messageArray[i]);
i++;
};
uint32_t i;
if (mFull) {
for (i = 0; i < mBufferSize; i++) {
// if full, fill the buffer starting from mCurrent (which'll be
// oldest) wrapping around the buffer to the most recent.
messageArray[i] = mMessages[(mCurrent + i) % mBufferSize];
NS_ADDREF(messageArray[i]);
}
} else {
for (i = 0; i < mCurrent; i++) {
messageArray[i] = mMessages[i];
NS_ADDREF(messageArray[i]);
}
}
*aCount = resultSize;
MOZ_ASSERT(i == mCurrentSize);
*aCount = i;
*aMessages = messageArray;
return NS_OK;
@ -480,16 +442,7 @@ nsConsoleService::Reset()
*/
MutexAutoLock lock(mLock);
mCurrent = 0;
mFull = false;
/*
* Free all messages stored so far (cf. destructor)
*/
for (uint32_t i = 0; i < mBufferSize && mMessages[i]; i++) {
NS_RELEASE(mMessages[i]);
}
ClearMessages();
return NS_OK;
}

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

@ -60,21 +60,45 @@ public:
void CollectCurrentListeners(nsCOMArray<nsIConsoleListener>& aListeners);
private:
class MessageElement : public mozilla::LinkedListElement<MessageElement>
{
public:
explicit MessageElement(nsIConsoleMessage* aMessage) : mMessage(aMessage)
{}
nsIConsoleMessage* Get()
{
return mMessage.get();
}
already_AddRefed<nsIConsoleMessage> forget()
{
return mMessage.forget();
}
~MessageElement();
private:
nsCOMPtr<nsIConsoleMessage> mMessage;
MessageElement(const MessageElement&) = delete;
MessageElement& operator=(const MessageElement&) = delete;
MessageElement(MessageElement&&) = delete;
MessageElement& operator=(MessageElement&&) = delete;
};
~nsConsoleService();
void ClearMessagesForWindowID(const uint64_t innerID);
void ClearMessages();
// Circular buffer of saved messages
nsIConsoleMessage** mMessages;
mozilla::LinkedList<MessageElement> mMessages;
// How big?
uint32_t mBufferSize;
// The current size of mMessages.
uint32_t mCurrentSize;
// Index of slot in mMessages that'll be filled by *next* log message
uint32_t mCurrent;
// Is the buffer full? (Has mCurrent wrapped around at least once?)
bool mFull;
// The maximum size of mMessages.
uint32_t mMaximumSize;
// Are we currently delivering a console message on the main thread? If
// so, we suppress incoming messages on the main thread only, to avoid

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

@ -451,15 +451,15 @@ main()
else
printf("foo1p == foo2p\n");
printf("\n### Test 7.5: can you compare a |nsCOMPtr| with nullptr [!=]?\n");
if ( foo1p != nullptr )
printf("foo1p != nullptr\n");
if ( nullptr != foo1p )
printf("nullptr != foo1p\n");
if ( foo1p == nullptr )
printf("foo1p == nullptr\n");
if ( nullptr == foo1p )
printf("nullptr == foo1p\n");
printf("\n### Test 7.5: can you compare a |nsCOMPtr| with NULL, 0, nullptr [!=]?\n");
if ( foo1p != 0 )
printf("foo1p != 0\n");
if ( 0 != foo1p )
printf("0 != foo1p\n");
if ( foo1p == 0 )
printf("foo1p == 0\n");
if ( 0 == foo1p )
printf("0 == foo1p\n");
Foo* raw_foo2p = foo2p.get();
@ -500,8 +500,8 @@ main()
else
printf("foo1p is NULL\n");
printf("\n### Test 13: null pointer test?\n");
if ( foo1p == nullptr )
printf("\n### Test 13: numeric pointer test?\n");
if ( foo1p == 0 )
printf("foo1p is NULL\n");
else
printf("foo1p is not NULL\n");