merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-11-26 16:54:27 +01:00
commit 358e4575c4
309 changed files with 5611 additions and 3806 deletions

View File

@ -1,5 +0,0 @@
<!DOCTYPE html [
<!ENTITY % passwordManagerDTD SYSTEM "chrome://passwordmgr/locale/passwordManager.dtd">
%passwordManagerDTD;
]>
<window>&savedLogins.title;</window>

View File

@ -25,7 +25,6 @@ support-files =
offlineEvent.html
subtst_contextmenu.html
video.ogg
bug_1182546.xml
[test_bug364677.html]
[test_bug395533.html]
@ -39,4 +38,3 @@ skip-if = e10s
skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
[test_offline_gzip.html]
skip-if = buildapp == 'mulet' || e10s # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
[test_bug1182546.html]

View File

@ -1,35 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1182546
-->
<head>
<title>Bug 1182546 - Test block loading DTD from random page</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<iframe id="testframe" src="bug_1182546.xml"></iframe>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
// make sure the DTD loader (nsExpatDriver) prevents accessing chrome: from random pages
var childNodes = testframe.contentDocument.documentElement.childNodes;
// make sure '&savedLogins.title;' from bug_1182546.xml does not translate into 'Saved Logins'
// the URL 'chrome://passwordmgr/locale/passwordManager.dtd' should not be accessible from content
var nodeValue = childNodes[0].nodeValue;
isnot(nodeValue, "Saved Logins",
"expatDriver should prevent accessing &savedLogins.title;");
ok(nodeValue.startsWith("XML Parsing Error: undefined entity"),
"expatDriver should not allow accessing chrome:");
});
addLoadEvent(SimpleTest.finish);
</script>
</body>
</html>

View File

@ -64,6 +64,8 @@ var LoopMochaUtils = (function(global, _) {
throw result;
};
this.catch = function() {};
asyncFn(this.resolve.bind(this), this.reject.bind(this));
}

View File

@ -66,9 +66,9 @@ if test "$COMPILER_WRAPPER" != "no"; then
_SUBDIR_CXX="$CXX"
ac_cv_prog_CC="$CC"
ac_cv_prog_CXX="$CXX"
MOZ_USING_COMPILER_WRAPPER=1
;;
esac
MOZ_USING_COMPILER_WRAPPER=1
fi
AC_SUBST(MOZ_USING_COMPILER_WRAPPER)

View File

@ -6987,7 +6987,7 @@ AC_SUBST(MOZ_FRAMEPTR_FLAGS)
AC_SUBST(MOZ_OPTIMIZE_FLAGS)
AC_SUBST(MOZ_OPTIMIZE_RUSTFLAGS)
AC_SUBST(MOZ_OPTIMIZE_LDFLAGS)
AC_SUBST(MOZ_ALLOW_HEAP_EXECUTE_FLAGS)
AC_SUBST_LIST(MOZ_ALLOW_HEAP_EXECUTE_FLAGS)
AC_SUBST(MOZ_PGO)
AC_SUBST(MOZ_PGO_OPTIMIZE_FLAGS)

View File

@ -1058,6 +1058,7 @@ Animation::UpdateEffect()
{
if (mEffect) {
UpdateRelevance();
mEffect->NotifyAnimationTimingUpdated();
}
}

119
dom/animation/EffectSet.cpp Normal file
View File

@ -0,0 +1,119 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "EffectSet.h"
#include "mozilla/dom/Element.h" // For Element
#include "nsCycleCollectionNoteChild.h" // For CycleCollectionNoteChild
namespace mozilla {
/* static */ void
EffectSet::PropertyDtor(void* aObject, nsIAtom* aPropertyName,
void* aPropertyValue, void* aData)
{
EffectSet* effectSet = static_cast<EffectSet*>(aPropertyValue);
#ifdef DEBUG
MOZ_ASSERT(!effectSet->mCalledPropertyDtor, "Should not call dtor twice");
effectSet->mCalledPropertyDtor = true;
#endif
delete effectSet;
}
void
EffectSet::Traverse(nsCycleCollectionTraversalCallback& aCallback)
{
for (auto iter = mEffects.Iter(); !iter.Done(); iter.Next()) {
CycleCollectionNoteChild(aCallback, iter.Get()->GetKey(),
"EffectSet::mEffects[]", aCallback.Flags());
}
}
/* static */ EffectSet*
EffectSet::GetEffectSet(dom::Element* aElement,
nsCSSPseudoElements::Type aPseudoType)
{
nsIAtom* propName = GetEffectSetPropertyAtom(aPseudoType);
return static_cast<EffectSet*>(aElement->GetProperty(propName));
}
/* static */ EffectSet*
EffectSet::GetOrCreateEffectSet(dom::Element* aElement,
nsCSSPseudoElements::Type aPseudoType)
{
EffectSet* effectSet = GetEffectSet(aElement, aPseudoType);
if (effectSet) {
return effectSet;
}
nsIAtom* propName = GetEffectSetPropertyAtom(aPseudoType);
effectSet = new EffectSet();
nsresult rv = aElement->SetProperty(propName, effectSet,
&EffectSet::PropertyDtor, true);
if (NS_FAILED(rv)) {
NS_WARNING("SetProperty failed");
// The set must be destroyed via PropertyDtor, otherwise
// mCalledPropertyDtor assertion is triggered in destructor.
EffectSet::PropertyDtor(aElement, propName, effectSet, nullptr);
return nullptr;
}
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
aElement->SetMayHaveAnimations();
}
return effectSet;
}
/* static */ nsIAtom**
EffectSet::GetEffectSetPropertyAtoms()
{
static nsIAtom* effectSetPropertyAtoms[] =
{
nsGkAtoms::animationEffectsProperty,
nsGkAtoms::animationEffectsForBeforeProperty,
nsGkAtoms::animationEffectsForAfterProperty,
nullptr
};
return effectSetPropertyAtoms;
}
/* static */ nsIAtom*
EffectSet::GetEffectSetPropertyAtom(nsCSSPseudoElements::Type aPseudoType)
{
switch (aPseudoType) {
case nsCSSPseudoElements::ePseudo_NotPseudoElement:
return nsGkAtoms::animationEffectsProperty;
case nsCSSPseudoElements::ePseudo_before:
return nsGkAtoms::animationEffectsForBeforeProperty;
case nsCSSPseudoElements::ePseudo_after:
return nsGkAtoms::animationEffectsForAfterProperty;
default:
NS_NOTREACHED("Should not try to get animation effects for a pseudo "
"other that :before or :after");
return nullptr;
}
}
void
EffectSet::AddEffect(dom::KeyframeEffectReadOnly& aEffect)
{
mEffects.PutEntry(&aEffect);
}
void
EffectSet::RemoveEffect(dom::KeyframeEffectReadOnly& aEffect)
{
mEffects.RemoveEntry(&aEffect);
}
} // namespace mozilla

136
dom/animation/EffectSet.h Normal file
View File

@ -0,0 +1,136 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_EffectSet_h
#define mozilla_EffectSet_h
#include "nsCSSPseudoElements.h" // For nsCSSPseudoElements::Type
#include "nsHashKeys.h" // For nsPtrHashKey
#include "nsTHashtable.h" // For nsTHashtable
namespace mozilla {
namespace dom {
class Element;
class KeyframeEffectReadOnly;
} // namespace dom
// A wrapper around a hashset of AnimationEffect objects to handle
// storing the set as a property of an element.
class EffectSet
{
public:
EffectSet()
#ifdef DEBUG
: mCalledPropertyDtor(false)
#endif
{
MOZ_COUNT_CTOR(EffectSet);
}
~EffectSet()
{
MOZ_ASSERT(mCalledPropertyDtor,
"must call destructor through element property dtor");
MOZ_COUNT_DTOR(EffectSet);
}
static void PropertyDtor(void* aObject, nsIAtom* aPropertyName,
void* aPropertyValue, void* aData);
// Methods for supporting cycle-collection
void Traverse(nsCycleCollectionTraversalCallback& aCallback);
static EffectSet* GetEffectSet(dom::Element* aElement,
nsCSSPseudoElements::Type aPseudoType);
static EffectSet* GetOrCreateEffectSet(dom::Element* aElement,
nsCSSPseudoElements::Type aPseudoType);
void AddEffect(dom::KeyframeEffectReadOnly& aEffect);
void RemoveEffect(dom::KeyframeEffectReadOnly& aEffect);
private:
typedef nsTHashtable<nsRefPtrHashKey<dom::KeyframeEffectReadOnly>>
OwningEffectSet;
public:
// A simple iterator to support iterating over the effects in this object in
// range-based for loops.
//
// This allows us to avoid exposing mEffects directly and saves the
// caller from having to dereference hashtable iterators using
// the rather complicated: iter.Get()->GetKey().
class Iterator
{
public:
explicit Iterator(OwningEffectSet::Iterator&& aHashIterator)
: mHashIterator(mozilla::Move(aHashIterator))
, mIsEndIterator(false) { }
Iterator(Iterator&& aOther)
: mHashIterator(mozilla::Move(aOther.mHashIterator))
, mIsEndIterator(aOther.mIsEndIterator) { }
static Iterator EndIterator(OwningEffectSet::Iterator&& aHashIterator)
{
Iterator result(mozilla::Move(aHashIterator));
result.mIsEndIterator = true;
return result;
}
bool operator!=(const Iterator& aOther) const {
if (Done() || aOther.Done()) {
return Done() != aOther.Done();
}
return mHashIterator.Get() != aOther.mHashIterator.Get();
}
Iterator& operator++() {
MOZ_ASSERT(!Done());
mHashIterator.Next();
return *this;
}
dom::KeyframeEffectReadOnly* operator* ()
{
MOZ_ASSERT(!Done());
return mHashIterator.Get()->GetKey();
}
private:
Iterator() = delete;
Iterator(const Iterator&) = delete;
Iterator& operator=(const Iterator&) = delete;
Iterator& operator=(const Iterator&&) = delete;
bool Done() const {
return mIsEndIterator || mHashIterator.Done();
}
OwningEffectSet::Iterator mHashIterator;
bool mIsEndIterator;
};
Iterator begin() { return Iterator(mEffects.Iter()); }
Iterator end()
{
return Iterator::EndIterator(mEffects.Iter());
}
static nsIAtom** GetEffectSetPropertyAtoms();
private:
static nsIAtom* GetEffectSetPropertyAtom(nsCSSPseudoElements::Type
aPseudoType);
OwningEffectSet mEffects;
#ifdef DEBUG
bool mCalledPropertyDtor;
#endif
};
} // namespace mozilla
#endif // mozilla_EffectSet_h

View File

@ -127,6 +127,9 @@ KeyframeEffectReadOnly::SetTiming(const AnimationTiming& aTiming)
if (mAnimation) {
mAnimation->NotifyEffectTimingUpdated();
}
// NotifyEffectTimingUpdated will eventually cause
// NotifyAnimationTimingUpdated to be called on this object which will
// update our registration with the target element.
}
Nullable<TimeDuration>
@ -336,6 +339,7 @@ void
KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation)
{
mAnimation = aAnimation;
NotifyAnimationTimingUpdated();
}
const AnimationProperty*
@ -511,8 +515,6 @@ KeyframeEffectReadOnly::SetIsRunningOnCompositor(nsCSSProperty aProperty,
}
}
// We need to define this here since Animation is an incomplete type
// (forward-declared) in the header.
KeyframeEffectReadOnly::~KeyframeEffectReadOnly()
{
}
@ -525,6 +527,34 @@ KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
}
}
void
KeyframeEffectReadOnly::UpdateTargetRegistration()
{
if (!mTarget) {
return;
}
bool isRelevant = mAnimation && mAnimation->IsRelevant();
// Animation::IsRelevant() returns a cached value. It only updates when
// something calls Animation::UpdateRelevance. Whenever our timing changes,
// we should be notifying our Animation before calling this, so
// Animation::IsRelevant() should be up-to-date by the time we get here.
MOZ_ASSERT(isRelevant == IsCurrent() || IsInEffect(),
"Out of date Animation::IsRelevant value");
if (isRelevant) {
EffectSet* effectSet = EffectSet::GetOrCreateEffectSet(mTarget,
mPseudoType);
effectSet->AddEffect(*this);
} else {
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
if (effectSet) {
effectSet->RemoveEffect(*this);
}
}
}
#ifdef DEBUG
void
DumpAnimationProperties(nsTArray<AnimationProperty>& aAnimationProperties)

View File

@ -219,6 +219,7 @@ public:
const AnimationTiming& Timing() const { return mTiming; }
AnimationTiming& Timing() { return mTiming; }
void SetTiming(const AnimationTiming& aTiming);
void NotifyAnimationTimingUpdated() { UpdateTargetRegistration(); }
Nullable<TimeDuration> GetLocalTime() const;
@ -256,6 +257,7 @@ public:
bool IsInEffect() const;
void SetAnimation(Animation* aAnimation);
Animation* GetAnimation() const { return mAnimation; }
const AnimationProperty*
GetAnimationOfProperty(nsCSSProperty aProperty) const;
@ -297,6 +299,17 @@ protected:
virtual ~KeyframeEffectReadOnly();
void ResetIsRunningOnCompositor();
// This effect is registered with its target element so long as:
//
// (a) It has a target element, and
// (b) It is "relevant" (i.e. yet to finish but not idle, or finished but
// filling forwards)
//
// As a result, we need to make sure this gets called whenever anything
// changes with regards to this effects's timing including changes to the
// owning Animation's timing.
void UpdateTargetRegistration();
static AnimationTiming ConvertKeyframeEffectOptions(
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions);

View File

@ -19,6 +19,7 @@ EXPORTS.mozilla += [
'AnimationComparator.h',
'AnimationUtils.h',
'ComputedTimingFunction.h',
'EffectSet.h',
'PendingAnimationTracker.h',
]
@ -28,6 +29,7 @@ UNIFIED_SOURCES += [
'AnimationTimeline.cpp',
'ComputedTimingFunction.cpp',
'DocumentTimeline.cpp',
'EffectSet.cpp',
'KeyframeEffect.cpp',
'PendingAnimationTracker.cpp',
]

View File

@ -54,6 +54,7 @@
#include "mozilla/AnimationComparator.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EffectSet.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventStateManager.h"
@ -3305,24 +3306,22 @@ Element::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
doc->FlushPendingNotifications(Flush_Style);
}
nsIAtom* properties[] = { nsGkAtoms::transitionsProperty,
nsGkAtoms::animationsProperty };
for (size_t propIdx = 0; propIdx < MOZ_ARRAY_LENGTH(properties);
propIdx++) {
AnimationCollection* collection =
static_cast<AnimationCollection*>(
GetProperty(properties[propIdx]));
if (!collection) {
continue;
}
for (size_t animIdx = 0;
animIdx < collection->mAnimations.Length();
animIdx++) {
Animation* anim = collection->mAnimations[animIdx];
if (anim->IsRelevant()) {
aAnimations.AppendElement(anim);
}
}
EffectSet* effects = EffectSet::GetEffectSet(this,
nsCSSPseudoElements::ePseudo_NotPseudoElement);
if (!effects) {
return;
}
for (KeyframeEffectReadOnly* effect : *effects) {
MOZ_ASSERT(effect && effect->GetAnimation(),
"Only effects associated with an animation should be "
"added to an element's effect set");
Animation* animation = effect->GetAnimation();
MOZ_ASSERT(animation->IsRelevant(),
"Only relevant animations should be added to an element's "
"effect set");
aAnimations.AppendElement(animation);
}
aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());

View File

@ -18,6 +18,7 @@
#include "mozilla/dom/FragmentOrElement.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/EffectSet.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventStates.h"
@ -1351,6 +1352,11 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
for (uint32_t i = 0; props[i]; ++i) {
tmp->DeleteProperty(*props[i]);
}
// Bug 1226091: Call MayHaveAnimations() first
nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
for (uint32_t i = 0; effectProps[i]; ++i) {
tmp->DeleteProperty(effectProps[i]);
}
}
}
@ -1901,6 +1907,15 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
cb.NoteXPCOMChild(property);
}
// Bug 1226091: Check MayHaveAnimations() first
nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
for (uint32_t i = 0; effectProps[i]; ++i) {
EffectSet* effectSet =
static_cast<EffectSet*>(tmp->GetProperty(effectProps[i]));
if (effectSet) {
effectSet->Traverse(cb);
}
}
}
}

View File

@ -2099,6 +2099,9 @@ GK_ATOM(ongamepaddisconnected, "ongamepaddisconnected")
GK_ATOM(animationsProperty, "AnimationsProperty") // FrameAnimations*
GK_ATOM(animationsOfBeforeProperty, "AnimationsOfBeforeProperty") // FrameAnimations*
GK_ATOM(animationsOfAfterProperty, "AnimationsOfAfterProperty") // FrameAnimations*
GK_ATOM(animationEffectsProperty, "AnimationEffectsProperty") // EffectSet*
GK_ATOM(animationEffectsForBeforeProperty, "AnimationsEffectsForBeforeProperty") // EffectSet*
GK_ATOM(animationEffectsForAfterProperty, "AnimationsEffectsForAfterProperty") // EffectSet*
GK_ATOM(transitionsProperty, "TransitionsProperty") // FrameTransitions*
GK_ATOM(transitionsOfBeforeProperty, "TransitionsOfBeforeProperty") // FrameTransitions*
GK_ATOM(transitionsOfAfterProperty, "TransitionsOfAfterProperty") // FrameTransitions*

View File

