mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-13 03:24:26 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
358e4575c4
@ -1,5 +0,0 @@
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % passwordManagerDTD SYSTEM "chrome://passwordmgr/locale/passwordManager.dtd">
|
||||
%passwordManagerDTD;
|
||||
]>
|
||||
<window>&savedLogins.title;</window>
|
@ -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]
|
||||
|
@ -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>
|
@ -64,6 +64,8 @@ var LoopMochaUtils = (function(global, _) {
|
||||
throw result;
|
||||
};
|
||||
|
||||
this.catch = function() {};
|
||||
|
||||
asyncFn(this.resolve.bind(this), this.reject.bind(this));
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -1058,6 +1058,7 @@ Animation::UpdateEffect()
|
||||
{
|
||||
if (mEffect) {
|
||||
UpdateRelevance();
|
||||
mEffect->NotifyAnimationTimingUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
|
119
dom/animation/EffectSet.cpp
Normal file
119
dom/animation/EffectSet.cpp
Normal 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
136
dom/animation/EffectSet.h
Normal 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
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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',
|
||||
]
|
||||
|
@ -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>>());
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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*
|
||||
|
@ -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);
|
||||
|
@ -1,2 +0,0 @@
|
||||
// Ensure the contacts service is running in the parent.
|
||||
Components.utils.import("resource://gre/modules/ContactService.jsm");
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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] () {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
LazyLogModule gWebAudioAPILog("WebAudioAPI");
|
||||
|
||||
namespace dom {
|
||||
|
||||
void WebAudioUtils::ConvertAudioTimelineEventToTicks(AudioTimelineEvent& aEvent,
|
||||
|
@ -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;
|
||||
|
@ -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']
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -13,8 +13,10 @@
|
||||
enum MediaKeyStatus {
|
||||
"usable",
|
||||
"expired",
|
||||
"released",
|
||||
"output-restricted",
|
||||
"output-downscaled",
|
||||
"output-not-allowed",
|
||||
"status-pending",
|
||||
"internal-error"
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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']),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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.
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -273,6 +273,9 @@ ImageBridgeChild::~ImageBridgeChild()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
|
||||
new DeleteTask<Transport>(GetTransport()));
|
||||
|
||||
delete mTxn;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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().
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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() {
|
||||
|
@ -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
46
js/src/asmjs/AsmJSCompileInputs.h
Normal file
46
js/src/asmjs/AsmJSCompileInputs.h
Normal 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
@ -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;
|
||||
|
@ -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_);
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
98
js/src/jit-test/tests/basic/testApplyArrayInline.js
Normal file
98
js/src/jit-test/tests/basic/testApplyArrayInline.js
Normal 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");
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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),
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user