@ -148,10 +148,12 @@ CropAndCopyDataSourceSurface(DataSourceSurface* aSurface, const IntRect& aCropRe
* Encapsulate the given _aSurface_ into a layers::CairoImage.
*/
static already_AddRefed<layers::Image>
CreateImageFromSurface(SourceSurface* aSurface, ErrorResult& aRv)
CreateImageFromSurface(SourceSurface* aSurface)
{
MOZ_ASSERT(aSurface);
RefPtr<layers::CairoImage> image = new layers::CairoImage(aSurface->GetSize(), aSurface);
RefPtr<layers::CairoImage> image =
new layers::CairoImage(aSurface->GetSize(), aSurface);
return image.forget();
}
@ -166,8 +168,7 @@ CreateSurfaceFromRawData(const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat,
uint8_t* aBuffer,
uint32_t aBufferLength,
const Maybe<IntRect>& aCropRect,
ErrorResult& aRv)
const Maybe<IntRect>& aCropRect)
{
MOZ_ASSERT(!aSize.IsEmpty());
MOZ_ASSERT(aBuffer);
@ -177,7 +178,6 @@ CreateSurfaceFromRawData(const gfx::IntSize& aSize,
Factory::CreateWrappingDataSourceSurface(aBuffer, aStride, aSize, aFormat);
if (NS_WARN_IF(!dataSurface)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
@ -189,7 +189,6 @@ CreateSurfaceFromRawData(const gfx::IntSize& aSize,
RefPtr<DataSourceSurface> result = CropAndCopyDataSourceSurface(dataSurface, cropRect);
if (NS_WARN_IF(!result)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
@ -202,8 +201,7 @@ CreateImageFromRawData(const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat,
uint8_t* aBuffer,
uint32_t aBufferLength,
const Maybe<IntRect>& aCropRect,
ErrorResult& aRv)
const Maybe<IntRect>& aCropRect)
{
MOZ_ASSERT(NS_IsMainThread());
@ -211,9 +209,9 @@ CreateImageFromRawData(const gfx::IntSize& aSize,
RefPtr<SourceSurface> rgbaSurface =
CreateSurfaceFromRawData(aSize, aStride, aFormat,
aBuffer, aBufferLength,
aCropRect, aRv);
aCropRect);
if (NS_WARN_IF(aRv.Failed())) {
if (NS_WARN_IF(!rgbaSurface)) {
return nullptr;
}
@ -241,7 +239,11 @@ CreateImageFromRawData(const gfx::IntSize& aSize,
bgraDataSurface->Unmap();
// Create an Image from the BGRA SourceSurface.
RefPtr<layers::Image> image = CreateImageFromSurface(bgraDataSurface, aRv);
RefPtr<layers::Image> image = CreateImageFromSurface(bgraDataSurface);
if (NS_WARN_IF(!image)) {
return nullptr;
}
return image.forget();
}
@ -266,7 +268,6 @@ public:
gfx::SurfaceFormat aFormat,
const gfx::IntSize& aSize,
const Maybe<IntRect>& aCropRect,
ErrorResult& aError,
layers::Image** aImage)
: WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate())
, mImage(aImage)
@ -276,8 +277,8 @@ public:
, mFormat(aFormat)
, mSize(aSize)
, mCropRect(aCropRect)
, mError(aError)
{
MOZ_ASSERT(!(*aImage), "Don't pass an existing Image into CreateImageFromRawDataInMainThreadSyncTask.");
}
bool MainThreadRun() override
@ -285,10 +286,9 @@ public:
RefPtr<layers::Image> image =
CreateImageFromRawData(mSize, mStride, mFormat,
mBuffer, mBufferLength,
mCropRect,
mError);
mCropRect);
if (NS_WARN_IF(mError.Failed())) {
if (NS_WARN_IF(!image)) {
return false;
}
@ -305,7 +305,6 @@ private:
gfx::SurfaceFormat mFormat;
gfx::IntSize mSize;
const Maybe<IntRect>& mCropRect;
ErrorResult& mError;
};
static bool
@ -519,9 +518,10 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl
}
// Create ImageBitmap.
RefPtr<layers::Image> data = CreateImageFromSurface(surface, aRv);
RefPtr<layers::Image> data = CreateImageFromSurface(surface);
if (NS_WARN_IF(aRv.Failed())) {
if (NS_WARN_IF(!data)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
@ -623,9 +623,10 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvas
}
// Create an Image from the SourceSurface.
RefPtr<layers::Image> data = CreateImageFromSurface(croppedSurface, aRv);
RefPtr<layers::Image> data = CreateImageFromSurface(croppedSurface);
if (NS_WARN_IF(aRv.Failed())) {
if (NS_WARN_IF(!data)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
@ -669,7 +670,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
if (NS_IsMainThread()) {
data = CreateImageFromRawData(imageSize, imageStride, FORMAT,
array.Data(), dataLength,
aCropRect, aRv);
aCropRect);
} else {
RefPtr<CreateImageFromRawDataInMainThreadSyncTask> task
= new CreateImageFromRawDataInMainThreadSyncTask(array.Data(),
@ -678,12 +679,12 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
FORMAT,
imageSize,
aCropRect,
aRv,
getter_AddRefs(data));
task->Dispatch(aRv);
}
if (NS_WARN_IF(aRv.Failed())) {
if (NS_WARN_IF(!data)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
@ -719,9 +720,10 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D&
return nullptr;
}
RefPtr<layers::Image> data = CreateImageFromSurface(surface, aRv);
RefPtr<layers::Image> data = CreateImageFromSurface(surface);
if (NS_WARN_IF(aRv.Failed())) {
if (NS_WARN_IF(!data)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
@ -823,12 +825,13 @@ AsyncFulfillImageBitmapPromise(Promise* aPromise, ImageBitmap* aImageBitmap)
}
static already_AddRefed<SourceSurface>
DecodeBlob(Blob& aBlob, ErrorResult& aRv)
DecodeBlob(Blob& aBlob)
{
// Get the internal stream of the blob.
nsCOMPtr<nsIInputStream> stream;
aBlob.Impl()->GetInternalStream(getter_AddRefs(stream), aRv);
if (NS_WARN_IF(aRv.Failed())) {
ErrorResult error;
aBlob.Impl()->GetInternalStream(getter_AddRefs(stream), error);
if (NS_WARN_IF(error.Failed())) {
return nullptr;
}
@ -840,7 +843,6 @@ DecodeBlob(Blob& aBlob, ErrorResult& aRv)
// Get the Component object.
nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID);
if (NS_WARN_IF(!imgtool)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
@ -848,8 +850,7 @@ DecodeBlob(Blob& aBlob, ErrorResult& aRv)
NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeTypeUTF16); // NS_ConvertUTF16toUTF8 ---|> nsAutoCString
nsCOMPtr<imgIContainer> imgContainer;
nsresult rv = imgtool->DecodeImage(stream, mimeTypeUTF8, getter_AddRefs(imgContainer));
if (NS_FAILED(rv)) {
aRv.Throw(rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
@ -859,7 +860,6 @@ DecodeBlob(Blob& aBlob, ErrorResult& aRv)
RefPtr<SourceSurface> surface = imgContainer->GetFrame(whichFrame, frameFlags);
if (NS_WARN_IF(!surface)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
@ -867,12 +867,12 @@ DecodeBlob(Blob& aBlob, ErrorResult& aRv)
}
static already_AddRefed<layers::Image>
DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect, ErrorResult& aRv)
DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect)
{
// Decode the blob into a SourceSurface.
RefPtr<SourceSurface> surface = DecodeBlob(aBlob, aRv);
RefPtr<SourceSurface> surface = DecodeBlob(aBlob);
if (NS_WARN_IF(aRv.Failed())) {
if (NS_WARN_IF(!surface)) {
return nullptr;
}
@ -898,12 +898,15 @@ DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect, ErrorResult& aRv)
}
if (NS_WARN_IF(!croppedSurface)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
// Create an Image from the source surface.
RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface, aRv);
RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface);
if (NS_WARN_IF(!image)) {
return nullptr;
}
return image.forget();
}
@ -985,11 +988,10 @@ public:
private:
already_AddRefed<ImageBitmap> CreateImageBitmap() override
{
ErrorResult rv;
RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect, rv);
RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect);
if (NS_WARN_IF(rv.Failed())) {
mPromise->MaybeReject(rv);
if (NS_WARN_IF(!data)) {
mPromise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
@ -1009,21 +1011,19 @@ class CreateImageBitmapFromBlobWorkerTask final : public WorkerSameThreadRunnabl
DecodeBlobInMainThreadSyncTask(WorkerPrivate* aWorkerPrivate,
Blob& aBlob,
Maybe<IntRect>& aCropRect,
ErrorResult& aError,
layers::Image** aImage)
: WorkerMainThreadRunnable(aWorkerPrivate)
, mBlob(aBlob)
, mCropRect(aCropRect)
, mError(aError)
, mImage(aImage)
{
}
bool MainThreadRun() override
{
RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect, mError);
RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect);
if (NS_WARN_IF(mError.Failed())) {
if (NS_WARN_IF(!image)) {
return true;
}
@ -1035,7 +1035,6 @@ class CreateImageBitmapFromBlobWorkerTask final : public WorkerSameThreadRunnabl
private:
Blob& mBlob;
Maybe<IntRect>& mCropRect;
ErrorResult& mError;
layers::Image** mImage;
};
@ -1062,7 +1061,7 @@ private:
ErrorResult rv;
RefPtr<DecodeBlobInMainThreadSyncTask> task =
new DecodeBlobInMainThreadSyncTask(mWorkerPrivate, *mBlob, mCropRect,
rv, getter_AddRefs(data));
getter_AddRefs(data));
task->Dispatch(rv); // This is a synchronous call.
if (NS_WARN_IF(rv.Failed())) {
@ -1070,6 +1069,11 @@ private:
mPromise->MaybeReject(rv);
return nullptr;
}
if (NS_WARN_IF(!data)) {
mPromise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
// Create ImageBitmap object.
RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);

View File

@ -1,2 +0,0 @@
// Ensure the contacts service is running in the parent.
Components.utils.import("resource://gre/modules/ContactService.jsm");

View File

@ -1,5 +1,5 @@
[DEFAULT]
support-files = shared.js contacts_chromescript.js
support-files = shared.js
[test_contacts_basics.html]
skip-if = (toolkit == 'gonk' && debug) #debug-only failure

View File

@ -1,11 +1,7 @@
"use strict";
// Fix the environment to run Contacts tests
if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/ContactService.jsm");
} else {
SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('contacts_chromescript.js'));
}
SpecialPowers.importInMainProcess("resource://gre/modules/ContactService.jsm");
// Some helpful global vars
var isAndroid = (navigator.userAgent.indexOf("Android") !== -1);

View File

@ -482,6 +482,30 @@ EventStateManager::TryToFlushPendingNotificationsToIME()
}
}
static bool
IsMessageMouseUserActivity(EventMessage aMessage)
{
return aMessage == eMouseMove ||
aMessage == eMouseUp ||
aMessage == eMouseDown ||
aMessage == eMouseDoubleClick ||
aMessage == eMouseClick ||
aMessage == eMouseActivate ||
aMessage == eMouseLongTap;
}
static bool
IsMessageGamepadUserActivity(EventMessage aMessage)
{
#ifndef MOZ_GAMEPAD
return false;
#else
return aMessage == eGamepadButtonDown ||
aMessage == eGamepadButtonUp ||
aMessage == eGamepadAxisMove;
#endif
}
nsresult
EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
WidgetEvent* aEvent,
@ -510,12 +534,12 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
if (aEvent->mFlags.mIsTrusted &&
((mouseEvent && mouseEvent->IsReal() &&
mouseEvent->mMessage != eMouseEnterIntoWidget &&
mouseEvent->mMessage != eMouseExitFromWidget) ||
IsMessageMouseUserActivity(mouseEvent->mMessage)) ||
aEvent->mClass == eWheelEventClass ||
aEvent->mClass == ePointerEventClass ||
aEvent->mClass == eTouchEventClass ||
aEvent->mClass == eKeyboardEventClass)) {
aEvent->mClass == eKeyboardEventClass ||
IsMessageGamepadUserActivity(aEvent->mMessage))) {
if (gMouseOrKeyboardEventCounter == 0) {
nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService();

View File

@ -333,6 +333,7 @@ PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
if (callback) {
ErrorResult err;
callback->Call(*this, err);
err.SuppressException();
}
} else {
nsIDOMGeoPositionErrorCallback* callback = aCallback.GetXPCOMCallback();
@ -661,6 +662,7 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
MOZ_ASSERT(callback);
callback->Call(*wrapped, err);
err.SuppressException();
} else {
nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();

View File

@ -4764,8 +4764,7 @@ HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
}
// If we are actually playing...
if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
!IsPlaybackEnded()) {
if (IsCurrentlyPlaying()) {
return true;
}
@ -5119,5 +5118,26 @@ HTMLMediaElement::ComputedMuted() const
return (mMuted & MUTED_BY_AUDIO_CHANNEL);
}
bool
HTMLMediaElement::IsCurrentlyPlaying() const
{
// We have playable data, but we still need to check whether data is "real"
// current data.
if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
!IsPlaybackEnded()) {
// Restart the video after ended, it needs to seek to the new position.
// In b2g, the cache is not large enough to store whole video data, so we
// need to download data again. In this case, although the ready state is
// "HAVE_CURRENT_DATA", it is the previous old data. Actually we are not
// yet have enough currently data.
if (mDecoder && mDecoder->IsSeeking() && !mPlayingBeforeSeek) {
return false;
}
return true;
}
return false;
}
} // namespace dom
} // namespace mozilla

View File

@ -1070,6 +1070,9 @@ protected:
// A method to check if we are playing through the AudioChannel.
bool IsPlayingThroughTheAudioChannel() const;
// A method to check whether we are currently playing.
bool IsCurrentlyPlaying() const;
// Update the audio channel playing state
void UpdateAudioChannelPlayingState();

View File

@ -153,16 +153,9 @@ function startTest() {
}
}
// test_ipc.html executes all the tests in this directory in content process.
// It will fail on this one for the moment.
if (!SpecialPowers.isMainProcess()) {
todo(false, "We should make this work on content process");
SimpleTest.finish();
} else {
// TODO: remove unsetting network.disable.ipc.security as part of bug 820712
SpecialPowers.pushPrefEnv({
"set": [
["network.disable.ipc.security", true],
]
}, startTest);
}
// TODO: remove unsetting network.disable.ipc.security as part of bug 820712
SpecialPowers.pushPrefEnv({
"set": [
["network.disable.ipc.security", true],
]
}, startTest);

View File

@ -8,25 +8,6 @@
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript">
function _runTest() {
// Work around Win2k3 debug tinderboxes that may be too slow to complete this test. (Bug 614474)
if (navigator.oscpu == "Windows NT 5.2") {
todo(false, "Test disabled on (too slow debug) Windows 2003 (tinderboxes)");
finishTest();
return;
}
if (!SpecialPowers.isMainProcess()) {
todo(false, "Test disabled in child processes, for now");
finishTest();
return;
}
runTest();
}
</script>
<script type="text/javascript;version=1.7">
var gOrigMaxTotalViewers = undefined;
function setCachePref(enabled) {
@ -79,7 +60,7 @@
</head>
<body onload="_runTest();">
<body onload="runTest();">
<iframe id="iframe"></iframe>
</body>

View File

@ -43,7 +43,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -44,7 +44,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -48,7 +48,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -60,7 +60,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -95,7 +95,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -53,7 +53,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -64,7 +64,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -72,7 +72,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -60,7 +60,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -65,7 +65,6 @@
yield undefined;
}
</script>
<script type="text/javascript;version=1.7" src="file.js"></script> <!-- This is included Just to skip this test in test_ipc.html -->
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>

View File

@ -15,13 +15,6 @@
<a id="a" href="leaving_page_iframe.html"></a>
<script type="text/javascript;version=1.7">
if (!SpecialPowers.isMainProcess()) {
window.runTest = function() {
todo(false, "Figure out this test for child processes!");
finishTest();
}
}
onmessage = function(e) {
ok(false, "gotmessage: " + e.data);
}
@ -37,7 +30,7 @@
yield undefined;
is(iframe.contentWindow.location.href, "about:blank",
"should nagivate to about:blank");
let request = indexedDB.open(location, 1);
request.onsuccess = grabEventAndContinueHandler;
let event = yield undefined;

View File

@ -110,12 +110,6 @@ function testSteps()
function start()
{
if (!SpecialPowers.isMainProcess()) {
todo(false, "Test disabled in child processes, for now");
SimpleTest.finish();
return;
}
SpecialPowers.addPermission("browser", true, document);
SpecialPowers.addPermission("browser", true, { manifestURL: manifestURL });
SpecialPowers.addPermission("embed-apps", true, document);

View File

@ -392,7 +392,7 @@ MediaDecoderStateMachine::CreateMediaSink(bool aAudioCaptured)
RefPtr<media::MediaSink> mediaSink =
new VideoSink(mTaskQueue, audioSink, mVideoQueue,
mDecoder->GetVideoFrameContainer(), mRealTime,
mDecoder->GetFrameStatistics(), AUDIO_DURATION_USECS,
mDecoder->GetFrameStatistics(),
sVideoQueueSendToCompositorSize);
return mediaSink.forget();
}
@ -894,19 +894,6 @@ MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample)
StopPrerollingVideo();
}
// Schedule the state machine to send stream data as soon as possible if
// the VideoQueue() is empty or contains one frame before the Push().
//
// The state machine threads requires a frame in VideoQueue() that is `in
// the future` to gather precise timing information. The head of
// VideoQueue() is always `in the past`.
//
// Schedule the state machine as soon as possible to render the video
// frame or delay the state machine thread accurately.
if (VideoQueue().GetSize() <= 2) {
ScheduleStateMachine();
}
// For non async readers, if the requested video sample was slow to
// arrive, increase the amount of audio we buffer to ensure that we
// don't run out of audio. This is unnecessary for async readers,

View File

@ -46,7 +46,7 @@ public:
MOZ_ASSERT(aItem);
NS_ADDREF(aItem);
nsDeque::Push(aItem);
mPushEvent.Notify();
mPushEvent.Notify(RefPtr<T>(aItem));
}
inline void PushFront(T* aItem) {
@ -54,7 +54,7 @@ public:
MOZ_ASSERT(aItem);
NS_ADDREF(aItem);
nsDeque::PushFront(aItem);
mPushEvent.Notify();
mPushEvent.Notify(RefPtr<T>(aItem));
}
inline already_AddRefed<T> PopFront() {
@ -161,7 +161,7 @@ public:
return mPopEvent;
}
MediaEventSource<void>& PushEvent() {
MediaEventSource<RefPtr<T>>& PushEvent() {
return mPushEvent;
}
@ -172,7 +172,7 @@ public:
private:
mutable ReentrantMonitor mReentrantMonitor;
MediaEventProducer<RefPtr<T>> mPopEvent;
MediaEventProducer<void> mPushEvent;
MediaEventProducer<RefPtr<T>> mPushEvent;
MediaEventProducer<void> mFinishEvent;
// True when we've decoded the last frame of data in the
// bitstream for which we're queueing frame data.

View File

@ -1321,17 +1321,27 @@ PeerConnectionObserver.prototype = {
// STUN requests.
handleIceConnectionStateChange: function(iceConnectionState) {
var histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
if (this._dompc.iceConnectionState === 'new') {
var checking_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_CHECKING_RATE");
if (iceConnectionState === 'checking') {
checking_histogram.add(true);
} else if (iceConnectionState === 'failed') {
checking_histogram.add(false);
}
} else if (this._dompc.iceConnectionState === 'checking') {
var success_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
if (iceConnectionState === 'completed' ||
iceConnectionState === 'connected') {
success_histogram.add(true);
} else if (iceConnectionState === 'failed') {
success_histogram.add(false);
}
}
if (iceConnectionState === 'failed') {
histogram.add(false);
this._dompc.logError("ICE failed, see about:webrtc for more details", null, 0);
}
if (this._dompc.iceConnectionState === 'checking' &&
(iceConnectionState === 'completed' ||
iceConnectionState === 'connected')) {
histogram.add(true);
}
this._dompc.changeIceConnectionState(iceConnectionState);
},

View File

@ -108,8 +108,10 @@ ToMediaKeyStatus(GMPMediaKeyStatus aStatus) {
case kGMPUsable: return MediaKeyStatus::Usable;
case kGMPExpired: return MediaKeyStatus::Expired;
case kGMPOutputDownscaled: return MediaKeyStatus::Output_downscaled;
case kGMPOutputNotAllowed: return MediaKeyStatus::Output_not_allowed;
case kGMPOutputRestricted: return MediaKeyStatus::Output_restricted;
case kGMPInternalError: return MediaKeyStatus::Internal_error;
case kGMPReleased: return MediaKeyStatus::Released;
case kGMPStatusPending: return MediaKeyStatus::Status_pending;
default: return MediaKeyStatus::Internal_error;
}
}

View File

@ -94,10 +94,12 @@ enum GMPMediaKeyStatus {
kGMPUsable = 0,
kGMPExpired = 1,
kGMPOutputDownscaled = 2,
kGMPOutputNotAllowed = 3,
kGMPOutputRestricted = 3,
kGMPInternalError = 4,
kGMPUnknown = 5,
kGMPMediaKeyStatusInvalid = 6 // Must always be last.
kGMPUnknown = 5, // Removes key from MediaKeyStatusMap
kGMPReleased = 6,
kGMPStatusPending = 7,
kGMPMediaKeyStatusInvalid = 8 // Must always be last.
};
// Time in milliseconds, as offset from epoch, 1 Jan 1970.

View File

@ -26,7 +26,6 @@ VideoSink::VideoSink(AbstractThread* aThread,
VideoFrameContainer* aContainer,
bool aRealTime,
FrameStatistics& aFrameStats,
int aDelayDuration,
uint32_t aVQueueSentToCompositerSize)
: mOwnerThread(aThread)
, mAudioSink(aAudioSink)
@ -38,7 +37,6 @@ VideoSink::VideoSink(AbstractThread* aThread,
, mVideoFrameEndTime(-1)
, mHasVideo(false)
, mUpdateScheduler(aThread)
, mDelayDuration(aDelayDuration)
, mVideoQueueSendToCompositorSize(aVQueueSentToCompositerSize)
{
MOZ_ASSERT(mAudioSink, "AudioSink should exist.");
@ -218,12 +216,18 @@ VideoSink::Shutdown()
}
void
VideoSink::OnVideoQueueEvent()
VideoSink::OnVideoQueueEvent(RefPtr<MediaData>&& aSample)
{
AssertOwnerThread();
// Listen to push event, VideoSink should try rendering ASAP if first frame
// arrives but update scheduler is not triggered yet.
TryUpdateRenderedVideoFrames();
VideoData* v = aSample->As<VideoData>();
if (!v->mSentToCompositor) {
// Since we push rendered frames back to the queue, we will receive
// push events for them. We only need to trigger render loop
// when this frame is not rendered yet.
TryUpdateRenderedVideoFrames();
}
}
void
@ -338,7 +342,7 @@ VideoSink::UpdateRenderedVideoFrames()
// the current frame.
NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
int64_t remainingTime = mDelayDuration;
int64_t remainingTime = -1;
if (VideoQueue().GetSize() > 0) {
RefPtr<MediaData> currentFrame = VideoQueue().PopFront();
int32_t framesRemoved = 0;
@ -365,7 +369,14 @@ VideoSink::UpdateRenderedVideoFrames()
RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);
TimeStamp target = nowTime + TimeDuration::FromMicroseconds(remainingTime);
// No next fame to render. There is no need to schedule next render
// loop. We will run render loops again upon incoming frames.
if (remainingTime < 0) {
return;
}
TimeStamp target = nowTime + TimeDuration::FromMicroseconds(
remainingTime / mAudioSink->GetPlaybackParams().mPlaybackRate);
RefPtr<VideoSink> self = this;
mUpdateScheduler.Ensure(target, [self] () {

View File

@ -35,7 +35,6 @@ public:
VideoFrameContainer* aContainer,
bool aRealTime,
FrameStatistics& aFrameStats,
int aDelayDuration,
uint32_t aVQueueSentToCompositerSize);
const PlaybackParams& GetPlaybackParams() const override;
@ -74,7 +73,7 @@ private:
virtual ~VideoSink();
// VideoQueue listener related.
void OnVideoQueueEvent();
void OnVideoQueueEvent(RefPtr<MediaData>&& aSample);
void ConnectListener();
void DisconnectListener();
@ -139,10 +138,6 @@ private:
// Used to trigger another update of rendered frames in next round.
DelayedScheduler mUpdateScheduler;
// A delay duration to trigger next time UpdateRenderedVideoFrames().
// Based on the default value in MDSM.
const int mDelayDuration;
// Max frame number sent to compositor at a time.
// Based on the pref value obtained in MDSM.
const uint32_t mVideoQueueSendToCompositorSize;

View File

@ -661,6 +661,10 @@ AudioBufferSourceNode::Start(double aWhen, double aOffset,
mOffset = aOffset;
mDuration = aDuration.WasPassed() ? aDuration.Value()
: std::numeric_limits<double>::min();
WEB_AUDIO_API_LOG("%f: %s %u Start(%f, %g, %g)", Context()->CurrentTime(),
NodeType(), Id(), aWhen, aOffset, mDuration);
// We can't send these parameters without a buffer because we don't know the
// buffer's sample rate or length.
if (mBuffer) {
@ -742,6 +746,9 @@ AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
return;
}
WEB_AUDIO_API_LOG("%f: %s %u Stop(%f)", Context()->CurrentTime(),
NodeType(), Id(), aWhen);
AudioNodeStream* ns = mStream;
if (!ns || !Context()) {
// We've already stopped and had our stream shut down

View File

@ -198,6 +198,10 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
return &aDestination;
}
WEB_AUDIO_API_LOG("%f: %s %u Connect() to %s %u",
Context()->CurrentTime(), NodeType(), Id(),
aDestination.NodeType(), aDestination.Id());
// The MediaStreamGraph will handle cycle detection. We don't need to do it
// here.
@ -300,6 +304,9 @@ AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
return;
}
WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(),
NodeType(), Id());
// An upstream node may be starting to play on the graph thread, and the
// engine for a downstream node may be sending a PlayingRefChangeHandler
// ADDREF message to this (main) thread. Wait for a round trip before

View File

@ -115,9 +115,48 @@ AudioParam::Stream()
return mStream;
}
static const char*
ToString(AudioTimelineEvent::Type aType)
{
switch (aType) {
case AudioTimelineEvent::SetValue:
return "SetValue";
case AudioTimelineEvent::SetValueAtTime:
return "SetValueAtTime";
case AudioTimelineEvent::LinearRamp:
return "LinearRamp";
case AudioTimelineEvent::ExponentialRamp:
return "ExponentialRamp";
case AudioTimelineEvent::SetTarget:
return "SetTarget";
case AudioTimelineEvent::SetValueCurve:
return "SetValueCurve";
case AudioTimelineEvent::Stream:
return "Stream";
case AudioTimelineEvent::Cancel:
return "Cancel";
default:
return "unknown AudioTimelineEvent";
}
}
void
AudioParam::SendEventToEngine(const AudioTimelineEvent& aEvent)
{
WEB_AUDIO_API_LOG("%f: %s for %u %s %s=%g time=%f %s=%g",
GetParentObject()->CurrentTime(),
mName, ParentNodeId(), ToString(aEvent.mType),
aEvent.mType == AudioTimelineEvent::SetValueCurve ?
"length" : "value",
aEvent.mType == AudioTimelineEvent::SetValueCurve ?
static_cast<double>(aEvent.mCurveLength) :
static_cast<double>(aEvent.mValue),
aEvent.Time<double>(),
aEvent.mType == AudioTimelineEvent::SetValueCurve ?
"duration" : "constant",
aEvent.mType == AudioTimelineEvent::SetValueCurve ?
aEvent.mDuration : aEvent.mTimeConstant);
AudioNodeStream* stream = mNode->GetStream();
if (stream) {
stream->SendTimelineEvent(mIndex, aEvent);

View File

@ -10,6 +10,8 @@
namespace mozilla {
LazyLogModule gWebAudioAPILog("WebAudioAPI");
namespace dom {
void WebAudioUtils::ConvertAudioTimelineEventToTicks(AudioTimelineEvent& aEvent,

View File

@ -20,6 +20,10 @@ namespace mozilla {
class AudioNodeStream;
extern LazyLogModule gWebAudioAPILog;
#define WEB_AUDIO_API_LOG(...) \
MOZ_LOG(gWebAudioAPILog, LogLevel::Debug, (__VA_ARGS__))
namespace dom {
struct AudioTimelineEvent;

View File

@ -52,7 +52,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
DEFFILE = SRCDIR + '/nptest.def'
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' and '64' in CONFIG['OS_TEST']:
EXTRA_DSO_LDOPTS += ['-framework Carbon']
OS_LIBS += ['-framework Carbon']
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
CXXFLAGS += CONFIG['MOZ_GTK2_CFLAGS']

View File

@ -254,8 +254,11 @@ PresentationIPCService::NotifyReceiverReady(const nsAString& aSessionId,
mRespondingSessionIds.Put(aWindowId, new nsAutoString(aSessionId));
mRespondingWindowIds.Put(aSessionId, aWindowId);
mCallback = nullptr;
NS_WARN_IF(!sPresentationChild->SendNotifyReceiverReady(nsAutoString(aSessionId)));
// Release mCallback after using aSessionId
// because aSessionId is held by mCallback.
mCallback = nullptr;
return NS_OK;
}

View File

@ -46,6 +46,11 @@ static bool SchemeIs(nsIURI* aURI, const char* aScheme)
static nsresult
DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
{
// Bug 1228117: determine the correct security policy for DTD loads
if (aLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DTD) {
return NS_OK;
}
nsresult rv = NS_OK;
nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadInfo->LoadingPrincipal();

View File

@ -13,8 +13,10 @@
enum MediaKeyStatus {
"usable",
"expired",
"released",
"output-restricted",
"output-downscaled",
"output-not-allowed",
"status-pending",
"internal-error"
};

View File

@ -506,8 +506,10 @@ Factory::GetMaxSurfaceSize(BackendType aType)
case BackendType::COREGRAPHICS_ACCELERATED:
return DrawTargetCG::GetMaxSurfaceSize();
#endif
#ifdef USE_SKIA
case BackendType::SKIA:
return DrawTargetSkia::GetMaxSurfaceSize();
#endif
#ifdef WIN32
case BackendType::DIRECT2D:
return DrawTargetD2D::GetMaxSurfaceSize();
@ -867,10 +869,10 @@ Factory::CreateWrappingDataSourceSurface(uint8_t *aData, int32_t aStride,
const IntSize &aSize,
SurfaceFormat aFormat)
{
MOZ_ASSERT(aData);
if (aSize.width <= 0 || aSize.height <= 0) {
return nullptr;
}
MOZ_ASSERT(aData);
RefPtr<SourceSurfaceRawData> newSurf = new SourceSurfaceRawData();

View File

@ -310,8 +310,8 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
OS_LIBS += [ 'd3d9', 'dxguid' ]
else:
EXTRA_DSO_LDOPTS += [
'\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'%s/lib/%s/d3d9.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'%s/lib/%s/dxguid.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
]
Library('libANGLE')

View File

@ -66,8 +66,8 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
OS_LIBS += [ 'd3d9', 'dxguid' ]
else:
EXTRA_DSO_LDOPTS += [
'\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'%s/lib/%s/d3d9.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'%s/lib/%s/dxguid.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
]

View File

@ -71,8 +71,8 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
OS_LIBS += [ 'd3d9', 'dxguid' ]
else:
EXTRA_DSO_LDOPTS += [
'\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'%s/lib/%s/d3d9.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'%s/lib/%s/dxguid.lib' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
]
GeckoSharedLibrary('libGLESv2', linkage=None)

View File

@ -299,6 +299,20 @@ public:
return gfx::Matrix4x4();
}
bool TransformIsPerspective() const
{
MOZ_ASSERT(IsValid());
// mLayer->GetTransformIsPerspective() tells us whether
// mLayer->GetTransform() is a perspective transform. Since
// mLayer->GetTransform() is only used at the bottom layer, we only
// need to check GetTransformIsPerspective() at the bottom layer too.
if (AtBottomLayer()) {
return mLayer->GetTransformIsPerspective();
}
return false;
}
EventRegions GetEventRegions() const
{
MOZ_ASSERT(IsValid());

View File

@ -245,6 +245,7 @@ Layer::Layer(LayerManager* aManager, void* aImplData) :
mContentFlags(0),
mUseTileSourceRect(false),
mIsFixedPosition(false),
mTransformIsPerspective(false),
mFixedPositionData(nullptr),
mStickyPositionData(nullptr),
mScrollbarTargetId(FrameMetrics::NULL_SCROLL_ID),
@ -1886,6 +1887,9 @@ Layer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
if (!mTransform.IsIdentity()) {
AppendToString(aStream, mTransform, " [transform=", "]");
}
if (mTransformIsPerspective) {
aStream << " [perspective]";
}
if (!mLayerBounds.IsEmpty()) {
AppendToString(aStream, mLayerBounds, " [bounds=", "]");
}
@ -1906,6 +1910,9 @@ Layer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) {
aStream << " [componentAlpha]";
}
if (GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
aStream << " [backfaceHidden]";
}
if (GetScrollbarDirection() == VERTICAL) {
aStream << nsPrintfCString(" [vscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
}

View File

@ -1113,6 +1113,21 @@ public:
}
}
/**
* CONSTRUCTION PHASE ONLY
* This flag is true when the transform on the layer is a perspective
* transform. The compositor treats perspective transforms specially
* for async scrolling purposes.
*/
void SetTransformIsPerspective(bool aTransformIsPerspective)
{
if (mTransformIsPerspective != aTransformIsPerspective) {
MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) TransformIsPerspective", this));
mTransformIsPerspective = aTransformIsPerspective;
Mutated();
}
}
// Call AddAnimation to add a new animation to this layer from layout code.
// Caller must fill in all the properties of the returned animation.
// A later animation overrides an earlier one.
@ -1266,6 +1281,7 @@ public:
virtual float GetPostXScale() const { return mPostXScale; }
virtual float GetPostYScale() const { return mPostYScale; }
bool GetIsFixedPosition() { return mIsFixedPosition; }
bool GetTransformIsPerspective() const { return mTransformIsPerspective; }
bool GetIsStickyPosition() { return mStickyPositionData; }
FrameMetrics::ViewID GetFixedPositionScrollContainerId() { return mFixedPositionData ? mFixedPositionData->mScrollId : FrameMetrics::NULL_SCROLL_ID; }
LayerPoint GetFixedPositionAnchor() { return mFixedPositionData ? mFixedPositionData->mAnchor : LayerPoint(); }
@ -1796,6 +1812,7 @@ protected:
uint32_t mContentFlags;
bool mUseTileSourceRect;
bool mIsFixedPosition;
bool mTransformIsPerspective;
struct FixedPositionData {
FrameMetrics::ViewID mScrollId;
LayerPoint mAnchor;

View File

@ -592,8 +592,9 @@ APZCTreeManager::UpdateHitTestingTree(TreeBuildingState& aState,
// transform to layer L when we recurse into the children below. If we are at a layer
// with an APZC, such as P, then we reset the ancestorTransform to just PC, to start
// the new accumulation as we go down.
Matrix4x4 transform = aLayer.GetTransform();
Matrix4x4 ancestorTransform = transform;
// If a transform is a perspective transform, it's ignored for this purpose
// (see bug 1168263).
Matrix4x4 ancestorTransform = aLayer.TransformIsPerspective() ? Matrix4x4() : aLayer.GetTransform();
if (!apzc) {
ancestorTransform = ancestorTransform * aAncestorTransform;
}

View File

@ -136,9 +136,6 @@ using mozilla::gfx::PointTyped;
* events we dispatched to it.\n
* Units: milliseconds
*
* \li\b apz.cross_slide_enabled
* Pref that enables integration with the Metro "cross-slide" gesture.
*
* \li\b apz.danger_zone_x
* \li\b apz.danger_zone_y
* When drawing high-res tiles, we drop down to drawing low-res tiles
@ -1228,8 +1225,6 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
case PANNING_LOCKED_Y:
case PANNING_LOCKED_X_SMOOTH_SCROLL:
case PANNING_LOCKED_Y_SMOOTH_SCROLL:
case CROSS_SLIDING_X:
case CROSS_SLIDING_Y:
case PINCHING:
NS_WARNING("Received impossible touch in OnTouchStart");
break;
@ -1252,12 +1247,6 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent)
// second tap. Ignore the move if this happens.
return nsEventStatus_eIgnore;
case CROSS_SLIDING_X:
case CROSS_SLIDING_Y:
// While cross-sliding, we don't want to consume any touchmove events for
// panning or zooming, and let the caller handle them instead.
return nsEventStatus_eIgnore;
case TOUCHING: {
ScreenCoord panThreshold = GetTouchStartTolerance();
UpdateWithTouchAtDevicePoint(aEvent);
@ -1331,8 +1320,6 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
return nsEventStatus_eIgnore;
case TOUCHING:
case CROSS_SLIDING_X:
case CROSS_SLIDING_Y:
// We may have some velocity stored on the axis from move events
// that were not big enough to trigger scrolling. Clear that out.
mX.SetVelocity(0);
@ -2155,26 +2142,19 @@ void AsyncPanZoomController::HandlePanning(double aAngle) {
bool canScrollVertical = !mY.IsAxisLocked() &&
overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL);
if (!gfxPrefs::APZCrossSlideEnabled() &&
(!canScrollHorizontal || !canScrollVertical)) {
if (!canScrollHorizontal || !canScrollVertical) {
SetState(PANNING);
} else if (IsCloseToHorizontal(aAngle, gfxPrefs::APZAxisLockAngle())) {
mY.SetAxisLocked(true);
if (canScrollHorizontal) {
SetState(PANNING_LOCKED_X);
overscrollHandoffChain->RequestSnapOnLock(Layer::VERTICAL);
} else {
SetState(CROSS_SLIDING_X);
mX.SetAxisLocked(true);
}
} else if (IsCloseToVertical(aAngle, gfxPrefs::APZAxisLockAngle())) {
mX.SetAxisLocked(true);
if (canScrollVertical) {
SetState(PANNING_LOCKED_Y);
overscrollHandoffChain->RequestSnapOnLock(Layer::HORIZONTAL);
} else {
SetState(CROSS_SLIDING_Y);
mY.SetAxisLocked(true);
}
} else {
SetState(PANNING);
@ -2204,12 +2184,12 @@ void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aPanDistance
float breakThreshold = gfxPrefs::APZAxisBreakoutThreshold() * APZCTreeManager::GetDPI();
if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) {
if (mState == PANNING_LOCKED_X || mState == CROSS_SLIDING_X) {
if (mState == PANNING_LOCKED_X) {
if (!IsCloseToHorizontal(angle, gfxPrefs::APZAxisBreakoutAngle())) {
mY.SetAxisLocked(false);
SetState(PANNING);
}
} else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
} else if (mState == PANNING_LOCKED_Y) {
if (!IsCloseToVertical(angle, gfxPrefs::APZAxisLockAngle())) {
mX.SetAxisLocked(false);
SetState(PANNING);

View File

@ -754,11 +754,6 @@ protected:
PAN_MOMENTUM, /* like PANNING, but controlled by momentum PanGestureInput events */
CROSS_SLIDING_X, /* Panning disabled while user does a horizontal gesture
on a vertically-scrollable view. This used for the
Windows Metro "cross-slide" gesture. */
CROSS_SLIDING_Y, /* as above for Y axis */
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
ANIMATING_ZOOM, /* animated zoom to a new rect */
OVERSCROLL_ANIMATION, /* Spring-based animation used to relieve overscroll once
@ -1056,7 +1051,8 @@ public:
private:
/* This is the cumulative CSS transform for all the layers from (and including)
* the parent APZC down to (but excluding) this one. */
* the parent APZC down to (but excluding) this one, and excluding any
* perspective transforms. */
Matrix4x4 mAncestorTransform;

View File

@ -504,7 +504,7 @@ AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
static void
SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
StyleAnimationValue& aEnd, Animatable* aValue)
StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
{
StyleAnimationValue interpolatedValue;
NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
@ -525,21 +525,21 @@ SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
nsPoint origin = data.origin();
// we expect all our transform data to arrive in device pixels
Point3D transformOrigin = data.transformOrigin();
Point3D perspectiveOrigin = data.perspectiveOrigin();
nsDisplayTransform::FrameTransformProperties props(interpolatedList,
transformOrigin,
perspectiveOrigin,
data.perspective());
transformOrigin);
// If our parent layer is a perspective layer, then the offset into reference
// frame coordinates is already on that layer. If not, then we need to ask
// for it to be added here.
uint32_t flags = 0;
if (!aLayer->GetParent() || !aLayer->GetParent()->GetTransformIsPerspective()) {
flags = nsDisplayTransform::OFFSET_BY_ORIGIN;
}
Matrix4x4 transform =
nsDisplayTransform::GetResultingTransformMatrix(props, origin,
data.appUnitsPerDevPixel(),
&data.bounds());
Point3D scaledOrigin =
Point3D(NS_round(NSAppUnitsToFloatPixels(origin.x, data.appUnitsPerDevPixel())),
NS_round(NSAppUnitsToFloatPixels(origin.y, data.appUnitsPerDevPixel())),
0.0f);
transform.PreTranslate(scaledOrigin);
flags, &data.bounds());
InfallibleTArray<TransformFunction> functions;
functions.AppendElement(TransformMatrix(transform));
@ -617,7 +617,7 @@ SampleAnimations(Layer* aLayer, TimeStamp aPoint)
// interpolate the property
Animatable interpolatedValue;
SampleValue(portion, animation, animData.mStartValues[segmentIndex],
animData.mEndValues[segmentIndex], &interpolatedValue);
animData.mEndValues[segmentIndex], &interpolatedValue, aLayer);
LayerComposite* layerComposite = aLayer->AsLayerComposite();
switch (animation.property()) {
case eCSSProperty_opacity:
@ -771,15 +771,32 @@ MoveScrollbarForLayerMargin(Layer* aRoot, FrameMetrics::ViewID aRootScrollId,
}
#endif
template <typename Units>
Maybe<IntRectTyped<Units>>
IntersectMaybeRects(const Maybe<IntRectTyped<Units>>& a,
const Maybe<IntRectTyped<Units>>& b)
{
if (!a) {
return b;
} else if (!b) {
return a;
} else {
return Some(a->Intersect(*b));
}
}
bool
AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
bool* aOutFoundRoot)
bool* aOutFoundRoot,
Maybe<ParentLayerIntRect>& aClipDeferredToParent)
{
Maybe<ParentLayerIntRect> clipDeferredFromChildren;
bool appliedTransform = false;
for (Layer* child = aLayer->GetFirstChild();
child; child = child->GetNextSibling()) {
appliedTransform |=
ApplyAsyncContentTransformToTree(child, aOutFoundRoot);
ApplyAsyncContentTransformToTree(child, aOutFoundRoot,
clipDeferredFromChildren);
}
Matrix4x4 oldTransform = aLayer->GetTransform();
@ -886,10 +903,15 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
// move with this APZC.
if (metrics.HasClipRect()) {
ParentLayerIntRect clip = metrics.ClipRect();
if (asyncClip) {
asyncClip = Some(clip.Intersect(*asyncClip));
if (aLayer->GetParent() && aLayer->GetParent()->GetTransformIsPerspective()) {
// If our parent layer has a perspective transform, we want to apply
// our scroll clip to it instead of to this layer (see bug 1168263).
// A layer with a perspective transform shouldn't have multiple
// children with FrameMetrics, nor a child with multiple FrameMetrics.
MOZ_ASSERT(!aClipDeferredToParent);
aClipDeferredToParent = Some(clip);
} else {
asyncClip = Some(clip);
asyncClip = IntersectMaybeRects(Some(clip), asyncClip);
}
}
@ -926,11 +948,12 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
transformWithoutOverscrollOrOmta, fixedLayerMargins);
}
if (hasAsyncTransform) {
if (asyncClip) {
aLayer->AsLayerComposite()->SetShadowClipRect(asyncClip);
}
if (hasAsyncTransform || clipDeferredFromChildren) {
aLayer->AsLayerComposite()->SetShadowClipRect(
IntersectMaybeRects(asyncClip, clipDeferredFromChildren));
}
if (hasAsyncTransform) {
// Apply the APZ transform on top of GetLocalTransform() here (rather than
// GetTransform()) in case the OMTA code in SampleAnimations already set a
// shadow transform; in that case we want to apply ours on top of that one
@ -1348,7 +1371,8 @@ AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
// in Gecko and partially in Java.
wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), aCurrentFrame);
bool foundRoot = false;
if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
Maybe<ParentLayerIntRect> clipDeferredFromChildren;
if (ApplyAsyncContentTransformToTree(root, &foundRoot, clipDeferredFromChildren)) {
#if defined(MOZ_ANDROID_APZ)
MOZ_ASSERT(foundRoot);
if (foundRoot && mFixedLayerMargins != ScreenMargin()) {

View File

@ -132,8 +132,11 @@ private:
// applied for |aLayer|. |*aOutFoundRoot| is set to true on Android only, if
// one of the metrics on one of the layers was determined to be the "root"
// and its state was synced to the Java front-end. |aOutFoundRoot| must be
// non-null.
bool ApplyAsyncContentTransformToTree(Layer* aLayer, bool* aOutFoundRoot);
// non-null. As the function recurses over the layer tree, a layer may
// populate |aClipDeferredToParent| a clip rect it wants to set on its parent.
bool ApplyAsyncContentTransformToTree(Layer* aLayer,
bool* aOutFoundRoot,
Maybe<ParentLayerIntRect>& aClipDeferredToParent);
/**
* Update the shadow transform for aLayer assuming that is a scrollbar,
* so that it stays in sync with the content that is being scrolled by APZ.

View File

@ -46,6 +46,9 @@ CompositorChild::CompositorChild(ClientLayerManager *aLayerManager)
CompositorChild::~CompositorChild()
{
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
new DeleteTask<Transport>(GetTransport()));
if (mCanSend) {
gfxCriticalError() << "CompositorChild was not deinitialized";
}

View File

@ -273,6 +273,9 @@ ImageBridgeChild::~ImageBridgeChild()
{
MOZ_ASSERT(NS_IsMainThread());
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
new DeleteTask<Transport>(GetTransport()));
delete mTxn;
}

View File

@ -328,6 +328,7 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
layer->SetOpacity(common.opacity());
layer->SetClipRect(common.useClipRect() ? Some(common.clipRect()) : Nothing());
layer->SetBaseTransform(common.transform().value());
layer->SetTransformIsPerspective(common.transformIsPerspective());
layer->SetPostScale(common.postXScale(), common.postYScale());
layer->SetIsFixedPosition(common.isFixedPosition());
if (common.isFixedPosition()) {
@ -743,14 +744,16 @@ LayerTransactionParent::RecvGetAnimationTransform(PLayerParent* aParent,
}
}
// Undo the translation to the origin of the reference frame applied by
// AsyncCompositionManager::SampleValue
transform.PreTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
// If our parent isn't a perspective layer, then the offset into reference
// frame coordinates will have been applied to us. Add an inverse translation
// to cancel it out.
if (!layer->GetParent() || !layer->GetParent()->GetTransformIsPerspective()) {
transform.PostTranslate(-scaledOrigin.x, -scaledOrigin.y, -scaledOrigin.z);
}
// Undo the rebasing applied by
// nsDisplayTransform::GetResultingTransformMatrixInternal
Point3D basis = -scaledOrigin - transformOrigin;
transform.ChangeBasis(basis.x, basis.y, basis.z);
transform.ChangeBasis(-transformOrigin);
// Convert to CSS pixels (this undoes the operations performed by
// nsStyleTransformMatrix::ProcessTranslatePart which is called from

View File

@ -164,10 +164,7 @@ struct TransformData {
nsPoint origin;
// the transform-origin property for the transform in device pixels
Point3D transformOrigin;
// the perspective-origin property for the transform in device pixels
Point3D perspectiveOrigin;
nsRect bounds;
nscoord perspective;
int32_t appUnitsPerDevPixel;
};
@ -208,6 +205,7 @@ struct CommonLayerAttributes {
nsIntRegion visibleRegion;
EventRegions eventRegions;
TransformMatrix transform;
bool transformIsPerspective;
float postXScale;
float postYScale;
uint32_t contentFlags;

View File

@ -589,6 +589,7 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies,
common.postXScale() = mutant->GetPostXScale();
common.postYScale() = mutant->GetPostYScale();
common.transform() = mutant->GetBaseTransform();
common.transformIsPerspective() = mutant->GetTransformIsPerspective();
common.contentFlags() = mutant->GetContentFlags();
common.opacity() = mutant->GetOpacity();
common.useClipRect() = !!mutant->GetClipRect();

View File

@ -48,7 +48,7 @@ struct SanityChecker {
{
MaybeYieldThread();
CriticalSectionAutoEnter lock(&mSection);
ASSERT_EQ(mAdvancements[aJobId], aCmdId-1);
MOZ_RELEASE_ASSERT(mAdvancements[aJobId] == aCmdId-1);
mAdvancements[aJobId] = aCmdId;
}
};
@ -66,12 +66,12 @@ struct JoinTestSanityCheck : public SanityChecker {
{
// Job 0 is the special task executed when everything is joined after task 1
if (aCmdId == 0) {
ASSERT_FALSE(mSpecialJobHasRun);
MOZ_RELEASE_ASSERT(!mSpecialJobHasRun);
mSpecialJobHasRun = true;
for (auto advancement : mAdvancements) {
// Because of the synchronization point (beforeFilter), all
// task buffers should have run task 1 when task 0 is run.
ASSERT_EQ(advancement, (uint32_t)1);
MOZ_RELEASE_ASSERT(advancement == 1);
}
} else {
// This check does not apply to task 0.
@ -79,7 +79,7 @@ struct JoinTestSanityCheck : public SanityChecker {
}
if (aCmdId == 2) {
ASSERT_TRUE(mSpecialJobHasRun);
MOZ_RELEASE_ASSERT(mSpecialJobHasRun);
}
}
};
@ -156,7 +156,7 @@ void TestSchedulerJoin(uint32_t aNumThreads, uint32_t aNumCmdBuffers)
MaybeYieldThread();
for (auto advancement : check.mAdvancements) {
ASSERT_TRUE(advancement == 2);
EXPECT_TRUE(advancement == 2);
}
}
@ -214,7 +214,7 @@ void TestSchedulerChain(uint32_t aNumThreads, uint32_t aNumCmdBuffers)
waitForCompletion->Wait();
for (auto advancement : check.mAdvancements) {
ASSERT_TRUE(advancement == numJobs);
EXPECT_TRUE(advancement == numJobs);
}
}

View File

@ -145,7 +145,6 @@ private:
DECL_GFX_PREF(Live, "apz.axis_lock.lock_angle", APZAxisLockAngle, float, float(M_PI / 6.0) /* 30 degrees */);
DECL_GFX_PREF(Live, "apz.axis_lock.mode", APZAxisLockMode, int32_t, 0);
DECL_GFX_PREF(Live, "apz.content_response_timeout", APZContentResponseTimeout, int32_t, 300);
DECL_GFX_PREF(Live, "apz.cross_slide.enabled", APZCrossSlideEnabled, bool, false);
DECL_GFX_PREF(Live, "apz.danger_zone_x", APZDangerZoneX, int32_t, 50);
DECL_GFX_PREF(Live, "apz.danger_zone_y", APZDangerZoneY, int32_t, 100);
DECL_GFX_PREF(Live, "apz.drag.enabled", APZDragEnabled, bool, false);

View File

@ -106,11 +106,15 @@ Downscaler::BeginFrame(const nsIntSize& aOriginalSize,
// Allocate the buffer, which contains scanlines of the original image.
// pad by 15 to handle overreads by the simd code
mRowBuffer.reset(new (fallible) uint8_t[mOriginalSize.width * sizeof(uint32_t) + 15]);
size_t bufferLen = mOriginalSize.width * sizeof(uint32_t) + 15;
mRowBuffer.reset(new (fallible) uint8_t[bufferLen]);
if (MOZ_UNLIKELY(!mRowBuffer)) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Zero buffer to keep valgrind happy.
memset(mRowBuffer.get(), 0, bufferLen);
// Allocate the window, which contains horizontally downscaled scanlines. (We
// can store scanlines which are already downscale because our downscaling
// filter is separable.)

View File

@ -75,6 +75,8 @@ typedef struct gif_struct {
// Parameters for image frame currently being decoded
unsigned x_offset, y_offset; // With respect to "screen" origin
unsigned height, width;
unsigned clamped_height; // Size of the frame rectangle clamped to the
unsigned clamped_width; // global logical size after x_ and y_offset.
int tpixel; // Index of transparent pixel
int32_t disposal_method; // Restore to background, leave in place, etc.
uint32_t* local_colormap; // Per-image colormap

View File

@ -181,11 +181,11 @@ nsGIFDecoder2::FlushImageData()
case 1: // one pass on - need to handle bottom & top rects
FlushImageData(0, mCurrentRow + 1);
FlushImageData(mLastFlushedRow + 1,
mGIFStruct.height - (mLastFlushedRow + 1));
mGIFStruct.clamped_height - (mLastFlushedRow + 1));
break;
default: // more than one pass on - push the whole frame
FlushImageData(0, mGIFStruct.height);
FlushImageData(0, mGIFStruct.clamped_height);
}
}
@ -338,11 +338,11 @@ nsGIFDecoder2::EndImageFrame()
mCurrentPass = mLastFlushedPass = 0;
// Only add frame if we have any rows at all
if (mGIFStruct.rows_remaining != mGIFStruct.height) {
if (mGIFStruct.rows_remaining != mGIFStruct.clamped_height) {
if (mGIFStruct.rows_remaining && mGIFStruct.images_decoded) {
// Clear the remaining rows (only needed for the animation frames)
uint8_t* rowp =
mImageData + ((mGIFStruct.height - mGIFStruct.rows_remaining) *
mImageData + ((mGIFStruct.clamped_height - mGIFStruct.rows_remaining) *
mGIFStruct.width);
memset(rowp, 0, mGIFStruct.rows_remaining * mGIFStruct.width);
}
@ -381,7 +381,7 @@ nsGIFDecoder2::OutputRow()
int drow_end = mGIFStruct.irow;
// Protect against too much image data
if ((unsigned)drow_start >= mGIFStruct.height) {
if ((unsigned)drow_start >= mGIFStruct.clamped_height) {
NS_WARNING("GIF2.cpp::OutputRow - too much image data");
return 0;
}
@ -401,16 +401,16 @@ nsGIFDecoder2::OutputRow()
drow_end = drow_start + row_dup;
// Extend if bottom edge isn't covered because of the shift upward.
if (((mGIFStruct.height - 1) - drow_end) <= row_shift) {
drow_end = mGIFStruct.height - 1;
if (((mGIFStruct.clamped_height - 1) - drow_end) <= row_shift) {
drow_end = mGIFStruct.clamped_height - 1;
}
// Clamp first and last rows to upper and lower edge of image.
if (drow_start < 0) {
drow_start = 0;
}
if ((unsigned)drow_end >= mGIFStruct.height) {
drow_end = mGIFStruct.height - 1;
if ((unsigned)drow_end >= mGIFStruct.clamped_height) {
drow_end = mGIFStruct.clamped_height - 1;
}
}
@ -418,17 +418,17 @@ nsGIFDecoder2::OutputRow()
uint8_t* rowp = GetCurrentRowBuffer();
// Convert color indices to Cairo pixels
uint8_t* from = rowp + mGIFStruct.width;
uint32_t* to = ((uint32_t*)rowp) + mGIFStruct.width;
uint8_t* from = rowp + mGIFStruct.clamped_width;
uint32_t* to = ((uint32_t*)rowp) + mGIFStruct.clamped_width;
uint32_t* cmap = mColormap;
for (uint32_t c = mGIFStruct.width; c > 0; c--) {
for (uint32_t c = mGIFStruct.clamped_width; c > 0; c--) {
*--to = cmap[*--from];
}
// check for alpha (only for first frame)
if (mGIFStruct.is_transparent && !mSawTransparency) {
const uint32_t* rgb = (uint32_t*)rowp;
for (uint32_t i = mGIFStruct.width; i > 0; i--) {
for (uint32_t i = mGIFStruct.clamped_width; i > 0; i--) {
if (*rgb++ == 0) {
mSawTransparency = true;
break;
@ -449,7 +449,7 @@ nsGIFDecoder2::OutputRow()
// vertically, which is incompatible with the filter that we use for
// downscale-during-decode, so we can't do this if we're downscaling.
MOZ_ASSERT_IF(mDownscaler, mDeinterlacer);
const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.width;
const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.clamped_width;
for (int r = drow_start; r <= drow_end; r++) {
// Skip the row we wrote to above; that's what we're copying *from*.
if (r != int(mGIFStruct.irow)) {
@ -475,12 +475,12 @@ nsGIFDecoder2::OutputRow()
do {
// Row increments resp. per 8,8,4,2 rows
mGIFStruct.irow += kjump[mGIFStruct.ipass];
if (mGIFStruct.irow >= mGIFStruct.height) {
if (mGIFStruct.irow >= mGIFStruct.clamped_height) {
// Next pass starts resp. at row 4,2,1,0
mGIFStruct.irow = 8 >> mGIFStruct.ipass;
mGIFStruct.ipass++;
}
} while (mGIFStruct.irow >= mGIFStruct.height);
} while (mGIFStruct.irow >= mGIFStruct.clamped_height);
// We've finished a pass. If we're downscaling, it's time to propagate the
// rows we've decoded so far from our Deinterlacer to our Downscaler.
@ -525,14 +525,14 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
uint8_t* stack = mGIFStruct.stack;
uint8_t* rowp = mGIFStruct.rowp;
uint8_t* rowend = GetCurrentRowBuffer() + mGIFStruct.width;
uint8_t* rowend = GetCurrentRowBuffer() + mGIFStruct.clamped_width;
#define OUTPUT_ROW() \
PR_BEGIN_MACRO \
if (!OutputRow()) \
goto END; \
rowp = GetCurrentRowBuffer(); \
rowend = rowp + mGIFStruct.width; \
rowend = rowp + mGIFStruct.clamped_width; \
PR_END_MACRO
for (const uint8_t* ch = q; count-- > 0; ch++) {
@ -625,6 +625,10 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
*rowp++ = *--stackp & mColorMask; // ensure index is within colormap
if (rowp == rowend) {
OUTPUT_ROW();
// Consume decoded data that falls past the end of the clamped width.
stackp -= mGIFStruct.width - mGIFStruct.clamped_width;
stackp = std::max(stackp, stack);
}
} while (stackp > stack);
}
@ -1108,6 +1112,21 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
}
}
// Hack around GIFs with frame rects outside the given screen bounds.
IntRect clampedRect =
ClampToImageRect(IntRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
mGIFStruct.width, mGIFStruct.height));
if (clampedRect.IsEmpty()) {
// XXX Bug 1227546 - Maybe we should treat this as valid?
mGIFStruct.state = gif_error;
break;
}
mGIFStruct.clamped_width = clampedRect.width;
mGIFStruct.clamped_height = clampedRect.height;
MOZ_ASSERT(mGIFStruct.clamped_width <= mGIFStruct.width);
MOZ_ASSERT(mGIFStruct.clamped_height <= mGIFStruct.height);
// Depth of colors is determined by colormap
// (q[8] & 0x80) indicates local colormap
// bits per pixel is (q[8]&0x07 + 1) when local colormap is set
@ -1170,7 +1189,7 @@ nsGIFDecoder2::WriteInternal(const char* aBuffer, uint32_t aCount)
// Clear state from last image
mGIFStruct.irow = 0;
mGIFStruct.rows_remaining = mGIFStruct.height;
mGIFStruct.rows_remaining = mGIFStruct.clamped_height;
mGIFStruct.rowp = GetCurrentRowBuffer();
// Depth of colors is determined by colormap

View File

@ -111,7 +111,7 @@ if CONFIG['_MSC_VER']:
# a console application.
WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
LDFLAGS += [CONFIG['MOZ_ALLOW_HEAP_EXECUTE_FLAGS']]
LDFLAGS += CONFIG['MOZ_ALLOW_HEAP_EXECUTE_FLAGS']
# Control the default heap size.
# This is the heap returned by GetProcessHeap().

View File

@ -104,25 +104,13 @@ ObjectToIdMap::init()
void
ObjectToIdMap::trace(JSTracer* trc)
{
for (Table::Enum e(table_); !e.empty(); e.popFront()) {
JSObject* obj = e.front().key();
JS_CallUnbarrieredObjectTracer(trc, &obj, "ipc-object");
if (obj != e.front().key())
e.rekeyFront(obj);
}
table_.trace(trc);
}
void
ObjectToIdMap::sweep()
{
for (Table::Enum e(table_); !e.empty(); e.popFront()) {
JSObject* obj = e.front().key();
JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
if (!obj)
e.removeFront();
else if (obj != e.front().key())
e.rekeyFront(obj);
}
table_.sweep();
}
ObjectId
@ -137,23 +125,7 @@ ObjectToIdMap::find(JSObject* obj)
bool
ObjectToIdMap::add(JSContext* cx, JSObject* obj, ObjectId id)
{
if (!table_.put(obj, id))
return false;
JS_StoreObjectPostBarrierCallback(cx, keyMarkCallback, obj, &table_);
return true;
}
/*
* This function is called during minor GCs for each key in the HashMap that has
* been moved.
*/
/* static */ void
ObjectToIdMap::keyMarkCallback(JSTracer* trc, JSObject* key, void* data)
{
Table* table = static_cast<Table*>(data);
JSObject* prior = key;
JS_CallUnbarrieredObjectTracer(trc, &key, "ObjectIdCache::table_ key");
table->rekeyIfMoved(prior, key);
return table_.put(obj, id);
}
void

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/jsipc/PJavaScript.h"
#include "js/GCHashTable.h"
#include "nsJSUtils.h"
namespace mozilla {
@ -51,6 +52,10 @@ class ObjectId {
return ObjectId(data >> FLAG_BITS, data & 1);
}
// For use with StructGCPolicy.
void trace(JSTracer*) const {}
bool needsSweep() const { return false; }
private:
ObjectId() : serialNumber_(0), hasXrayWaiver_(false) {}
@ -103,8 +108,8 @@ class IdToObjectMap
// Map JSObjects -> ids
class ObjectToIdMap
{
typedef js::PointerHasher<JSObject*, 3> Hasher;
typedef js::HashMap<JSObject*, ObjectId, Hasher, js::SystemAllocPolicy> Table;
using Hasher = js::MovableCellHasher<JS::Heap<JSObject*>>;
using Table = js::GCHashMap<JS::Heap<JSObject*>, ObjectId, Hasher, js::SystemAllocPolicy>;
public:
explicit ObjectToIdMap(JSRuntime* rt);
@ -120,8 +125,6 @@ class ObjectToIdMap
void clear();
private:
static void keyMarkCallback(JSTracer* trc, JSObject* key, void* data);
JSRuntime* rt_;
Table table_;
};

View File

@ -65,10 +65,7 @@ class GCHashMap : public HashMap<Key, Value, HashPolicy, AllocPolicy>,
return;
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
GCPolicy::ValuePolicy::trace(trc, &e.front().value(), "hashmap value");
Key key = e.front().key();
GCPolicy::KeyPolicy::trace(trc, &key, "hashmap key");
if (key != e.front().key())
e.rekeyFront(key);
GCPolicy::KeyPolicy::trace(trc, &e.front().mutableKey(), "hashmap key");
}
}
@ -210,12 +207,8 @@ class GCHashSet : public HashSet<T, HashPolicy, AllocPolicy>,
void trace(JSTracer* trc) {
if (!this->initialized())
return;
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
T elem = e.front();
GCPolicy::trace(trc, &elem, "hashset element");
if (elem != e.front())
e.rekeyFront(elem);
}
for (typename Base::Enum e(*this); !e.empty(); e.popFront())
GCPolicy::trace(trc, &e.mutableFront(), "hashset element");
}
void sweep() {

View File

@ -17,7 +17,6 @@
*/
#include "asmjs/AsmJSCompile.h"
#include "asmjs/AsmJSGlobals.h"
#include "jit/CodeGenerator.h"
@ -27,101 +26,6 @@ using namespace js::wasm;
using mozilla::DebugOnly;
using mozilla::Maybe;
namespace js {
// ModuleCompiler encapsulates the compilation of an entire asm.js module. Over
// the course of an ModuleCompiler object's lifetime, many FunctionCompiler
// objects will be created and destroyed in sequence, one for each function in
// the module.
//
// *** asm.js FFI calls ***
//
// asm.js allows calling out to non-asm.js via "FFI calls". The asm.js type
// system does not place any constraints on the FFI call. In particular:
// - an FFI call's target is not known or speculated at module-compile time;
// - a single external function can be called with different signatures.
//
// If performance didn't matter, all FFI calls could simply box their arguments
// and call js::Invoke. However, we'd like to be able to specialize FFI calls
// to be more efficient in several cases:
//
// - for calls to JS functions which have been jitted, we'd like to call
// directly into JIT code without going through C++.
//
// - for calls to certain builtins, we'd like to be call directly into the C++
// code for the builtin without going through the general call path.
//
// All of this requires dynamic specialization techniques which must happen
// after module compilation. To support this, at module-compilation time, each
// FFI call generates a call signature according to the system ABI, as if the
// callee was a C++ function taking/returning the same types as the caller was
// passing/expecting. The callee is loaded from a fixed offset in the global
// data array which allows the callee to change at runtime. Initially, the
// callee is stub which boxes its arguments and calls js::Invoke.
//
// To do this, we need to generate a callee stub for each pairing of FFI callee
// and signature. We call this pairing an "exit". For example, this code has
// two external functions and three exits:
//
// function f(global, imports) {
// "use asm";
// var foo = imports.foo;
// var bar = imports.bar;
// function g() {
// foo(1); // Exit #1: (int) -> void
// foo(1.5); // Exit #2: (double) -> void
// bar(1)|0; // Exit #3: (int) -> int
// bar(2)|0; // Exit #3: (int) -> int
// }
// }
//
// The ModuleCompiler maintains a hash table (ExitMap) which allows a call site
// to add a new exit or reuse an existing one. The key is an index into the
// Vector<Exit> stored in the AsmJSModule and the value is the signature of
// that exit's variant.
//
// Although ModuleCompiler isn't a MOZ_STACK_CLASS, it has the same rooting
// properties as the ModuleValidator, and a shorter lifetime: so it is marked
// as rooted in the in the rooting analysis. Don't add non-JSATom pointers, or
// this will break!
class ModuleCompiler
{
ModuleCompileInputs compileInputs_;
ScopedJSDeletePtr<ModuleCompileResults> compileResults_;
public:
explicit ModuleCompiler(const ModuleCompileInputs& inputs)
: compileInputs_(inputs)
{}
bool init() {
compileResults_.reset(js_new<ModuleCompileResults>());
return !!compileResults_;
}
/*************************************************** Read-only interface */
MacroAssembler& masm() { return compileResults_->masm(); }
Label& stackOverflowLabel() { return compileResults_->stackOverflowLabel(); }
Label& asyncInterruptLabel() { return compileResults_->asyncInterruptLabel(); }
Label& syncInterruptLabel() { return compileResults_->syncInterruptLabel(); }
Label& onOutOfBoundsLabel() { return compileResults_->onOutOfBoundsLabel(); }
Label& onConversionErrorLabel() { return compileResults_->onConversionErrorLabel(); }
int64_t usecBefore() { return compileResults_->usecBefore(); }
bool usesSignalHandlersForOOB() const { return compileInputs_.usesSignalHandlersForOOB; }
CompileRuntime* runtime() const { return compileInputs_.runtime; }
CompileCompartment* compartment() const { return compileInputs_.compartment; }
/***************************************************** Mutable interface */
void finish(ScopedJSDeletePtr<ModuleCompileResults>* results) {
*results = compileResults_.forget();
}
};
} // namespace js
enum class AsmType : uint8_t {
Int32,
Float32,
@ -135,7 +39,7 @@ typedef Vector<MBasicBlock*, 8, SystemAllocPolicy> BlockVector;
// Encapsulates the compilation of a single function in an asm.js module. The
// function compiler handles the creation and final backend compilation of the
// MIR graph. Also see ModuleCompiler comment.
// MIR graph.
class FunctionCompiler
{
private:
@ -144,15 +48,13 @@ class FunctionCompiler
typedef Vector<size_t, 4, SystemAllocPolicy> PositionStack;
typedef Vector<Type, 4, SystemAllocPolicy> LocalVarTypes;
ModuleCompiler & m_;
LifoAlloc & lifo_;
ModuleCompileInputs inputs_;
const AsmFunction & func_;
size_t pc_;
TempAllocator * alloc_;
MIRGraph * graph_;
CompileInfo * info_;
TempAllocator & alloc_;
MIRGraph & graph_;
const CompileInfo & info_;
MIRGenerator * mirGen_;
Maybe<JitContext> jitContext_;
@ -167,30 +69,76 @@ class FunctionCompiler
LocalVarTypes localVarTypes_;
FunctionCompileResults& compileResults_;
public:
FunctionCompiler(ModuleCompiler& m, const AsmFunction& func, LifoAlloc& lifo)
: m_(m),
lifo_(lifo),
FunctionCompiler(ModuleCompileInputs inputs, const AsmFunction& func, const CompileInfo& info,
TempAllocator* alloc, MIRGraph* graph, MIRGenerator* mirGen,
FunctionCompileResults& compileResults)
: inputs_(inputs),
func_(func),
pc_(0),
alloc_(nullptr),
graph_(nullptr),
info_(nullptr),
mirGen_(nullptr),
curBlock_(nullptr)
alloc_(*alloc),
graph_(*graph),
info_(info),
mirGen_(mirGen),
curBlock_(nullptr),
compileResults_(compileResults)
{}
ModuleCompiler & m() const { return m_; }
TempAllocator & alloc() const { return *alloc_; }
LifoAlloc & lifo() const { return lifo_; }
TempAllocator & alloc() const { return alloc_; }
MacroAssembler & masm() const { return compileResults_.masm(); }
RetType returnedType() const { return func_.returnedType(); }
bool init()
bool init(const VarTypeVector& argTypes)
{
return unlabeledBreaks_.init() &&
unlabeledContinues_.init() &&
labeledBreaks_.init() &&
labeledContinues_.init();
if (!unlabeledBreaks_.init() ||
!unlabeledContinues_.init() ||
!labeledBreaks_.init() ||
!labeledContinues_.init())
{
return false;
}
const AsmFunction::VarInitializerVector& varInitializers = func_.varInitializers();
// Prepare data structures
jitContext_.emplace(inputs_.runtime, /* CompileCompartment = */ nullptr, &alloc_);
MOZ_ASSERT(func_.numLocals() == argTypes.length() + varInitializers.length());
if (!newBlock(/* pred = */ nullptr, &curBlock_))
return false;
// Emit parameters and local variables
for (ABIArgTypeIter i(argTypes); !i.done(); i++) {
MAsmJSParameter* ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
curBlock_->add(ins);
curBlock_->initSlot(info().localSlot(i.index()), ins);
if (!mirGen_->ensureBallast())
return false;
localVarTypes_.append(argTypes[i.index()].toType());
}
unsigned firstLocalSlot = argTypes.length();
for (unsigned i = 0; i < varInitializers.length(); i++) {
const AsmJSNumLit& lit = varInitializers[i];
Type type = Type::Of(lit);
MIRType mirType = type.toMIRType();
MInstruction* ins;
if (lit.isSimd())
ins = MSimdConstant::New(alloc(), lit.simdValue(), mirType);
else
ins = MConstant::NewAsmJS(alloc(), lit.scalarValue(), mirType);
curBlock_->add(ins);
curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins);
if (!mirGen_->ensureBallast())
return false;
localVarTypes_.append(type);
}
return true;
}
void checkPostconditions()
@ -206,9 +154,9 @@ class FunctionCompiler
/************************* Read-only interface (after local scope setup) */
MIRGenerator & mirGen() const { MOZ_ASSERT(mirGen_); return *mirGen_; }
MIRGraph & mirGraph() const { MOZ_ASSERT(graph_); return *graph_; }
CompileInfo & info() const { MOZ_ASSERT(info_); return *info_; }
MIRGenerator & mirGen() const { MOZ_ASSERT(mirGen_); return *mirGen_; }
MIRGraph & mirGraph() const { return graph_; }
const CompileInfo & info() const { return info_; }
MDefinition* getLocalDef(unsigned slot)
{
@ -614,7 +562,7 @@ class FunctionCompiler
return;
CallSiteDesc callDesc(lineno, column, CallSiteDesc::Relative);
curBlock_->add(MAsmJSInterruptCheck::New(alloc(), &m().syncInterruptLabel(), callDesc));
curBlock_->add(MAsmJSInterruptCheck::New(alloc(), masm().asmSyncInterruptLabel(), callDesc));
}
MDefinition* extractSimdElement(SimdLane lane, MDefinition* base, MIRType type)
@ -1223,79 +1171,6 @@ class FunctionCompiler
bool done() const { return pc_ == func_.size(); }
size_t pc() const { return pc_; }
bool prepareEmitMIR(const VarTypeVector& argTypes)
{
const AsmFunction::VarInitializerVector& varInitializers = func_.varInitializers();
size_t numLocals = func_.numLocals();
// Prepare data structures
alloc_ = lifo_.new_<TempAllocator>(&lifo_);
if (!alloc_)
return false;
jitContext_.emplace(m().runtime(), /* CompileCompartment = */ nullptr, alloc_);
graph_ = lifo_.new_<MIRGraph>(alloc_);
if (!graph_)
return false;
MOZ_ASSERT(numLocals == argTypes.length() + varInitializers.length());
info_ = lifo_.new_<CompileInfo>(numLocals);
if (!info_)
return false;
const OptimizationInfo* optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS);
const JitCompileOptions options;
mirGen_ = lifo_.new_<MIRGenerator>(m().compartment(),
options, alloc_,
graph_, info_, optimizationInfo,
&m().onOutOfBoundsLabel(),
&m().onConversionErrorLabel(),
m().usesSignalHandlersForOOB());
if (!mirGen_)
return false;
if (!newBlock(/* pred = */ nullptr, &curBlock_))
return false;
// Emit parameters and local variables
for (ABIArgTypeIter i(argTypes); !i.done(); i++) {
MAsmJSParameter* ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
curBlock_->add(ins);
curBlock_->initSlot(info().localSlot(i.index()), ins);
if (!mirGen_->ensureBallast())
return false;
localVarTypes_.append(argTypes[i.index()].toType());
}
unsigned firstLocalSlot = argTypes.length();
for (unsigned i = 0; i < varInitializers.length(); i++) {
const AsmJSNumLit& lit = varInitializers[i];
Type type = Type::Of(lit);
MIRType mirType = type.toMIRType();
MInstruction* ins;
if (lit.isSimd())
ins = MSimdConstant::New(alloc(), lit.simdValue(), mirType);
else
ins = MConstant::NewAsmJS(alloc(), lit.scalarValue(), mirType);
curBlock_->add(ins);
curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins);
if (!mirGen_->ensureBallast())
return false;
localVarTypes_.append(type);
}
return true;
}
/*************************************************************************/
MIRGenerator* extractMIR()
{
MOZ_ASSERT(mirGen_ != nullptr);
MIRGenerator* mirGen = mirGen_;
mirGen_ = nullptr;
return mirGen;
}
/*************************************************************************/
private:
bool newBlockWithDepth(MBasicBlock* pred, unsigned loopDepth, MBasicBlock** block)
@ -3088,94 +2963,72 @@ EmitF32X4Expr(FunctionCompiler& f, MDefinition** def)
}
bool
js::GenerateAsmFunctionMIR(ModuleCompiler& m, LifoAlloc& lifo, AsmFunction& func, MIRGenerator** mir)
js::CompileAsmFunction(LifoAlloc& lifo, ModuleCompileInputs inputs, const AsmFunction& func,
FunctionCompileResults* results)
{
int64_t before = PRMJ_Now();
FunctionCompiler f(m, func, lifo);
if (!f.init())
return false;
TempAllocator tempAlloc(&lifo);
MIRGraph graph(&tempAlloc);
CompileInfo compileInfo(func.numLocals());
const OptimizationInfo* optimizationInfo = js_IonOptimizations.get(Optimization_AsmJS);
const JitCompileOptions options;
MIRGenerator mir(inputs.compartment, options, &tempAlloc, &graph, &compileInfo,
optimizationInfo, results->masm().asmOnOutOfBoundsLabel(),
results->masm().asmOnConversionErrorLabel(), inputs.usesSignalHandlersForOOB);
if (!f.prepareEmitMIR(func.argTypes()))
return false;
while (!f.done()) {
if (!EmitStatement(f))
// Keep the FunctionCompiler in a separate scope so that its memory gets
// freed when it's done.
{
FunctionCompiler f(inputs, func, compileInfo, &tempAlloc, &graph, &mir, *results);
if (!f.init(func.argTypes()))
return false;
while (!f.done()) {
if (!EmitStatement(f))
return false;
}
f.checkPostconditions();
}
*mir = f.extractMIR();
if (!*mir)
return false;
{
JitContext jitContext(inputs.runtime, /* CompileCompartment = */ nullptr, &mir.alloc());
jit::SpewBeginFunction(*mir, nullptr);
jit::SpewBeginFunction(&mir, nullptr);
jit::AutoSpewEndFunction spewEndFunction(&mir);
f.checkPostconditions();
if (!OptimizeMIR(&mir))
return false;
func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
return true;
}
LIRGraph* lir = GenerateLIR(&mir);
if (!lir)
return false;
bool
js::GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, MIRGenerator& mir, LIRGraph& lir,
FunctionCompileResults* results)
{
JitContext jitContext(m.runtime(), /* CompileCompartment = */ nullptr, &mir.alloc());
// Although each function gets to have its own MacroAssembler for
// compilation, parallel compilations may recycle an already existing
// MacroAssembler.
//
// To avoid spiking memory, a LifoAllocScope in the caller
// frees all MIR/LIR after each function is compiled. This method is
// responsible for cleaning out any dangling pointers that the
// MacroAssembler may have kept.
results->masm().resetForNewCodeGenerator(mir.alloc());
int64_t before = PRMJ_Now();
Label entry;
AsmJSFunctionLabels labels(entry, *results->masm().asmStackOverflowLabel());
CodeGenerator codegen(&mir, lir, &results->masm());
if (!codegen.generateAsmJS(&labels))
return false;
// A single MacroAssembler is reused for all function compilations so
// that there is a single linear code segment for each module. To avoid
// spiking memory, a LifoAllocScope in the caller frees all MIR/LIR
// after each function is compiled. This method is responsible for cleaning
// out any dangling pointers that the MacroAssembler may have kept.
m.masm().resetForNewCodeGenerator(mir.alloc());
PropertyName* funcName = func.name();
unsigned line = func.lineno();
ScopedJSDeletePtr<CodeGenerator> codegen(js_new<CodeGenerator>(&mir, &lir, &m.masm()));
if (!codegen)
return false;
Label entry;
AsmJSFunctionLabels labels(entry, m.stackOverflowLabel());
if (!codegen->generateAsmJS(&labels))
return false;
func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
PropertyName* funcName = func.name();
unsigned line = func.lineno();
// Fill in the results of the function's compilation
AsmJSModule::FunctionCodeRange codeRange(funcName, line, labels);
results->finishCodegen(func, codeRange, *codegen->extractScriptCounts());
// Unlike regular IonMonkey, which links and generates a new JitCode for
// every function, we accumulate all the functions in the module in a
// single MacroAssembler and link at end. Linking asm.js doesn't require a
// CodeGenerator so we can destroy it now (via ScopedJSDeletePtr).
return true;
}
bool
js::CreateAsmModuleCompiler(ModuleCompileInputs mci, AsmModuleCompilerScope* scope)
{
auto* mc = js_new<ModuleCompiler>(mci);
if (!mc || !mc->init())
return false;
scope->setModule(mc);
return true;
}
AsmModuleCompilerScope::~AsmModuleCompilerScope()
{
if (m_) {
js_delete(m_);
m_ = nullptr;
// Fill in the results of the function's compilation
AsmJSModule::FunctionCodeRange codeRange(funcName, line, labels);
results->finishCodegen(codeRange, *codegen.extractScriptCounts());
}
}
void
js::FinishAsmModuleCompilation(ModuleCompiler& m, ScopedJSDeletePtr<ModuleCompileResults>* results)
{
m.finish(results);
results->setCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
return true;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2015 Mozilla Foundation
*
* 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.
*/
#ifndef jit_AsmJSCompileInputs_h
#define jit_AsmJSCompileInputs_h
namespace js {
namespace jit {
class CompileCompartment;
class CompileRuntime;
}
struct ModuleCompileInputs
{
jit::CompileCompartment* compartment;
jit::CompileRuntime* runtime;
bool usesSignalHandlersForOOB;
ModuleCompileInputs(jit::CompileCompartment* compartment,
jit::CompileRuntime* runtime,
bool usesSignalHandlersForOOB)
: compartment(compartment),
runtime(runtime),
usesSignalHandlersForOOB(usesSignalHandlersForOOB)
{}
};
} // namespace js
#endif //jit_AsmJSCompileInputs_h

File diff suppressed because it is too large Load Diff

View File

@ -341,22 +341,12 @@ AsmJSModule::finish(ExclusiveContext* cx, TokenStream& tokenStream, MacroAssembl
// CodeLabels are used for switch cases and loads from floating-point /
// SIMD values in the constant pool.
for (size_t i = 0; i < masm.numCodeLabels(); i++) {
CodeLabel src = masm.codeLabel(i);
int32_t labelOffset = src.dest()->offset();
int32_t targetOffset = src.src()->offset();
// The patched uses of a label embed a linked list where the
// to-be-patched immediate is the offset of the next to-be-patched
// instruction.
while (labelOffset != LabelBase::INVALID_OFFSET) {
size_t patchAtOffset = masm.labelOffsetToPatchOffset(labelOffset);
RelativeLink link(RelativeLink::CodeLabel);
link.patchAtOffset = patchAtOffset;
link.targetOffset = targetOffset;
if (!staticLinkData_.relativeLinks.append(link))
return false;
labelOffset = Assembler::ExtractCodeLabelOffset(code_ + patchAtOffset);
}
CodeLabel cl = masm.codeLabel(i);
RelativeLink link(RelativeLink::CodeLabel);
link.patchAtOffset = masm.labelToPatchOffset(*cl.patchAt());
link.targetOffset = cl.target()->offset();
if (!staticLinkData_.relativeLinks.append(link))
return false;
}
#if defined(JS_CODEGEN_X86)
@ -366,7 +356,7 @@ AsmJSModule::finish(ExclusiveContext* cx, TokenStream& tokenStream, MacroAssembl
for (size_t i = 0; i < masm.numAsmJSGlobalAccesses(); i++) {
AsmJSGlobalAccess a = masm.asmJSGlobalAccess(i);
RelativeLink link(RelativeLink::InstructionImmediate);
link.patchAtOffset = masm.labelOffsetToPatchOffset(a.patchAt.offset());
link.patchAtOffset = masm.labelToPatchOffset(a.patchAt);
link.targetOffset = offsetOfGlobalData() + a.globalDataOffset;
if (!staticLinkData_.relativeLinks.append(link))
return false;

View File

@ -623,6 +623,12 @@ class AsmJSModule
MOZ_ASSERT(isFunction());
return lineNumber_;
}
void functionOffsetBy(uint32_t offset) {
MOZ_ASSERT(isFunction());
begin_ += offset;
profilingReturn_ += offset;
end_ += offset;
}
AsmJSExit::BuiltinKind thunkTarget() const {
MOZ_ASSERT(isThunk());
return AsmJSExit::BuiltinKind(u.thunk.target_);

View File

@ -30,7 +30,6 @@
#include "jsutil.h"
#include "asmjs/AsmJSCompile.h"
#include "asmjs/AsmJSGlobals.h"
#include "asmjs/AsmJSLink.h"
#include "asmjs/AsmJSSignalHandlers.h"
#include "builtin/SIMD.h"
@ -618,13 +617,12 @@ class MOZ_STACK_CLASS ModuleValidator
uint32_t funcIndex_;
uint32_t srcBegin_;
uint32_t srcEnd_;
uint32_t compileTime_;
bool defined_;
public:
Func(PropertyName* name, uint32_t firstUseOffset, LifoSignature* sig, uint32_t funcIndex)
: sig_(sig), name_(name), firstUseOffset_(firstUseOffset), funcIndex_(funcIndex),
srcBegin_(0), srcEnd_(0), compileTime_(0), defined_(false)
srcBegin_(0), srcEnd_(0), defined_(false)
{}
PropertyName* name() const { return name_; }
@ -643,8 +641,6 @@ class MOZ_STACK_CLASS ModuleValidator
uint32_t srcEnd() const { MOZ_ASSERT(defined_); return srcEnd_; }
LifoSignature& sig() { return *sig_; }
const LifoSignature& sig() const { return *sig_; }
uint32_t compileTime() const { return compileTime_; }
void accumulateCompileTime(uint32_t ms) { compileTime_ += ms; }
};
typedef Vector<const Func*, 0, LifoAllocPolicy<Fallible>> ConstFuncVector;
@ -944,10 +940,15 @@ class MOZ_STACK_CLASS ModuleValidator
bool supportsSimd_;
bool atomicsPresent_;
MacroAssembler masm_;
int64_t usecBefore_;
Vector<uint32_t> functionEntryOffsets_;
SlowFunctionVector slowFunctions_;
ScopedJSDeletePtr<ModuleCompileResults> compileResults_;
// Labels not accessible in functions compilations
NonAssertingLabel asyncInterruptLabel_;
NonAssertingLabel onDetachedLabel_;
DebugOnly<bool> finishedFunctionBodies_;
public:
@ -972,9 +973,10 @@ class MOZ_STACK_CLASS ModuleValidator
hasChangeHeap_(false),
supportsSimd_(cx->jitSupportsSimd()),
atomicsPresent_(false),
masm_(MacroAssembler::AsmJSToken()),
usecBefore_(0),
functionEntryOffsets_(cx),
slowFunctions_(cx),
compileResults_(nullptr),
finishedFunctionBodies_(false)
{
MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
@ -1405,6 +1407,7 @@ class MOZ_STACK_CLASS ModuleValidator
TokenStream& tokenStream() const { return parser_.tokenStream; }
bool supportsSimd() const { return supportsSimd_; }
LifoAlloc& lifo() { return moduleLifo_; }
int64_t usecBefore() const { return usecBefore_; }
unsigned numArrayViews() const {
return arrayViews_.length();
@ -1466,24 +1469,31 @@ class MOZ_STACK_CLASS ModuleValidator
}
// End-of-compilation utils
MacroAssembler& masm() { return compileResults_->masm(); }
Label& stackOverflowLabel() { return compileResults_->stackOverflowLabel(); }
Label& asyncInterruptLabel() { return compileResults_->asyncInterruptLabel(); }
Label& syncInterruptLabel() { return compileResults_->syncInterruptLabel(); }
Label& onDetachedLabel() { return compileResults_->onDetachedLabel(); }
Label& onOutOfBoundsLabel() { return compileResults_->onOutOfBoundsLabel(); }
Label& onConversionErrorLabel() { return compileResults_->onConversionErrorLabel(); }
ExitMap::Range allExits() const { return exits_.all(); }
MacroAssembler& masm() { return masm_; }
Label& stackOverflowLabel() { return *masm_.asmStackOverflowLabel(); }
Label& syncInterruptLabel() { return *masm_.asmSyncInterruptLabel(); }
Label& onConversionErrorLabel() { return *masm_.asmOnConversionErrorLabel(); }
Label& onOutOfBoundsLabel() { return *masm_.asmOnOutOfBoundsLabel(); }
Label& asyncInterruptLabel() { return asyncInterruptLabel_; }
Label& onDetachedLabel() { return onDetachedLabel_; }
ExitMap::Range allExits() const { return exits_.all(); }
bool finishGeneratingFunction(FunctionCompileResults& results) {
const AsmFunction& func = results.func();
public:
bool finishGeneratingFunction(const AsmFunction& func, const FunctionCompileResults& results) {
unsigned i = func.funcIndex();
if (functionEntryOffsets_.length() <= i && !functionEntryOffsets_.resize(i + 1))
return false;
AsmJSModule::FunctionCodeRange codeRange = results.codeRange();
size_t delta = masm().size();
codeRange.functionOffsetBy(delta);
functionEntryOffsets_[i] = codeRange.entry();
DebugOnly<size_t> sizeNewMasm = results.masm().size();
if (!masm().asmMergeWith(results.masm()))
return false;
MOZ_ASSERT(delta + sizeNewMasm == masm().size());
PropertyName* funcName = func.name();
unsigned line = func.lineno();
unsigned column = func.column();
@ -1494,9 +1504,9 @@ class MOZ_STACK_CLASS ModuleValidator
if (!module().addFunctionCodeRange(codeRange.name(), codeRange))
return false;
unsigned compileTime = func.compileTime();
if (compileTime >= 250) {
if (!slowFunctions_.append(SlowFunction(funcName, compileTime, line, column)))
unsigned totalTime = func.parseTime() + results.compileTime();
if (totalTime >= 250) {
if (!slowFunctions_.append(SlowFunction(funcName, totalTime, line, column)))
return false;
}
@ -1586,11 +1596,9 @@ class MOZ_STACK_CLASS ModuleValidator
module_->setViewsAreShared();
}
module_->startFunctionBodies();
usecBefore_ = PRMJ_Now();
}
bool finishFunctionBodies(ScopedJSDeletePtr<ModuleCompileResults>* compileResults) {
// Take ownership of compilation results
compileResults_ = compileResults->forget();
bool finishFunctionBodies() {
// Patch internal calls to their final positions
for (auto& cs : masm().callSites()) {
if (!cs.isInternal())
@ -1611,7 +1619,7 @@ class MOZ_STACK_CLASS ModuleValidator
#ifndef JS_MORE_DETERMINISTIC
ScopedJSFreePtr<char> slowFuns;
int64_t usecAfter = PRMJ_Now();
int msTotal = (usecAfter - compileResults_->usecBefore()) / PRMJ_USEC_PER_MSEC;
int msTotal = (usecAfter - usecBefore()) / PRMJ_USEC_PER_MSEC;
if (!slowFunctions_.empty()) {
slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length()));
if (!slowFuns)
@ -6499,12 +6507,11 @@ CheckFunction(ModuleValidator& m, LifoAlloc& lifo, AsmFunction** funcOut)
func->define(fn);
func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
unsigned lineno, column;
int64_t parseTime = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
m.tokenStream().srcCoords.lineNumAndColumnIndex(func->srcBegin(), &lineno, &column);
if (!asmFunc->finish(func->sig().args(), func->name(), func->funcIndex(),
func->srcBegin(), lineno, column, func->compileTime()))
func->srcBegin(), lineno, column, parseTime))
{
return false;
}
@ -6530,18 +6537,13 @@ CheckAllFunctionsDefined(ModuleValidator& m)
}
static bool
CheckFunctionsSequential(ModuleValidator& m, ScopedJSDeletePtr<ModuleCompileResults>* compileResults)
CheckFunctionsSequential(ModuleValidator& m)
{
// Use a single LifoAlloc to allocate all the temporary compiler IR.
// All allocated LifoAlloc'd memory is released after compiling each
// function by the LifoAllocScope inside the loop.
LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
AsmModuleCompilerScope scope;
if (!CreateAsmModuleCompiler(m.compileInputs(), &scope))
return false;
ModuleCompiler& mc = scope.module();
while (true) {
TokenKind tk;
if (!PeekToken(m.parser(), &tk))
@ -6559,39 +6561,15 @@ CheckFunctionsSequential(ModuleValidator& m, ScopedJSDeletePtr<ModuleCompileResu
if (!func)
continue;
MIRGenerator* mir;
if (!GenerateAsmFunctionMIR(mc, lifo, *func, &mir))
return false;
int64_t before = PRMJ_Now();
LIRGraph* lir;
{
JitContext jcx(m.cx(), &mir->alloc());
jit::AutoSpewEndFunction spewEndFunction(mir);
if (!OptimizeMIR(mir))
return m.failOffset(func->srcBegin(), "internal compiler failure (probably out of memory)");
lir = GenerateLIR(mir);
if (!lir)
return m.failOffset(func->srcBegin(), "internal compiler failure (probably out of memory)");
func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
}
FunctionCompileResults results;
if (!GenerateAsmFunctionCode(mc, *func, *mir, *lir, &results))
return false;
if (!m.finishGeneratingFunction(results))
if (!CompileAsmFunction(lifo, m.compileInputs(), *func, &results))
return m.failOffset(func->srcBegin(), "internal compiler failure (probably out of memory)");
if (!m.finishGeneratingFunction(*func, results))
return false;
}
if (!CheckAllFunctionsDefined(m))
return false;
FinishAsmModuleCompilation(mc, compileResults);
return true;
return CheckAllFunctionsDefined(m);
}
// Currently, only one asm.js parallel compilation is allowed at a time.
@ -6648,7 +6626,7 @@ struct ParallelGroupState
// Block until a helper-assigned LifoAlloc becomes finished.
static AsmJSParallelTask*
GetFinishedCompilation(ModuleCompiler& m, ParallelGroupState& group)
GetFinishedCompilation(ParallelGroupState& group)
{
AutoLockHelperThreadState lock;
@ -6664,28 +6642,20 @@ GetFinishedCompilation(ModuleCompiler& m, ParallelGroupState& group)
}
static bool
GetUsedTask(ModuleValidator& m, ModuleCompiler& mc, ParallelGroupState& group, AsmJSParallelTask** outTask)
GetUsedTask(ModuleValidator& m, ParallelGroupState& group, AsmJSParallelTask** outTask)
{
// Block until a used LifoAlloc becomes available.
AsmJSParallelTask* task = GetFinishedCompilation(mc, group);
AsmJSParallelTask* task = GetFinishedCompilation(group);
if (!task)
return false;
auto& func = *reinterpret_cast<AsmFunction*>(task->func);
func.accumulateCompileTime(task->compileTime);
// Perform code generation on the main thread.
FunctionCompileResults results;
if (!GenerateAsmFunctionCode(mc, func, *task->mir, *task->lir, &results))
return false;
if (!m.finishGeneratingFunction(results))
if (!m.finishGeneratingFunction(*task->func, *task->results))
return false;
group.compiledJobs++;
// Clear the LifoAlloc for use by another helper.
TempAllocator& tempAlloc = task->mir->alloc();
tempAlloc.TempAllocator::~TempAllocator();
task->results.reset();
task->lifo.releaseAll();
*outTask = task;
@ -6705,8 +6675,7 @@ GetUnusedTask(ParallelGroupState& group, uint32_t i, AsmJSParallelTask** outTask
}
static bool
CheckFunctionsParallel(ModuleValidator& m, ParallelGroupState& group,
ScopedJSDeletePtr<ModuleCompileResults>* compileResults)
CheckFunctionsParallel(ModuleValidator& m, ParallelGroupState& group)
{
#ifdef DEBUG
{
@ -6717,11 +6686,6 @@ CheckFunctionsParallel(ModuleValidator& m, ParallelGroupState& group,
#endif
HelperThreadState().resetAsmJSFailureState();
AsmModuleCompilerScope scope;
if (!CreateAsmModuleCompiler(m.compileInputs(), &scope))
return false;
ModuleCompiler& mc = scope.module();
AsmJSParallelTask* task = nullptr;
for (unsigned i = 0;; i++) {
TokenKind tk;
@ -6730,7 +6694,7 @@ CheckFunctionsParallel(ModuleValidator& m, ParallelGroupState& group,
if (tk != TOK_FUNCTION)
break;
if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(m, mc, group, &task))
if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(m, group, &task))
return false;
AsmFunction* func;
@ -6741,13 +6705,8 @@ CheckFunctionsParallel(ModuleValidator& m, ParallelGroupState& group,
if (!func)
continue;
// Generate MIR into the LifoAlloc on the main thread.
MIRGenerator* mir;
if (!GenerateAsmFunctionMIR(mc, task->lifo, *func, &mir))
return false;
// Perform optimizations and LIR generation on a helper thread.
task->init(m.cx()->compartment()->runtimeFromAnyThread(), func, mir);
task->init(m.cx()->compartment()->runtimeFromAnyThread(), func);
if (!StartOffThreadAsmJSCompile(m.cx(), task))
return false;
@ -6758,7 +6717,7 @@ CheckFunctionsParallel(ModuleValidator& m, ParallelGroupState& group,
// Block for all outstanding helpers to complete.
while (group.outstandingJobs > 0) {
AsmJSParallelTask* ignored = nullptr;
if (!GetUsedTask(m, mc, group, &ignored))
if (!GetUsedTask(m, group, &ignored))
return false;
}
@ -6775,8 +6734,6 @@ CheckFunctionsParallel(ModuleValidator& m, ParallelGroupState& group,
}
#endif
MOZ_ASSERT(!HelperThreadState().asmJSFailed());
FinishAsmModuleCompilation(mc, compileResults);
return true;
}
@ -6824,7 +6781,7 @@ CancelOutstandingJobs(ParallelGroupState& group)
static const size_t LIFO_ALLOC_PARALLEL_CHUNK_SIZE = 1 << 12;
static bool
CheckFunctions(ModuleValidator& m, ScopedJSDeletePtr<ModuleCompileResults>* results)
CheckFunctions(ModuleValidator& m)
{
// If parallel compilation isn't enabled (not enough cores, disabled by
// pref, etc) or another thread is currently compiling asm.js in parallel,
@ -6833,7 +6790,7 @@ CheckFunctions(ModuleValidator& m, ScopedJSDeletePtr<ModuleCompileResults>* resu
// concurrent asm.js parallel compilations don't race.)
ParallelCompilationGuard g;
if (!ParallelCompilationEnabled(m.cx()) || !g.claim())
return CheckFunctionsSequential(m, results);
return CheckFunctionsSequential(m);
JitSpew(JitSpew_IonSyncLogs, "Can't log asm.js script. (Compiled on background thread.)");
@ -6847,21 +6804,19 @@ CheckFunctions(ModuleValidator& m, ScopedJSDeletePtr<ModuleCompileResults>* resu
return false;
for (size_t i = 0; i < numParallelJobs; i++)
tasks.infallibleAppend(LIFO_ALLOC_PARALLEL_CHUNK_SIZE);
tasks.infallibleEmplaceBack(LIFO_ALLOC_PARALLEL_CHUNK_SIZE, m.compileInputs());
// With compilation memory in-scope, dispatch helper threads.
ParallelGroupState group(tasks);
if (!CheckFunctionsParallel(m, group, results)) {
if (!CheckFunctionsParallel(m, group)) {
CancelOutstandingJobs(group);
// If a validation error didn't occur on the main thread, either a
// syntax error occurred and will be signalled by the regular parser,
// or an error occurred on an helper thread.
if (!m.hasAlreadyFailed()) {
if (void* maybeFunc = HelperThreadState().maybeAsmJSFailedFunction()) {
AsmFunction* func = reinterpret_cast<AsmFunction*>(maybeFunc);
if (AsmFunction* func = HelperThreadState().maybeAsmJSFailedFunction())
return m.failOffset(func->srcBegin(), "allocation failure during compilation");
}
}
// Otherwise, the error occurred on the main thread and was already reported.
@ -8207,11 +8162,10 @@ CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList,
"shared views not supported by this build");
#endif
ScopedJSDeletePtr<ModuleCompileResults> mcd;
if (!CheckFunctions(m, &mcd))
if (!CheckFunctions(m))
return false;
if (!m.finishFunctionBodies(&mcd))
if (!m.finishFunctionBodies())
return false;
if (!CheckFuncPtrTables(m))

View File

@ -219,6 +219,11 @@ IndirectBindingMap::Binding::Binding(ModuleEnvironmentObject* environment, Shape
: environment(environment), shape(shape)
{}
IndirectBindingMap::IndirectBindingMap(Zone* zone)
: map_(ZoneAllocPolicy(zone))
{
}
bool
IndirectBindingMap::init()
{
@ -583,7 +588,8 @@ ModuleObject::create(ExclusiveContext* cx, HandleObject enclosingStaticScope)
self->initReservedSlot(StaticScopeSlot, ObjectOrNullValue(enclosingStaticScope));
IndirectBindingMap* bindings = cx->new_<IndirectBindingMap>();
Zone* zone = cx->zone();
IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>(zone);
if (!bindings || !bindings->init()) {
ReportOutOfMemory(cx);
return nullptr;
@ -591,9 +597,11 @@ ModuleObject::create(ExclusiveContext* cx, HandleObject enclosingStaticScope)
self->initReservedSlot(ImportBindingsSlot, PrivateValue(bindings));
FunctionDeclarationVector* funDecls = cx->new_<FunctionDeclarationVector>(cx);
if (!funDecls)
FunctionDeclarationVector* funDecls = zone->new_<FunctionDeclarationVector>(zone);
if (!funDecls) {
ReportOutOfMemory(cx);
return nullptr;
}
self->initReservedSlot(FunctionDeclarationsSlot, PrivateValue(funDecls));
return self;
@ -826,7 +834,8 @@ ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleArra
if (!ns)
return nullptr;
IndirectBindingMap* bindings = cx->new_<IndirectBindingMap>();
Zone* zone = cx->zone();
IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>(zone);
if (!bindings || !bindings->init()) {
ReportOutOfMemory(cx);
return nullptr;

View File

@ -10,6 +10,8 @@
#include "jsapi.h"
#include "jsatom.h"
#include "gc/Zone.h"
#include "js/TraceableVector.h"
#include "vm/NativeObject.h"
@ -84,6 +86,7 @@ typedef Handle<ExportEntryObject*> HandleExportEntryObject;
class IndirectBindingMap
{
public:
explicit IndirectBindingMap(Zone* zone);
bool init();
void trace(JSTracer* trc);
@ -115,7 +118,7 @@ class IndirectBindingMap
RelocatablePtrShape shape;
};
typedef HashMap<jsid, Binding, JsidHasher, SystemAllocPolicy> Map;
typedef HashMap<jsid, Binding, JsidHasher, ZoneAllocPolicy> Map;
Map map_;
};
@ -189,7 +192,7 @@ struct FunctionDeclaration
RelocatablePtrFunction fun;
};
using FunctionDeclarationVector = TraceableVector<FunctionDeclaration>;
using FunctionDeclarationVector = TraceableVector<FunctionDeclaration, 0, ZoneAllocPolicy>;
class ModuleObject : public NativeObject
{

View File

@ -1004,6 +1004,10 @@ static const JSFunctionSpec object_static_methods[] = {
JS_FN("setPrototypeOf", obj_setPrototypeOf, 2, 0),
JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2, 0),
JS_FN("keys", obj_keys, 1, 0),
#ifndef RELEASE_BUILD
JS_SELF_HOSTED_FN("values", "ObjectValues", 1, JSPROP_DEFINE_LATE),
JS_SELF_HOSTED_FN("entries", "ObjectEntries", 1, JSPROP_DEFINE_LATE),
#endif
JS_FN("is", obj_is, 2, 0),
JS_FN("defineProperty", obj_defineProperty, 3, 0),
JS_FN("defineProperties", obj_defineProperties, 2, 0),

View File

@ -22,7 +22,7 @@ function ObjectStaticAssign(target, firstSource) {
var from = ToObject(nextSource);
// Steps 5.b.iii-iv.
var keys = OwnPropertyKeys(from);
var keys = OwnPropertyKeys(from, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
// Step 5.c.
for (var nextIndex = 0, len = keys.length; nextIndex < len; nextIndex++) {
@ -134,3 +134,48 @@ function ObjectLookupGetter(name) {
} while (object !== null);
}
// Draft proposal http://tc39.github.io/proposal-object-values-entries/#Object.values
function ObjectValues(O) {
// Steps 1-2.
var object = ToObject(O);
// Steps 3-4.
// EnumerableOwnProperties is inlined here.
var keys = OwnPropertyKeys(object, JSITER_OWNONLY | JSITER_HIDDEN);
var values = [];
var valuesCount = 0;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!callFunction(std_Object_propertyIsEnumerable, object, key))
continue;
var value = object[key];
_DefineDataProperty(values, valuesCount++, value);
}
// Step 5.
return values;
}
// Draft proposal http://tc39.github.io/proposal-object-values-entries/#Object.entries
function ObjectEntries(O) {
// Steps 1-2.
var object = ToObject(O);
// Steps 3-4.
// EnumerableOwnProperties is inlined here.
var keys = OwnPropertyKeys(object, JSITER_OWNONLY | JSITER_HIDDEN);
var entries = [];
var entriesCount = 0;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!callFunction(std_Object_propertyIsEnumerable, object, key))
continue;
var value = object[key];
_DefineDataProperty(entries, entriesCount++, [key, value]);
}
// Step 5.
return entries;
}

View File

@ -50,4 +50,10 @@
#define ITEM_KIND_VALUE 1
#define ITEM_KIND_KEY_AND_VALUE 2
// NB: keep these in sync with the copy in jsfriendapi.h.
#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */
#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */
#define JSITER_SYMBOLS 0x20 /* also include symbol property keys */
#define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
#endif

View File

@ -920,6 +920,7 @@ class GCRuntime
bool shouldPreserveJITCode(JSCompartment* comp, int64_t currentTime,
JS::gcreason::Reason reason);
void bufferGrayRoots();
void markCompartments();
IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase);
template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
void markWeakReferencesInCurrentGroup(gcstats::Phase phase);

View File

@ -144,7 +144,7 @@ struct Zone : public JS::shadow::Zone,
void onTooMuchMalloc();
void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr = nullptr) {
if (!CurrentThreadCanAccessRuntime(runtime_))
if (!js::CurrentThreadCanAccessRuntime(runtime_))
return nullptr;
return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr);
}
@ -576,6 +576,62 @@ class CompartmentsIterT
typedef CompartmentsIterT<ZonesIter> CompartmentsIter;
/*
* Allocation policy that uses Zone::pod_malloc and friends, so that memory
* pressure is accounted for on the zone. This is suitable for memory associated
* with GC things allocated in the zone.
*
* Since it doesn't hold a JSContext (those may not live long enough), it can't
* report out-of-memory conditions itself; the caller must check for OOM and
* take the appropriate action.
*
* FIXME bug 647103 - replace these *AllocPolicy names.
*/
class ZoneAllocPolicy
{
Zone* const zone;
public:
MOZ_IMPLICIT ZoneAllocPolicy(Zone* zone) : zone(zone) {}
template <typename T>
T* maybe_pod_malloc(size_t numElems) {
return zone->maybe_pod_malloc<T>(numElems);
}
template <typename T>
T* maybe_pod_calloc(size_t numElems) {
return zone->maybe_pod_calloc<T>(numElems);
}
template <typename T>
T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
return zone->maybe_pod_realloc<T>(p, oldSize, newSize);
}
template <typename T>
T* pod_malloc(size_t numElems) {
return zone->pod_malloc<T>(numElems);
}
template <typename T>
T* pod_calloc(size_t numElems) {
return zone->pod_calloc<T>(numElems);
}
template <typename T>
T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
return zone->pod_realloc<T>(p, oldSize, newSize);
}
void free_(void* p) { js_free(p); }
void reportAllocOverflow() const {}
bool checkSimulatedOOM() const {
return !js::oom::ShouldFailWithOOM();
}
};
} // namespace js
#endif // gc_Zone_h

View File

@ -0,0 +1,98 @@
// Test inlining in Ion of fun.apply(..., array).
if (!this.getJitCompilerOptions() || !getJitCompilerOptions()['ion.enable'])
quit(0);
var itercount = 1000;
var warmup = 100;
// Force Ion to do something predictable without having to wait
// forever for it.
if (getJitCompilerOptions()["ion.warmup.trigger"] > warmup)
setJitCompilerOption("ion.warmup.trigger", warmup);
setJitCompilerOption("offthread-compilation.enable", 0);
function g(a, b, c, d) {
return a + b + c + (d === undefined);
}
var g_inIonInLoop = false;
var g_inIonAtEnd = false;
function f(xs) {
var sum = 0;
var inIonInLoop = 0;
for ( var i=0 ; i < itercount ; i++ ) {
inIonInLoop |= inIon();
sum += g.apply(null, xs);
}
g_ionAtEnd = inIon();
g_inIonInLoop = !!inIonInLoop;
return sum;
}
// Basic test
assertEq(f([1,2,3,4]), 6*itercount);
// Attempt to detect a botched optimization: either we ion-compiled
// the loop, or we did not ion-compile the function (ion not actually
// effective at all, this can happen).
assertEq(g_inIonInLoop || !g_inIonAtEnd, true);
// If Ion is inert just leave.
if (!g_inIonInLoop) {
print("Leaving early - ion not kicking in at all");
quit(0);
}
// Test that we get the correct argument value even if the array has
// fewer initialized members than its length.
var headroom = [1,2,3];
headroom.length = 13;
assertEq(f(headroom), 7*itercount);
// Test that we throw when the array is too long.
var thrown = false;
try {
var long = [];
long.length = getMaxArgs() + 1;
f(long);
}
catch (e) {
thrown = true;
assertEq(e instanceof RangeError, true);
}
assertEq(thrown, true);
// Test that the optimization is effective. There's the possibility
// of some false results here, and in release builds the margins are
// actually small.
itercount *= 2;
var A = Date.now();
assertEq(f([1,2,3,4]), 6*itercount) // Fast path because a sane array
var AinLoop = g_inIonInLoop;
var B = Date.now();
assertEq(f([1,2,3]), 7*itercount); // Fast path because a sane array, even if short
var BinLoop = g_inIonInLoop;
var C = Date.now();
assertEq(f(headroom), 7*itercount); // Slow path because length > initializedLength
var CinLoop = g_inIonInLoop;
var D = Date.now();
if (AinLoop && BinLoop && CinLoop) {
print("No bailout: " + (B - A));
print("Short: " + (C - B));
print("Bailout: " + (D - C));
assertEq((D - C) >= (B - A), true);
assertEq((D - C) >= (C - B), true);
} else {
print("Not running perf test");
}

View File

@ -53,8 +53,10 @@ function checkLcov(fun) {
}
}
// Evaluate the code, and generate the Lcov result from the execution.
var g = newGlobal();
// Evaluate the code, and generate the Lcov result from the execution. We have
// to disable lazy parsing, as we rely on the ZoneCellIter to emulate the
// behaviour of the finalizer.
var g = newGlobal({ disableLazyParsing: true });
g.eval(source);
var lcovResRaw = getLcovInfo(g);
@ -101,7 +103,33 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
if (l.length == 3) //DA:$,1
if (l.length == 3) //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,-
l.push(''); //DA:$,0
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:4
//LH:3
//BRF:2
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
if (l.length == 2) //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
l.push(''); //DA:$,1
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:4
//LH:4
//BRF:2
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
if (l.length == 3) //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,-
l.push(''); //DA:$,0
else
l.pop(); //DA:$,1
@ -109,13 +137,13 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
//FNH:1
//LF:4
//LH:3
//BRF:1
//BRF:2
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
if (l.length == 2) //DA:$,1
if (l.length == 2) //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
l.push(''); //DA:$,1
else
l.pop(); //DA:$,0
@ -123,23 +151,23 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
//FNH:1
//LF:4
//LH:3
//BRF:1
//BRF:2
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
if (l.length == 2) //DA:$,1
if (l.length == 2) //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
l.push(''); //DA:$,1
else {
if (l.length == 1) //DA:$,0
if (l.length == 1) //DA:$,0 //BRDA:$,1,0,- //BRDA:$,1,1,-
l.pop(); //DA:$,0
}
//FNF:1
//FNH:1
//LF:5
//LH:3
//BRF:2
//BRF:4
//BRH:1
});
@ -152,8 +180,8 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
x = x / 2; //DA:$,10
}
return x; //DA:$,2
//BRF:1
//BRH:1
//BRF:2
//BRH:2
}
f(5); //DA:$,1
@ -165,7 +193,7 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
checkLcov(function () { //FN:$,top-level //FNDA:1,%
try { //DA:$,1
var l = ",".split(','); //DA:$,1
if (l.length == 2) { //DA:$,1 // BRDA:$,0
if (l.length == 2) { //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
l.push(''); //DA:$,1
throw l; //DA:$,1
}
@ -178,7 +206,7 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
//LF:9 // Expected LF:8 , Apparently if the first statement is a try, the
// statement following the "try{" statement is visited twice.
//LH:8 // Expected LH:7
//BRF:1
//BRF:2
//BRH:1
});
@ -186,11 +214,11 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
try { //DA:$,1
try { //DA:$,1
if (l.length == 2) { //DA:$,1 // BRDA:$,0
if (l.length == 2) { //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
l.push(''); //DA:$,1
throw l; //DA:$,1
}
l.pop(); //DA:$,0 // BRDA:$,-
l.pop(); //DA:$,0
} finally { //DA:$,1
l.pop(); //DA:$,1
}
@ -223,5 +251,130 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
//BRH:0
});
// Test TableSwitch opcode
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
case 0:
l.push('0'); //DA:$,0
break;
case 1:
l.push('1'); //DA:$,0
break;
case 2:
l.push('2'); //DA:$,1
break;
case 3:
l.push('3'); //DA:$,0
break;
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:7
//LH:4
//BRF:5
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
case 0:
l.push('0'); //DA:$,0
case 1:
l.push('1'); //DA:$,0
case 2:
l.push('2'); //DA:$,1
case 3:
l.push('3'); //DA:$,1
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:7
//LH:5
//BRF:5
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
// Branches are ordered, and starting at 0
switch (l.length) { //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,- //BRDA:$,0,2,- //BRDA:$,0,3,- //BRDA:$,0,4,-
case 5:
l.push('5'); //DA:$,0
case 4:
l.push('4'); //DA:$,0
case 3:
l.push('3'); //DA:$,0
case 2:
l.push('2'); //DA:$,1
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:7
//LH:4
//BRF:5
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
case 2:
l.push('2'); //DA:$,1
case 5:
l.push('5'); //DA:$,1
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:5
//LH:5
//BRF:3
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,2,- //BRDA:$,0,3,1
case 3:
l.push('1'); //DA:$,0
case 5:
l.push('5'); //DA:$,0
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:5
//LH:3
//BRF:3
//BRH:1
});
// Unfortunately the differences between switch implementations leaks in the
// code coverage reports.
checkLcov(function () { //FN:$,top-level //FNDA:1,%
function f(a) { //FN:$,f //FNDA:2,%
return a; //DA:$,2
}
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case f(-42): //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
l.push('1'); //DA:$,0
case f(51): //DA:$,1 //BRDA:$,1,0,- //BRDA:$,1,1,1
l.push('5'); //DA:$,0
}
l.pop(); //DA:$,1
//FNF:2
//FNH:2
//LF:8
//LH:6
//BRF:4
//BRH:2
});
// If you add a test case here, do the same in
// jit-test/tests/debug/Script-getOffsetsCoverage-01.js

View File

@ -136,65 +136,55 @@ function checkGetOffsetsCoverage(fun) {
}
}
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
checkGetOffsetsCoverage(function () { //FN:$,top-level
",".split(','); //DA:$,1
//FNF:1
//FNH:1
//LF:1
//LH:1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
checkGetOffsetsCoverage(function () { //FN:$,top-level
function f() { //FN:$,f
",".split(','); //DA:$,0
}
",".split(','); //DA:$,1
//FNF:2
//FNH:1
//LF:2
//LH:1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
function f() { //FN:$,f //FNDA:1,%
checkGetOffsetsCoverage(function () { //FN:$,top-level
function f() { //FN:$,f
",".split(','); //DA:$,1
}
f(); //DA:$,1
//FNF:2
//FNH:2
//LF:2
//LH:2
});
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
if (l.length == 3) //DA:$,1
l.push(''); //DA:$,0
else
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:4
//LH:3
//BRF:1
//BRH:1
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
if (l.length == 2) //DA:$,1
l.push(''); //DA:$,1
else
l.pop(); //DA:$,0
//FNF:1
//FNH:1
//LF:4
//LH:3
//BRF:1
//BRH:1
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
if (l.length == 3) //DA:$,1
l.push(''); //DA:$,0
else
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
if (l.length == 2) //DA:$,1
l.push(''); //DA:$,1
else
l.pop(); //DA:$,0
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
if (l.length == 2) //DA:$,1
l.push(''); //DA:$,1
@ -202,16 +192,10 @@ checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
if (l.length == 1) //DA:$,0
l.pop(); //DA:$,0
}
//FNF:1
//FNH:1
//LF:5
//LH:3
//BRF:2
//BRH:1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
function f(i) { //FN:$,f //FNDA:2,%
checkGetOffsetsCoverage(function () { //FN:$,top-level
function f(i) { //FN:$,f
var x = 0; //DA:$,2
while (i--) { // Currently OSR wrongly count the loop header twice.
// So instead of DA:$,12 , we have DA:$,13 .
@ -219,20 +203,16 @@ checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
x = x / 2; //DA:$,10
}
return x; //DA:$,2
//BRF:1
//BRH:1
}
f(5); //DA:$,1
f(5); //DA:$,1
//FNF:2
//FNH:2
});
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
checkGetOffsetsCoverage(function () { //FN:$,top-level
try { //DA:$,1
var l = ",".split(','); //DA:$,1
if (l.length == 2) { //DA:$,1 // BRDA:$,0
if (l.length == 2) { //DA:$,1
l.push(''); //DA:$,1
throw l; //DA:$,1
}
@ -240,39 +220,26 @@ checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
} catch (x) { //DA:$,1
x.pop(); //DA:$,1
}
//FNF:1
//FNH:1
//LF:9 // Expected LF:8 , Apparently if the first statement is a try, the
// statement following the "try{" statement is visited twice.
//LH:8 // Expected LH:7
//BRF:1
//BRH:1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
try { //DA:$,1
try { //DA:$,1
if (l.length == 2) { //DA:$,1 // BRDA:$,0
if (l.length == 2) { //DA:$,1
l.push(''); //DA:$,1
throw l; //DA:$,1
}
l.pop(); //DA:$,0 // BRDA:$,-
l.pop(); //DA:$,0
} finally { //DA:$,1
l.pop(); //DA:$,1
}
} catch (x) { //DA:$,1
}
//FNF:1
//FNH:1
//LF:10
//LH:9
//BRF:2
//BRH:1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
function f() { //FN:$,f //FNDA:1,%
checkGetOffsetsCoverage(function () { //FN:$,top-level
function f() { //FN:$,f
throw 1; //DA:$,1
f(); //DA:$,0
}
@ -282,12 +249,95 @@ checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
f(); //DA:$,0
} catch (x) { //DA:$,1
}
//FNF:2
//FNH:2
//LF:7
//LH:5
//BRF:0
//BRH:0
});
// Test TableSwitch opcode
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 0:
l.push('0'); //DA:$,0
break;
case 1:
l.push('1'); //DA:$,0
break;
case 2:
l.push('2'); //DA:$,1
break;
case 3:
l.push('3'); //DA:$,0
break;
}
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 0:
l.push('0'); //DA:$,0
case 1:
l.push('1'); //DA:$,0
case 2:
l.push('2'); //DA:$,1
case 3:
l.push('3'); //DA:$,1
}
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 5:
l.push('5'); //DA:$,0
case 4:
l.push('4'); //DA:$,0
case 3:
l.push('3'); //DA:$,0
case 2:
l.push('2'); //DA:$,1
}
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 2:
l.push('2'); //DA:$,1
case 5:
l.push('5'); //DA:$,1
}
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 3:
l.push('1'); //DA:$,0
case 5:
l.push('5'); //DA:$,0
}
l.pop(); //DA:$,1
});
// Unfortunately the differences between switch implementations leaks in the
// code coverage reports.
checkGetOffsetsCoverage(function () { //FN:$,top-level
function f(a) { //FN:$,f
return a; //DA:$,2
}
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case f(-42): //DA:$,1
l.push('1'); //DA:$,0
case f(51): //DA:$,1
l.push('5'); //DA:$,0
}
l.pop(); //DA:$,1
});
// If you add a test case here, do the same in

View File

@ -1723,6 +1723,9 @@ BacktrackingAllocator::resolveControlFlow()
if (mir->shouldCancel("Backtracking Resolve Control Flow (vreg loop)"))
return false;
if (!alloc().ensureBallast())
return false;
for (LiveRange::RegisterLinkIterator iter = reg.rangesBegin(); iter; ) {
LiveRange* range = LiveRange::get(*iter);

View File

@ -1720,7 +1720,9 @@ CopyFromRematerializedFrame(JSContext* cx, JitActivation* act, uint8_t* fp, size
MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs());
frame->setScopeChain(rematFrame->scopeChain());
frame->thisValue() = rematFrame->thisValue();
if (frame->isNonEvalFunctionFrame())
frame->thisArgument() = rematFrame->thisArgument();
for (unsigned i = 0; i < frame->numActualArgs(); i++)
frame->argv()[i] = rematFrame->argv()[i];

View File

@ -891,7 +891,7 @@ BaselineCompiler::emitProfilerEnterFrame()
masm.bind(&noInstrument);
// Store the start offset in the appropriate location.
MOZ_ASSERT(profilerEnterFrameToggleOffset_.offset() == 0);
MOZ_ASSERT(!profilerEnterFrameToggleOffset_.used());
profilerEnterFrameToggleOffset_ = toggleOffset;
}
@ -906,7 +906,7 @@ BaselineCompiler::emitProfilerExitFrame()
masm.bind(&noInstrument);
// Store the start offset in the appropriate location.
MOZ_ASSERT(profilerExitFrameToggleOffset_.offset() == 0);
MOZ_ASSERT(!profilerExitFrameToggleOffset_.used());
profilerExitFrameToggleOffset_ = toggleOffset;
}
@ -4028,8 +4028,8 @@ BaselineCompiler::emit_JSOP_RESUME()
}
masm.bind(&loopDone);
// Push |this|.
masm.pushValue(Address(genObj, GeneratorObject::offsetOfThisSlot()));
// Push |undefined| for |this|.
masm.pushValue(UndefinedValue());
// Update BaselineFrame frameSize field and create the frame descriptor.
masm.computeEffectiveAddress(Address(BaselineFrameReg, BaselineFrame::FramePointerOffset),

View File

@ -32,10 +32,10 @@ BaselineFrame::trace(JSTracer* trc, JitFrameIterator& frameIterator)
{
replaceCalleeToken(MarkCalleeToken(trc, calleeToken()));
TraceRoot(trc, &thisValue(), "baseline-this");
// Mark actual and formal args.
// Mark |this|, actual and formal args.
if (isNonEvalFunctionFrame()) {
TraceRoot(trc, &thisArgument(), "baseline-this");
unsigned numArgs = js::Max(numActualArgs(), numFormalArgs());
TraceRootRange(trc, numArgs + isConstructing(), argv(), "baseline-args");
}

Some files were not shown because too many files have changed in this diff Show More