mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1123516 - Implement maplike/setlike in WebIDL parser; r=bz (5d62bcd93) - Bug 1140324 - Remove __noSuchMethod__ handling from WebIDL parser and throw an exception instead. r=peterv (f7ea99339) - Bug 1123516 - Implement maplike/setlike in WebIDL Codegen; r=b (0ca39b335) - Bug 1183604, add some more assertions to help implementing new cycle collectable classes, r=mccr8 (1e66d29fe) - Bug 1178665 - Part 1: Make Promise::DispatchToMicroTask public. r=khuey (b962e6006) - Bug 1178665 - Part 2 - Adapt to latest Animation.finish procedure changes. r=bbirtles (33219fc0d) - Bug 1178665 - Part 3: Make finish notifications asynchronously in most cases. r=bbirtles, r=smaug (144c0944a) - Bug1180770part 1. Remove the unused ThrowNotEnoughArgsError. r=peterv (8bc1690f5) - Bug1180770part 2. Remove the unused ifaceName/memberName arguments of ThrowMethodFailedWithDetails and rename it to ThrowMethodFailed. r=peterv (ee4900547) - Bug 1135961. Implement subclassing of DOM objects. r=peterv (8e7e67b88) - Bug 1170691 - part 1 - add the generating script's directory to sys.path in file_generate.py; r=glandium (dd1520952) - Bug 1168409 - part 1 - avoid importing buildconfig in histogram_tools.py; r=gfritzsche (6a46dce23) - Bug 1168409 - part 2 - avoiding importing usecounters in histogram_tools.py; r=gfritzsche (21a468303) - Bug 1144397. Disallow using fill when dedent would do. r=peterv (544d4978d) - Bug 1158806. Don't try to include stuff for a generated hasInstance hook if we have no interface object, since in that case we don't need the include. r=peterv (d280a1608) - missing bit of Bug 1161627 - part 2 - machine-convert TemporaryRef<T> to already_AddRefed<T> (c51384311) - Bug 1166910 followup: Add missing 'override' keyword to HTMLImageElement method GetImageReferrerPolicy. rs=ehsan (9e3dc8e6d) - Bug 1174913 - remove unnecessary attribute parsing. r=bz (fdb769eda) - Bug 1170680 - Do not add non-animated images to the visible list in response to UNLOCKED_DRAW. r=tn (a594883e8) - Bug 1174923 - Stop delaying the document load event until images are decoded. r=tn a=kwierso (caee1b25f) - Bug 968923 - part 3b - propagating use counters from SVG images into owning/parent documents; r=seth (234a41484) - Bug 968923 - part 3a - add core DOM use counter functionality; r=smaug (98bb77358) - Bug 968923 - part 3c - miscellaneous telemetry changes for use counters; r=gfritzsche (83adec291) - Bug 968923 - part 4 - hook up use counters to WebIDL bindings; r=bz (8545e9a9b) - Bug 771367 - Update test_animations_omta.html to support testing pseudo-elements. r=dbaron (4b2e5481b) - Bug 1177563 - Test that we share agent rule processors across different documents. r=dbaron (d64146359) - Bug 1181450 - Make GENERATED_FILES more visible during the build by printing their name when they are being generated. r=gps (b0c2166e8) - Bug 1215526 - part 1 - pass dependencies file to file_generate.py; r=glandium (a14ea304a) - Bug 1215526 - part 2 - write dependencies to file_generate.py's depfile; r=glandium (dc49ad380)
This commit is contained in:
@@ -677,6 +677,7 @@
|
||||
#if defined(ENABLE_TESTS) && defined(MOZ_DEBUG)
|
||||
@RESPATH@/components/TestInterfaceJS.js
|
||||
@RESPATH@/components/TestInterfaceJS.manifest
|
||||
@RESPATH@/components/TestInterfaceJSMaplike.js
|
||||
#endif
|
||||
|
||||
@RESPATH@/components/PACGenerator.js
|
||||
|
||||
@@ -636,6 +636,7 @@
|
||||
#if defined(ENABLE_TESTS) && defined(MOZ_DEBUG)
|
||||
@RESPATH@/components/TestInterfaceJS.js
|
||||
@RESPATH@/components/TestInterfaceJS.manifest
|
||||
@RESPATH@/components/TestInterfaceJSMaplike.js
|
||||
#endif
|
||||
|
||||
@RESPATH@/components/PACGenerator.js
|
||||
|
||||
@@ -13712,15 +13712,9 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent,
|
||||
// if per element referrer is enabled, the element referrer overrules
|
||||
// the document wide referrer
|
||||
if (IsElementAnchor(aContent)) {
|
||||
MOZ_ASSERT(aContent->IsHTMLElement());
|
||||
if (Preferences::GetBool("network.http.enablePerElementReferrer", false)) {
|
||||
nsAutoString referrerPolicy;
|
||||
if (aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::referrer, referrerPolicy)) {
|
||||
uint32_t refPolEnum = mozilla::net::ReferrerPolicyFromString(referrerPolicy);
|
||||
if (refPolEnum != mozilla::net::RP_Unset) {
|
||||
refererPolicy = refPolEnum;
|
||||
}
|
||||
}
|
||||
net::ReferrerPolicy refPolEnum = aContent->AsElement()->GetReferrerPolicy();
|
||||
if (refPolEnum != net::RP_Unset) {
|
||||
refererPolicy = refPolEnum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+69
-33
@@ -13,6 +13,7 @@
|
||||
#include "nsIDocument.h" // For nsIDocument
|
||||
#include "nsIPresShell.h" // For nsIPresShell
|
||||
#include "nsLayoutUtils.h" // For PostRestyleEvent (remove after bug 1073336)
|
||||
#include "nsThreadUtils.h" // For nsRunnableMethod and nsRevocableEventPtr
|
||||
#include "PendingAnimationTracker.h" // For PendingAnimationTracker
|
||||
|
||||
namespace mozilla {
|
||||
@@ -70,7 +71,7 @@ Animation::SetTimeline(AnimationTimeline* aTimeline)
|
||||
|
||||
// FIXME(spec): Once we implement the seeking defined in the spec
|
||||
// surely this should be SeekFlag::DidSeek but the spec says otherwise.
|
||||
UpdateTiming(SeekFlag::NoSeek);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
|
||||
// FIXME: When we expose this method to script we'll need to call PostUpdate
|
||||
// (but *not* when this method gets called from style).
|
||||
@@ -107,7 +108,7 @@ Animation::SetStartTime(const Nullable<TimeDuration>& aNewStartTime)
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
PostUpdate();
|
||||
}
|
||||
|
||||
@@ -148,7 +149,7 @@ Animation::SetCurrentTime(const TimeDuration& aSeekTime)
|
||||
CancelPendingTasks();
|
||||
}
|
||||
|
||||
UpdateTiming(SeekFlag::DidSeek);
|
||||
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Async);
|
||||
PostUpdate();
|
||||
}
|
||||
|
||||
@@ -210,8 +211,8 @@ Animation::GetFinished(ErrorResult& aRv)
|
||||
}
|
||||
if (!mFinished) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
} else if (PlayState() == AnimationPlayState::Finished) {
|
||||
mFinished->MaybeResolve(this);
|
||||
} else if (mFinishedIsResolved) {
|
||||
MaybeResolveFinishedPromise();
|
||||
}
|
||||
return mFinished;
|
||||
}
|
||||
@@ -236,7 +237,7 @@ Animation::Finish(ErrorResult& aRv)
|
||||
TimeDuration limit =
|
||||
mPlaybackRate > 0 ? TimeDuration(EffectEnd()) : TimeDuration(0);
|
||||
|
||||
SetCurrentTime(limit);
|
||||
SilentlySetCurrentTime(limit);
|
||||
|
||||
// If we are paused or play-pending we need to fill in the start time in
|
||||
// order to transition to the finished state.
|
||||
@@ -252,18 +253,22 @@ Animation::Finish(ErrorResult& aRv)
|
||||
limit.MultDouble(1.0 / mPlaybackRate));
|
||||
}
|
||||
|
||||
// If we just resolved the start time for a pause-pending animation, we need
|
||||
// to clear the task. We don't do this as a branch of the above however since
|
||||
// we can have a play-pending animation with a resolved start time if we
|
||||
// aborted a pause operation.
|
||||
if (mPendingState == PendingState::PlayPending &&
|
||||
!mStartTime.IsNull()) {
|
||||
// If we just resolved the start time for a pause or play-pending
|
||||
// animation, we need to clear the task. We don't do this as a branch of
|
||||
// the above however since we can have a play-pending animation with a
|
||||
// resolved start time if we aborted a pause operation.
|
||||
if (!mStartTime.IsNull() &&
|
||||
(mPendingState == PendingState::PlayPending ||
|
||||
mPendingState == PendingState::PausePending)) {
|
||||
if (mPendingState == PendingState::PausePending) {
|
||||
mHoldTime.SetNull();
|
||||
}
|
||||
CancelPendingTasks();
|
||||
if (mReady) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
UpdateTiming(SeekFlag::DidSeek);
|
||||
UpdateTiming(SeekFlag::DidSeek, SyncNotifyFlag::Sync);
|
||||
PostUpdate();
|
||||
}
|
||||
|
||||
@@ -343,7 +348,7 @@ Animation::Tick()
|
||||
FinishPendingAt(mTimeline->GetCurrentTime().Value());
|
||||
}
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -452,13 +457,12 @@ Animation::DoCancel()
|
||||
if (mFinished) {
|
||||
mFinished->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
// Clear finished promise. We'll create a new one lazily.
|
||||
mFinished = nullptr;
|
||||
ResetFinishedPromise();
|
||||
|
||||
mHoldTime.SetNull();
|
||||
mStartTime.SetNull();
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -590,7 +594,7 @@ Animation::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
|
||||
mEffect->ComposeStyle(aStyleRule, aSetProperties);
|
||||
|
||||
if (updatedHoldTime) {
|
||||
UpdateTiming(SeekFlag::NoSeek);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
}
|
||||
|
||||
mFinishedAtLastComposeStyle = (playState == AnimationPlayState::Finished);
|
||||
@@ -661,7 +665,7 @@ Animation::DoPlay(ErrorResult& aRv, LimitBehavior aLimitBehavior)
|
||||
TriggerOnNextTick(Nullable<TimeDuration>());
|
||||
}
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
}
|
||||
|
||||
// http://w3c.github.io/web-animations/#pause-an-animation
|
||||
@@ -712,7 +716,7 @@ Animation::DoPause(ErrorResult& aRv)
|
||||
TriggerOnNextTick(Nullable<TimeDuration>());
|
||||
}
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -740,7 +744,7 @@ Animation::ResumeAt(const TimeDuration& aReadyTime)
|
||||
}
|
||||
mPendingState = PendingState::NotPending;
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
|
||||
if (mReady) {
|
||||
mReady->MaybeResolve(this);
|
||||
@@ -760,7 +764,7 @@ Animation::PauseAt(const TimeDuration& aReadyTime)
|
||||
mStartTime.SetNull();
|
||||
mPendingState = PendingState::NotPending;
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek);
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
|
||||
if (mReady) {
|
||||
mReady->MaybeResolve(this);
|
||||
@@ -768,7 +772,7 @@ Animation::PauseAt(const TimeDuration& aReadyTime)
|
||||
}
|
||||
|
||||
void
|
||||
Animation::UpdateTiming(SeekFlag aSeekFlag)
|
||||
Animation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag)
|
||||
{
|
||||
// Update the sequence number each time we transition in or out of the
|
||||
// idle state
|
||||
@@ -782,7 +786,7 @@ Animation::UpdateTiming(SeekFlag aSeekFlag)
|
||||
|
||||
// We call UpdateFinishedState before UpdateEffect because the former
|
||||
// can change the current time, which is used by the latter.
|
||||
UpdateFinishedState(aSeekFlag);
|
||||
UpdateFinishedState(aSeekFlag, aSyncNotifyFlag);
|
||||
UpdateEffect();
|
||||
|
||||
// Unconditionally Add/Remove from the timeline. This is ok because if the
|
||||
@@ -822,7 +826,8 @@ Animation::UpdateTiming(SeekFlag aSeekFlag)
|
||||
}
|
||||
|
||||
void
|
||||
Animation::UpdateFinishedState(SeekFlag aSeekFlag)
|
||||
Animation::UpdateFinishedState(SeekFlag aSeekFlag,
|
||||
SyncNotifyFlag aSyncNotifyFlag)
|
||||
{
|
||||
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
||||
TimeDuration effectEnd = TimeDuration(EffectEnd());
|
||||
@@ -860,18 +865,14 @@ Animation::UpdateFinishedState(SeekFlag aSeekFlag)
|
||||
}
|
||||
|
||||
bool currentFinishedState = PlayState() == AnimationPlayState::Finished;
|
||||
if (currentFinishedState && !mIsPreviousStateFinished) {
|
||||
if (mFinished) {
|
||||
mFinished->MaybeResolve(this);
|
||||
}
|
||||
} else if (!currentFinishedState && mIsPreviousStateFinished) {
|
||||
// Clear finished promise. We'll create a new one lazily.
|
||||
mFinished = nullptr;
|
||||
if (currentFinishedState && !mFinishedIsResolved) {
|
||||
DoFinishNotification(aSyncNotifyFlag);
|
||||
} else if (!currentFinishedState && mFinishedIsResolved) {
|
||||
ResetFinishedPromise();
|
||||
if (mEffect->AsTransition()) {
|
||||
mEffect->SetIsFinishedTransition(false);
|
||||
}
|
||||
}
|
||||
mIsPreviousStateFinished = currentFinishedState;
|
||||
// We must recalculate the current time to take account of any mHoldTime
|
||||
// changes the code above made.
|
||||
mPreviousCurrentTime = GetCurrentTime();
|
||||
@@ -1042,5 +1043,40 @@ Animation::GetCollection() const
|
||||
return manager->GetAnimations(targetElement, targetPseudoType, false);
|
||||
}
|
||||
|
||||
void
|
||||
Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
|
||||
{
|
||||
if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
|
||||
MaybeResolveFinishedPromise();
|
||||
} else if (!mFinishNotificationTask.IsPending()) {
|
||||
nsRefPtr<nsRunnableMethod<Animation>> runnable =
|
||||
NS_NewRunnableMethod(this, &Animation::MaybeResolveFinishedPromise);
|
||||
Promise::DispatchToMicroTask(runnable);
|
||||
mFinishNotificationTask = runnable;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Animation::ResetFinishedPromise()
|
||||
{
|
||||
mFinishedIsResolved = false;
|
||||
mFinished = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Animation::MaybeResolveFinishedPromise()
|
||||
{
|
||||
mFinishNotificationTask.Revoke();
|
||||
|
||||
if (PlayState() != AnimationPlayState::Finished) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFinished) {
|
||||
mFinished->MaybeResolve(this);
|
||||
}
|
||||
mFinishedIsResolved = true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -60,9 +60,9 @@ public:
|
||||
, mPendingState(PendingState::NotPending)
|
||||
, mSequenceNum(kUnsequenced)
|
||||
, mIsRunningOnCompositor(false)
|
||||
, mIsPreviousStateFinished(false)
|
||||
, mFinishedAtLastComposeStyle(false)
|
||||
, mIsRelevant(false)
|
||||
, mFinishedIsResolved(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -321,11 +321,21 @@ protected:
|
||||
DidSeek
|
||||
};
|
||||
|
||||
void UpdateTiming(SeekFlag aSeekFlag);
|
||||
void UpdateFinishedState(SeekFlag aSeekFlag);
|
||||
enum class SyncNotifyFlag {
|
||||
Sync,
|
||||
Async
|
||||
};
|
||||
|
||||
void UpdateTiming(SeekFlag aSeekFlag,
|
||||
SyncNotifyFlag aSyncNotifyFlag);
|
||||
void UpdateFinishedState(SeekFlag aSeekFlag,
|
||||
SyncNotifyFlag aSyncNotifyFlag);
|
||||
void UpdateEffect();
|
||||
void FlushStyle() const;
|
||||
void PostUpdate();
|
||||
void ResetFinishedPromise();
|
||||
void MaybeResolveFinishedPromise();
|
||||
void DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag);
|
||||
|
||||
/**
|
||||
* Remove this animation from the pending animation tracker and reset
|
||||
@@ -383,13 +393,17 @@ protected:
|
||||
uint64_t mSequenceNum;
|
||||
|
||||
bool mIsRunningOnCompositor;
|
||||
// Indicates whether we were in the finished state during our
|
||||
// most recent unthrottled sample (our last ComposeStyle call).
|
||||
bool mIsPreviousStateFinished; // Spec calls this "previous finished state"
|
||||
bool mFinishedAtLastComposeStyle;
|
||||
// Indicates that the animation should be exposed in an element's
|
||||
// getAnimations() list.
|
||||
bool mIsRelevant;
|
||||
|
||||
nsRevocableEventPtr<nsRunnableMethod<Animation>> mFinishNotificationTask;
|
||||
// True if mFinished is resolved or would be resolved if mFinished has
|
||||
// yet to be created. This is not set when mFinished is rejected since
|
||||
// in that case mFinished is immediately reset to represent a new current
|
||||
// finished promise.
|
||||
bool mFinishedIsResolved;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -239,6 +239,27 @@ async_test(function(t) {
|
||||
}));
|
||||
}, 'Test resetting of computed style');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var resolvedFinished = false;
|
||||
animation.finished.then(function() {
|
||||
resolvedFinished = true;
|
||||
});
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.finish();
|
||||
}).then(t.step_func(function() {
|
||||
assert_true(resolvedFinished,
|
||||
'Animation.finished should be resolved soon after ' +
|
||||
'Animation.finish()');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
}, 'Test finish() resolves finished promise synchronously');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -405,6 +405,142 @@ async_test(function(t) {
|
||||
}));
|
||||
}, 'Test finished promise changes when animationPlayState set to running');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.currentTime = 0;
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should change once a prior ' +
|
||||
'finished promise resolved and the animation ' +
|
||||
'falls out finished state');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finished promise changes when a prior finished promise resolved ' +
|
||||
'and the animation falls out finished state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
animation.currentTime = ANIM_DURATION / 2;
|
||||
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'No new finished promise generated when finished state ' +
|
||||
'is checked asynchronously');
|
||||
t.done();
|
||||
}, 'Test no new finished promise generated when finished state ' +
|
||||
'is checked asynchronously');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.finish();
|
||||
animation.currentTime = ANIM_DURATION / 2;
|
||||
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'New finished promise generated when finished state ' +
|
||||
'is checked synchronously');
|
||||
t.done();
|
||||
}, 'Test new finished promise generated when finished state ' +
|
||||
'is checked synchronously');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var resolvedFinished = false;
|
||||
animation.finished.then(function() {
|
||||
resolvedFinished = true;
|
||||
});
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.finish();
|
||||
animation.currentTime = ANIM_DURATION / 2;
|
||||
}).then(t.step_func(function() {
|
||||
assert_true(resolvedFinished,
|
||||
'Animation.finished should be resolved even if ' +
|
||||
'the finished state is changed soon');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
}, 'Test synchronous finished promise resolved even if finished state ' +
|
||||
'is changed soon');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var resolvedFinished = false;
|
||||
animation.finished.then(function() {
|
||||
resolvedFinished = true;
|
||||
});
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
animation.finish();
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(resolvedFinished,
|
||||
'Animation.finished should be resolved soon after finish() is ' +
|
||||
'called even if there are other asynchronous promises just before it');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test synchronous finished promise resolved even if asynchronous ' +
|
||||
'finished promise happens just before synchronous promise');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('Animation.finished should not be resolved');
|
||||
}));
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
animation.currentTime = ANIM_DURATION / 2;
|
||||
}).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finished promise is not resolved when the animation ' +
|
||||
'falls out finished state immediately');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('Animation.finished should not be resolved');
|
||||
}));
|
||||
animation.currentTime = 0;
|
||||
}).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
}, 'Test finished promise is not resolved once the animation ' +
|
||||
'falls out finished state even though the current finished ' +
|
||||
'promise is generated soon after animation state became finished');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
#include "mozilla/dom/ElementBinding.h"
|
||||
#include "mozilla/dom/VRDevice.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@@ -3681,3 +3682,16 @@ Element::FontSizeInflation()
|
||||
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
net::ReferrerPolicy
|
||||
Element::GetReferrerPolicy()
|
||||
{
|
||||
if (Preferences::GetBool("network.http.enablePerElementReferrer", false) &&
|
||||
IsHTMLElement()) {
|
||||
const nsAttrValue* referrerValue = GetParsedAttr(nsGkAtoms::referrer);
|
||||
if (referrerValue && referrerValue->Type() == nsAttrValue::eEnum) {
|
||||
return net::ReferrerPolicy(referrerValue->GetEnumValue());
|
||||
}
|
||||
}
|
||||
return net::RP_Unset;
|
||||
}
|
||||
@@ -1105,6 +1105,8 @@ public:
|
||||
*/
|
||||
float FontSizeInflation();
|
||||
|
||||
net::ReferrerPolicy GetReferrerPolicy();
|
||||
|
||||
protected:
|
||||
/*
|
||||
* Named-bools for use with SetAttrAndNotify to make call sites easier to
|
||||
|
||||
@@ -5,14 +5,17 @@
|
||||
#ifndef UseCounter_h_
|
||||
#define UseCounter_h_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
enum UseCounter {
|
||||
enum UseCounter : int16_t {
|
||||
eUseCounter_UNKNOWN = -1,
|
||||
#define USE_COUNTER_DOM_METHOD(interface_, name_) \
|
||||
eUseCounter_##interface_##_##name_,
|
||||
#define USE_COUNTER_DOM_ATTRIBUTE(interface_, name_) \
|
||||
eUseCounter_##interface_##_##name_,
|
||||
eUseCounter_##interface_##_##name_##_getter, \
|
||||
eUseCounter_##interface_##_##name_##_setter,
|
||||
#define USE_COUNTER_CSS_PROPERTY(name_, id_) \
|
||||
eUseCounter_property_##id_,
|
||||
#include "mozilla/dom/UseCounterList.h"
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
#include "nsIContentIterator.h"
|
||||
#include "nsIDOMStyleSheet.h"
|
||||
#include "nsIStyleSheet.h"
|
||||
#include "nsIStyleSheetService.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
@@ -3520,6 +3521,7 @@ nsDOMWindowUtils::RequestCompositorProperty(const nsAString& property,
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetOMTAStyle(nsIDOMElement* aElement,
|
||||
const nsAString& aProperty,
|
||||
const nsAString& aPseudoElement,
|
||||
nsAString& aResult)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
@@ -3531,6 +3533,15 @@ nsDOMWindowUtils::GetOMTAStyle(nsIDOMElement* aElement,
|
||||
|
||||
nsRefPtr<nsROCSSPrimitiveValue> cssValue = nullptr;
|
||||
nsIFrame* frame = element->GetPrimaryFrame();
|
||||
if (frame && !aPseudoElement.IsEmpty()) {
|
||||
if (aPseudoElement.EqualsLiteral("::before")) {
|
||||
frame = nsLayoutUtils::GetBeforeFrame(frame);
|
||||
} else if (aPseudoElement.EqualsLiteral("::after")) {
|
||||
frame = nsLayoutUtils::GetAfterFrame(frame);
|
||||
} else {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
if (frame && nsLayoutUtils::AreAsyncAnimationsEnabled()) {
|
||||
if (aProperty.EqualsLiteral("opacity")) {
|
||||
Layer* layer =
|
||||
@@ -3864,6 +3875,21 @@ nsDOMWindowUtils::LeaveChaosMode()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
|
||||
bool* aRetVal)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
nsIPresShell* presShell = GetPresShell();
|
||||
if (!presShell) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return presShell->HasRuleProcessorUsedByMultipleStyleSets(aSheetType,
|
||||
aRetVal);
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)
|
||||
|
||||
@@ -1677,6 +1677,8 @@ nsDocument::~nsDocument()
|
||||
}
|
||||
}
|
||||
|
||||
ReportUseCounters();
|
||||
|
||||
mInDestructor = true;
|
||||
mInUnlinkOrDeletion = true;
|
||||
|
||||
@@ -4651,6 +4653,8 @@ nsIDocument::SetContainer(nsDocShell* aContainer)
|
||||
if (sameTypeRoot == aContainer) {
|
||||
static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
|
||||
}
|
||||
|
||||
static_cast<nsDocument*>(this)->SetIsContentDocument(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4858,6 +4862,18 @@ nsDocument::SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument)
|
||||
mIsTopLevelContentDocument = aIsTopLevelContentDocument;
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocument::IsContentDocument() const
|
||||
{
|
||||
return mIsContentDocument;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::SetIsContentDocument(bool aIsContentDocument)
|
||||
{
|
||||
mIsContentDocument = aIsContentDocument;
|
||||
}
|
||||
|
||||
nsPIDOMWindow *
|
||||
nsDocument::GetWindowInternal() const
|
||||
{
|
||||
@@ -12973,6 +12989,196 @@ nsIDocument::InlineScriptAllowedByCSP()
|
||||
return allowsInlineScript;
|
||||
}
|
||||
|
||||
nsIDocument*
|
||||
nsIDocument::GetTopLevelContentDocument()
|
||||
{
|
||||
nsDocument* parent;
|
||||
|
||||
if (!mLoadedAsData) {
|
||||
parent = static_cast<nsDocument*>(this);
|
||||
} else {
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetScopeObject());
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
parent = static_cast<nsDocument*>(window->GetExtantDoc());
|
||||
if (!parent) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
if (parent->IsTopLevelContentDocument()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If we ever have a non-content parent before we hit a toplevel content
|
||||
// parent, then we're never going to find one. Just bail.
|
||||
if (!parent->IsContentDocument()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIDocument* candidate = parent->GetParentDocument();
|
||||
parent = static_cast<nsDocument*>(candidate);
|
||||
} while (parent);
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::PropagateUseCounters(nsIDocument* aParentDocument)
|
||||
{
|
||||
MOZ_ASSERT(this != aParentDocument);
|
||||
|
||||
// What really matters here is that our use counters get propagated as
|
||||
// high up in the content document hierarchy as possible. So,
|
||||
// starting with aParentDocument, we need to find the toplevel content
|
||||
// document, and propagate our use counters into its
|
||||
// mChildDocumentUseCounters.
|
||||
nsIDocument* contentParent = aParentDocument->GetTopLevelContentDocument();
|
||||
|
||||
if (!contentParent) {
|
||||
return;
|
||||
}
|
||||
|
||||
contentParent->mChildDocumentUseCounters |= mUseCounters;
|
||||
contentParent->mChildDocumentUseCounters |= mChildDocumentUseCounters;
|
||||
}
|
||||
|
||||
void
|
||||
nsIDocument::SetPageUseCounter(UseCounter aUseCounter)
|
||||
{
|
||||
// We want to set the use counter on the "page" that owns us; the definition
|
||||
// of "page" depends on what kind of document we are. See the comments below
|
||||
// for details. In any event, checking all the conditions below is
|
||||
// reasonably expensive, so we cache whether we've notified our owning page.
|
||||
if (mNotifiedPageForUseCounter[aUseCounter]) {
|
||||
return;
|
||||
}
|
||||
mNotifiedPageForUseCounter[aUseCounter] = true;
|
||||
|
||||
if (mDisplayDocument) {
|
||||
// If we are a resource document, we won't have a docshell and so we won't
|
||||
// record any page use counters on this document. Instead, we should
|
||||
// forward it up to the document that loaded us.
|
||||
MOZ_ASSERT(!mDocumentContainer);
|
||||
mDisplayDocument->SetChildDocumentUseCounter(aUseCounter);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsBeingUsedAsImage()) {
|
||||
// If this is an SVG image document, we also won't have a docshell.
|
||||
MOZ_ASSERT(!mDocumentContainer);
|
||||
return;
|
||||
}
|
||||
|
||||
// We only care about use counters in content. If we're already a toplevel
|
||||
// content document, then we should have already set the use counter on
|
||||
// ourselves, and we are done.
|
||||
nsIDocument* contentParent = GetTopLevelContentDocument();
|
||||
if (!contentParent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this == contentParent) {
|
||||
MOZ_ASSERT(GetUseCounter(aUseCounter));
|
||||
return;
|
||||
}
|
||||
|
||||
contentParent->SetChildDocumentUseCounter(aUseCounter);
|
||||
}
|
||||
|
||||
static bool
|
||||
MightBeAboutOrChromeScheme(nsIURI* aURI)
|
||||
{
|
||||
MOZ_ASSERT(aURI);
|
||||
bool isAbout = true;
|
||||
bool isChrome = true;
|
||||
aURI->SchemeIs("about", &isAbout);
|
||||
aURI->SchemeIs("chrome", &isChrome);
|
||||
return isAbout || isChrome;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::ReportUseCounters()
|
||||
{
|
||||
static const bool sDebugUseCounters = false;
|
||||
if (mReportedUseCounters) {
|
||||
return;
|
||||
}
|
||||
|
||||
mReportedUseCounters = true;
|
||||
|
||||
if (Telemetry::HistogramUseCounterCount > 0 &&
|
||||
(IsContentDocument() || IsResourceDoc())) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
NodePrincipal()->GetURI(getter_AddRefs(uri));
|
||||
if (!uri || MightBeAboutOrChromeScheme(uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sDebugUseCounters) {
|
||||
nsCString spec;
|
||||
uri->GetSpec(spec);
|
||||
|
||||
// URIs can be rather long for data documents, so truncate them to
|
||||
// some reasonable length.
|
||||
spec.Truncate(std::min(128U, spec.Length()));
|
||||
printf("-- Use counters for %s --\n", spec.get());
|
||||
}
|
||||
|
||||
// We keep separate counts for individual documents and top-level
|
||||
// pages to more accurately track how many web pages might break if
|
||||
// certain features were removed. Consider the case of a single
|
||||
// HTML document with several SVG images and/or iframes with
|
||||
// sub-documents of their own. If we maintained a single set of use
|
||||
// counters and all the sub-documents use a particular feature, then
|
||||
// telemetry would indicate that we would be breaking N documents if
|
||||
// that feature were removed. Whereas with a document/top-level
|
||||
// page split, we can see that N documents would be affected, but
|
||||
// only a single web page would be affected.
|
||||
for (int32_t c = 0;
|
||||
c < eUseCounter_Count; ++c) {
|
||||
UseCounter uc = static_cast<UseCounter>(c);
|
||||
|
||||
Telemetry::ID id =
|
||||
static_cast<Telemetry::ID>(Telemetry::HistogramFirstUseCounter + uc * 2);
|
||||
bool value = GetUseCounter(uc);
|
||||
|
||||
if (sDebugUseCounters && value) {
|
||||
const char* name = Telemetry::GetHistogramName(id);
|
||||
if (name) {
|
||||
printf(" %s", name);
|
||||
} else {
|
||||
printf(" #%d", id);
|
||||
}
|
||||
printf(": %d\n", value);
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(id, value);
|
||||
|
||||
if (IsTopLevelContentDocument()) {
|
||||
id = static_cast<Telemetry::ID>(Telemetry::HistogramFirstUseCounter +
|
||||
uc * 2 + 1);
|
||||
value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc);
|
||||
|
||||
if (sDebugUseCounters && value) {
|
||||
const char* name = Telemetry::GetHistogramName(id);
|
||||
if (name) {
|
||||
printf(" %s", name);
|
||||
} else {
|
||||
printf(" #%d", id);
|
||||
}
|
||||
printf(": %d\n", value);
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(id, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XPathEvaluator*
|
||||
nsIDocument::XPathEvaluator()
|
||||
{
|
||||
|
||||
+15
-2
@@ -945,6 +945,8 @@ public:
|
||||
*/
|
||||
void OnAppThemeChanged();
|
||||
|
||||
void ReportUseCounters();
|
||||
|
||||
private:
|
||||
void AddOnDemandBuiltInUASheet(mozilla::CSSStyleSheet* aSheet);
|
||||
nsRadioGroupStruct* GetRadioGroupInternal(const nsAString& aName) const;
|
||||
@@ -1471,12 +1473,15 @@ public:
|
||||
|
||||
static void XPCOMShutdown();
|
||||
|
||||
bool mIsTopLevelContentDocument:1;
|
||||
bool mIsTopLevelContentDocument: 1;
|
||||
bool mIsContentDocument: 1;
|
||||
|
||||
bool IsTopLevelContentDocument();
|
||||
|
||||
void SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument);
|
||||
|
||||
bool IsContentDocument() const;
|
||||
void SetIsContentDocument(bool aIsContentDocument);
|
||||
|
||||
js::ExpandoAndGeneration mExpandoAndGeneration;
|
||||
|
||||
bool ContainsMSEContent();
|
||||
@@ -1682,6 +1687,14 @@ public:
|
||||
// The value is saturated to kPointerLockRequestLimit+1 = 3.
|
||||
uint8_t mCancelledPointerLockRequests:2;
|
||||
|
||||
// Whether we have reported use counters for this document with Telemetry yet.
|
||||
// Normally this is only done at document destruction time, but for image
|
||||
// documents (SVG documents) that are not guaranteed to be destroyed, we
|
||||
// report use counters when the image cache no longer has any imgRequestProxys
|
||||
// pointing to them. We track whether we ever reported use counters so
|
||||
// that we only report them once for the document.
|
||||
bool mReportedUseCounters:1;
|
||||
|
||||
uint8_t mXMLDeclarationBits;
|
||||
|
||||
nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject> *mBoxObjectTable;
|
||||
|
||||
+55
-2
@@ -27,6 +27,7 @@
|
||||
#include "mozilla/net/ReferrerPolicy.h" // for member
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/dom/DocumentBinding.h"
|
||||
#include "mozilla/UseCounter.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "Units.h"
|
||||
#include "nsExpirationTracker.h"
|
||||
@@ -1753,7 +1754,7 @@ public:
|
||||
|
||||
bool IsResourceDoc() const {
|
||||
return IsBeingUsedAsImage() || // Are we a helper-doc for an SVG image?
|
||||
!!mDisplayDocument; // Are we an external resource doc?
|
||||
mHasDisplayDocument; // Are we an external resource doc?
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1782,6 +1783,7 @@ public:
|
||||
NS_PRECONDITION(!aDisplayDocument->GetDisplayDocument(),
|
||||
"Display documents should not nest");
|
||||
mDisplayDocument = aDisplayDocument;
|
||||
mHasDisplayDocument = !!aDisplayDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2357,6 +2359,8 @@ public:
|
||||
eAttributeChanged
|
||||
};
|
||||
|
||||
nsIDocument* GetTopLevelContentDocument();
|
||||
|
||||
/**
|
||||
* Registers an unresolved custom element that is a candidate for
|
||||
* upgrade when the definition is registered via registerElement.
|
||||
@@ -2624,7 +2628,24 @@ public:
|
||||
mozilla::dom::FontFaceSet* Fonts();
|
||||
|
||||
bool DidFireDOMContentLoaded() const { return mDidFireDOMContentLoaded; }
|
||||
|
||||
|
||||
void SetDocumentUseCounter(mozilla::UseCounter aUseCounter)
|
||||
{
|
||||
if (!mUseCounters[aUseCounter]) {
|
||||
mUseCounters[aUseCounter] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SetPageUseCounter(mozilla::UseCounter aUseCounter);
|
||||
|
||||
void SetDocumentAndPageUseCounter(mozilla::UseCounter aUseCounter)
|
||||
{
|
||||
SetDocumentUseCounter(aUseCounter);
|
||||
SetPageUseCounter(aUseCounter);
|
||||
}
|
||||
|
||||
void PropagateUseCounters(nsIDocument* aParentDocument);
|
||||
|
||||
void SetUserHasInteracted(bool aUserHasInteracted)
|
||||
{
|
||||
mUserHasInteracted = aUserHasInteracted;
|
||||
@@ -2639,6 +2660,24 @@ public:
|
||||
|
||||
bool InlineScriptAllowedByCSP();
|
||||
|
||||
protected:
|
||||
bool GetUseCounter(mozilla::UseCounter aUseCounter)
|
||||
{
|
||||
return mUseCounters[aUseCounter];
|
||||
}
|
||||
|
||||
void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
|
||||
{
|
||||
if (!mChildDocumentUseCounters[aUseCounter]) {
|
||||
mChildDocumentUseCounters[aUseCounter] = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
|
||||
{
|
||||
return mChildDocumentUseCounters[aUseCounter];
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::bitset<eDeprecatedOperationCount> mDeprecationWarnedAbout;
|
||||
mutable std::bitset<eDocumentWarningCount> mDocWarningWarnedAbout;
|
||||
@@ -2878,6 +2917,12 @@ protected:
|
||||
bool mIsLinkUpdateRegistrationsForbidden;
|
||||
#endif
|
||||
|
||||
// Whether this document has a display document and thus is considered to
|
||||
// be a resource document. Normally this is the same as !!mDisplayDocument,
|
||||
// but mDisplayDocument is cleared during Unlink. mHasDisplayDocument is
|
||||
// valid in the document's destructor.
|
||||
bool mHasDisplayDocument : 1;
|
||||
|
||||
// Is the current mFontFaceSet valid?
|
||||
bool mFontFaceSetDirty;
|
||||
|
||||
@@ -3017,6 +3062,14 @@ protected:
|
||||
// Our live MediaQueryLists
|
||||
PRCList mDOMMediaQueryLists;
|
||||
|
||||
// Flags for use counters used directly by this document.
|
||||
std::bitset<mozilla::eUseCounter_Count> mUseCounters;
|
||||
// Flags for use counters used by any child documents of this document.
|
||||
std::bitset<mozilla::eUseCounter_Count> mChildDocumentUseCounters;
|
||||
// Flags for whether we've notified our top-level "page" of a use counter
|
||||
// for this child document.
|
||||
std::bitset<mozilla::eUseCounter_Count> mNotifiedPageForUseCounter;
|
||||
|
||||
// Whether the user has interacted with the document or not:
|
||||
bool mUserHasInteracted;
|
||||
};
|
||||
|
||||
@@ -90,7 +90,6 @@ nsImageLoadingContent::nsImageLoadingContent()
|
||||
mBroken(true),
|
||||
mUserDisabled(false),
|
||||
mSuppressed(false),
|
||||
mFireEventsOnDecode(false),
|
||||
mNewRequestsWillNeedAnimationReset(false),
|
||||
mStateChangerDepth(0),
|
||||
mCurrentRequestRegistered(false),
|
||||
@@ -200,16 +199,10 @@ nsImageLoadingContent::Notify(imgIRequest* aRequest,
|
||||
}
|
||||
|
||||
if (aType == imgINotificationObserver::DECODE_COMPLETE) {
|
||||
if (mFireEventsOnDecode) {
|
||||
mFireEventsOnDecode = false;
|
||||
|
||||
uint32_t reqStatus;
|
||||
aRequest->GetImageStatus(&reqStatus);
|
||||
if (reqStatus & imgIRequest::STATUS_ERROR) {
|
||||
FireEvent(NS_LITERAL_STRING("error"));
|
||||
} else {
|
||||
FireEvent(NS_LITERAL_STRING("load"));
|
||||
}
|
||||
nsCOMPtr<imgIContainer> container;
|
||||
aRequest->GetImage(getter_AddRefs(container));
|
||||
if (container) {
|
||||
container->PropagateUseCounters(GetOurOwnerDoc());
|
||||
}
|
||||
|
||||
UpdateImageState(true);
|
||||
@@ -291,23 +284,11 @@ nsImageLoadingContent::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
|
||||
}
|
||||
}
|
||||
|
||||
// We want to give the decoder a chance to find errors. If we haven't found
|
||||
// an error yet and we've started decoding, either from the above
|
||||
// StartDecoding or from some other place, we must only fire these events
|
||||
// after we finish decoding.
|
||||
uint32_t reqStatus;
|
||||
aRequest->GetImageStatus(&reqStatus);
|
||||
if (NS_SUCCEEDED(aStatus) && !(reqStatus & imgIRequest::STATUS_ERROR) &&
|
||||
(reqStatus & imgIRequest::STATUS_DECODE_STARTED) &&
|
||||
!(reqStatus & imgIRequest::STATUS_DECODE_COMPLETE)) {
|
||||
mFireEventsOnDecode = true;
|
||||
// Fire the appropriate DOM event.
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
FireEvent(NS_LITERAL_STRING("load"));
|
||||
} else {
|
||||
// Fire the appropriate DOM event.
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
FireEvent(NS_LITERAL_STRING("load"));
|
||||
} else {
|
||||
FireEvent(NS_LITERAL_STRING("error"));
|
||||
}
|
||||
FireEvent(NS_LITERAL_STRING("error"));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
|
||||
@@ -316,6 +297,25 @@ nsImageLoadingContent::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
ImageIsAnimated(imgIRequest* aRequest)
|
||||
{
|
||||
if (!aRequest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
|
||||
bool isAnimated = false;
|
||||
nsresult rv = image->GetAnimated(&isAnimated);
|
||||
if (NS_SUCCEEDED(rv) && isAnimated) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::OnUnlockedDraw()
|
||||
{
|
||||
@@ -324,6 +324,15 @@ nsImageLoadingContent::OnUnlockedDraw()
|
||||
return;
|
||||
}
|
||||
|
||||
// It's OK for non-animated images to wait until the next image visibility
|
||||
// update to become locked. (And that's preferable, since in the case of
|
||||
// scrolling it keeps memory usage minimal.) For animated images, though, we
|
||||
// want to mark them visible right away so we can call
|
||||
// IncrementAnimationConsumers() on them and they'll start animating.
|
||||
if (!ImageIsAnimated(mCurrentRequest) && !ImageIsAnimated(mPendingRequest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = GetFramePresContext();
|
||||
if (!presContext)
|
||||
return;
|
||||
@@ -942,29 +951,25 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
|
||||
}
|
||||
|
||||
// get document wide referrer policy
|
||||
mozilla::net::ReferrerPolicy referrerPolicy = aDocument->GetReferrerPolicy();
|
||||
bool referrerAttributeEnabled = Preferences::GetBool("network.http.enablePerElementReferrer", false);
|
||||
// if referrer attributes are enabled in preferences, load img referrer attribute
|
||||
nsresult rv;
|
||||
if (referrerAttributeEnabled) {
|
||||
mozilla::net::ReferrerPolicy imgReferrerPolicy = GetImageReferrerPolicy();
|
||||
// if the image does not provide a referrer attribute, ignore this
|
||||
if (imgReferrerPolicy != mozilla::net::RP_Unset) {
|
||||
referrerPolicy = imgReferrerPolicy;
|
||||
}
|
||||
// if the image does not provide a referrer attribute, ignore this
|
||||
net::ReferrerPolicy referrerPolicy = aDocument->GetReferrerPolicy();
|
||||
net::ReferrerPolicy imgReferrerPolicy = GetImageReferrerPolicy();
|
||||
if (imgReferrerPolicy != net::RP_Unset) {
|
||||
referrerPolicy = imgReferrerPolicy;
|
||||
}
|
||||
|
||||
// Not blocked. Do the load.
|
||||
nsRefPtr<imgRequestProxy>& req = PrepareNextRequest(aImageLoadType);
|
||||
nsIContent* content = AsContent();
|
||||
rv = nsContentUtils::LoadImage(aNewURI, aDocument,
|
||||
aDocument->NodePrincipal(),
|
||||
aDocument->GetDocumentURI(),
|
||||
referrerPolicy,
|
||||
this, loadFlags,
|
||||
content->LocalName(),
|
||||
getter_AddRefs(req),
|
||||
policyType);
|
||||
nsresult rv = nsContentUtils::LoadImage(aNewURI, aDocument,
|
||||
aDocument->NodePrincipal(),
|
||||
aDocument->GetDocumentURI(),
|
||||
referrerPolicy,
|
||||
this, loadFlags,
|
||||
content->LocalName(),
|
||||
getter_AddRefs(req),
|
||||
policyType);
|
||||
|
||||
// Tell the document to forget about the image preload, if any, for
|
||||
// this URI, now that we might have another imgRequestProxy for it.
|
||||
|
||||
@@ -424,7 +424,6 @@ private:
|
||||
bool mBroken : 1;
|
||||
bool mUserDisabled : 1;
|
||||
bool mSuppressed : 1;
|
||||
bool mFireEventsOnDecode : 1;
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
||||
@@ -315,8 +315,10 @@ private:
|
||||
nsScriptObjectTracer* aTracer);
|
||||
|
||||
#ifdef DEBUG
|
||||
public:
|
||||
void CheckCCWrapperTraversal(void* aScriptObjectHolder,
|
||||
nsScriptObjectTracer* aTracer);
|
||||
private:
|
||||
#endif // DEBUG
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,9 @@ def generate_histograms(filename):
|
||||
append_counters(method.replace('.', '_').upper(), 'called %s' % method)
|
||||
elif counter['type'] == 'attribute':
|
||||
attr = '%s.%s' % (counter['interface_name'], counter['attribute_name'])
|
||||
append_counters(attr.replace('.', '_').upper(), 'got or set %s' % attr)
|
||||
counter_name = attr.replace('.', '_').upper()
|
||||
append_counters('%s_getter' % counter_name, 'got %s' % attr)
|
||||
append_counters('%s_setter' % counter_name, 'set %s' % attr)
|
||||
elif counter['type'] == 'property':
|
||||
prop = counter['property_name']
|
||||
append_counters('PROPERTY_%s' % prop.replace('-', '_').upper(), "used the '%s' property" % prop)
|
||||
|
||||
+222
-19
@@ -14,6 +14,7 @@
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/UseCounter.h"
|
||||
|
||||
#include "AccessCheck.h"
|
||||
#include "jsfriendapi.h"
|
||||
@@ -30,6 +31,7 @@
|
||||
#include "XrayWrapper.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "prprf.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
@@ -46,6 +48,7 @@
|
||||
#include "WorkerPrivate.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "ipc/ErrorIPCUtils.h"
|
||||
#include "mozilla/UseCounter.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -289,17 +292,6 @@ ErrorResult::StealJSException(JSContext* cx,
|
||||
mResult = NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ErrorResult::ReportNotEnoughArgsError(JSContext* cx,
|
||||
const char* ifaceName,
|
||||
const char* memberName)
|
||||
{
|
||||
MOZ_ASSERT(ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS);
|
||||
|
||||
nsPrintfCString errorMessage("%s.%s", ifaceName, memberName);
|
||||
ThrowErrorMessage(cx, dom::MSG_MISSING_ARGUMENTS, errorMessage.get());
|
||||
}
|
||||
|
||||
void
|
||||
ErrorResult::ReportGenericError(JSContext* cx)
|
||||
{
|
||||
@@ -978,6 +970,18 @@ ThrowConstructorWithoutNew(JSContext* cx, const char* name)
|
||||
return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name);
|
||||
}
|
||||
|
||||
inline const NativePropertyHooks*
|
||||
GetNativePropertyHooksFromConstructorFunction(JS::Handle<JSObject*> obj)
|
||||
{
|
||||
MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
|
||||
const JS::Value& v =
|
||||
js::GetFunctionNativeReserved(obj,
|
||||
CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
|
||||
const JSNativeHolder* nativeHolder =
|
||||
static_cast<const JSNativeHolder*>(v.toPrivate());
|
||||
return nativeHolder->mPropertyHooks;
|
||||
}
|
||||
|
||||
inline const NativePropertyHooks*
|
||||
GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
|
||||
DOMObjectType& type)
|
||||
@@ -992,14 +996,8 @@ GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
|
||||
}
|
||||
|
||||
if (JS_ObjectIsFunction(cx, obj)) {
|
||||
MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
|
||||
type = eInterface;
|
||||
const JS::Value& v =
|
||||
js::GetFunctionNativeReserved(obj,
|
||||
CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
|
||||
const JSNativeHolder* nativeHolder =
|
||||
static_cast<const JSNativeHolder*>(v.toPrivate());
|
||||
return nativeHolder->mPropertyHooks;
|
||||
return GetNativePropertyHooksFromConstructorFunction(obj);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj)));
|
||||
@@ -2635,7 +2633,7 @@ ConvertExceptionToPromise(JSContext* cx,
|
||||
if (rv.Failed()) {
|
||||
// We just give up. Make sure to not leak memory on the
|
||||
// ErrorResult, but then just put the original exception back.
|
||||
ThrowMethodFailedWithDetails(cx, rv, "", "");
|
||||
ThrowMethodFailed(cx, rv);
|
||||
JS_SetPendingException(cx, exn);
|
||||
return false;
|
||||
}
|
||||
@@ -2794,5 +2792,210 @@ SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj)
|
||||
ResolveSystemBinding(cx, obj, JSID_VOIDHANDLE, &ignored);
|
||||
}
|
||||
|
||||
template<decltype(JS::NewMapObject) Method>
|
||||
bool
|
||||
GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated)
|
||||
{
|
||||
JS::Rooted<JSObject*> reflector(aCx);
|
||||
reflector = IsDOMObject(aObj) ? aObj : js::UncheckedUnwrap(aObj,
|
||||
/* stopAtOuter = */ false);
|
||||
|
||||
// Retrieve the backing object from the reserved slot on the maplike/setlike
|
||||
// object. If it doesn't exist yet, create it.
|
||||
JS::Rooted<JS::Value> slotValue(aCx);
|
||||
slotValue = js::GetReservedSlot(reflector, aSlotIndex);
|
||||
if (slotValue.isUndefined()) {
|
||||
// Since backing object access can happen in non-originating compartments,
|
||||
// make sure to create the backing object in reflector compartment.
|
||||
{
|
||||
JSAutoCompartment ac(aCx, reflector);
|
||||
JS::Rooted<JSObject*> newBackingObj(aCx);
|
||||
newBackingObj.set(Method(aCx));
|
||||
if (NS_WARN_IF(!newBackingObj)) {
|
||||
return false;
|
||||
}
|
||||
js::SetReservedSlot(reflector, aSlotIndex, JS::ObjectValue(*newBackingObj));
|
||||
}
|
||||
slotValue = js::GetReservedSlot(reflector, aSlotIndex);
|
||||
*aBackingObjCreated = true;
|
||||
} else {
|
||||
*aBackingObjCreated = false;
|
||||
}
|
||||
if (!MaybeWrapNonDOMObjectValue(aCx, &slotValue)) {
|
||||
return false;
|
||||
}
|
||||
aBackingObj.set(&slotValue.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated)
|
||||
{
|
||||
return GetMaplikeSetlikeBackingObject<JS::NewMapObject>(aCx, aObj, aSlotIndex,
|
||||
aBackingObj,
|
||||
aBackingObjCreated);
|
||||
}
|
||||
|
||||
bool
|
||||
GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated)
|
||||
{
|
||||
return GetMaplikeSetlikeBackingObject<JS::NewSetObject>(aCx, aObj, aSlotIndex,
|
||||
aBackingObj,
|
||||
aBackingObjCreated);
|
||||
}
|
||||
|
||||
bool
|
||||
ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
|
||||
// Unpack callback and object from slots
|
||||
JS::Rooted<JS::Value>
|
||||
callbackFn(aCx, js::GetFunctionNativeReserved(&args.callee(),
|
||||
FOREACH_CALLBACK_SLOT));
|
||||
JS::Rooted<JS::Value>
|
||||
maplikeOrSetlikeObj(aCx,
|
||||
js::GetFunctionNativeReserved(&args.callee(),
|
||||
FOREACH_MAPLIKEORSETLIKEOBJ_SLOT));
|
||||
MOZ_ASSERT(aArgc == 3);
|
||||
JS::AutoValueVector newArgs(aCx);
|
||||
// Arguments are passed in as value, key, object. Keep value and key, replace
|
||||
// object with the maplike/setlike object.
|
||||
newArgs.append(args.get(0));
|
||||
newArgs.append(args.get(1));
|
||||
newArgs.append(maplikeOrSetlikeObj);
|
||||
JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue());
|
||||
// Now actually call the user specified callback
|
||||
return JS::Call(aCx, args.thisv(), callbackFn, newArgs, &rval);
|
||||
}
|
||||
|
||||
static inline prototypes::ID
|
||||
GetProtoIdForNewtarget(JS::Handle<JSObject*> aNewTarget)
|
||||
{
|
||||
const js::Class* newTargetClass = js::GetObjectClass(aNewTarget);
|
||||
if (IsDOMIfaceAndProtoClass(newTargetClass)) {
|
||||
const DOMIfaceAndProtoJSClass* newTargetIfaceClass =
|
||||
DOMIfaceAndProtoJSClass::FromJSClass(newTargetClass);
|
||||
if (newTargetIfaceClass->mType == eInterface) {
|
||||
return newTargetIfaceClass->mPrototypeID;
|
||||
}
|
||||
} else if (JS_IsNativeFunction(aNewTarget, Constructor)) {
|
||||
return GetNativePropertyHooksFromConstructorFunction(aNewTarget)->mPrototypeID;
|
||||
}
|
||||
|
||||
return prototypes::id::_ID_Count;
|
||||
}
|
||||
|
||||
bool
|
||||
GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
|
||||
JS::MutableHandle<JSObject*> aDesiredProto)
|
||||
{
|
||||
if (!aCallArgs.isConstructing()) {
|
||||
aDesiredProto.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The desired prototype depends on the actual constructor that was invoked,
|
||||
// which is passed to us as the newTarget in the callargs. We want to do
|
||||
// something akin to the ES6 specification's GetProtototypeFromConstructor (so
|
||||
// get .prototype on the newTarget, with a fallback to some sort of default).
|
||||
|
||||
// First, a fast path for the case when the the constructor is in fact one of
|
||||
// our DOM constructors. This is safe because on those the "constructor"
|
||||
// property is non-configurable and non-writable, so we don't have to do the
|
||||
// slow JS_GetProperty call.
|
||||
JS::Rooted<JSObject*> newTarget(aCx, &aCallArgs.newTarget().toObject());
|
||||
JS::Rooted<JSObject*> originalNewTarget(aCx, newTarget);
|
||||
// See whether we have a known DOM constructor here, such that we can take a
|
||||
// fast path.
|
||||
prototypes::ID protoID = GetProtoIdForNewtarget(newTarget);
|
||||
if (protoID == prototypes::id::_ID_Count) {
|
||||
// We might still have a cross-compartment wrapper for a known DOM
|
||||
// constructor.
|
||||
newTarget = js::CheckedUnwrap(newTarget);
|
||||
if (newTarget && newTarget != originalNewTarget) {
|
||||
protoID = GetProtoIdForNewtarget(newTarget);
|
||||
}
|
||||
}
|
||||
|
||||
if (protoID != prototypes::id::_ID_Count) {
|
||||
ProtoAndIfaceCache& protoAndIfaceCache =
|
||||
*GetProtoAndIfaceCache(js::GetGlobalForObjectCrossCompartment(newTarget));
|
||||
aDesiredProto.set(protoAndIfaceCache.EntrySlotMustExist(protoID));
|
||||
if (newTarget != originalNewTarget) {
|
||||
return JS_WrapObject(aCx, aDesiredProto);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Slow path. This basically duplicates the ES6 spec's
|
||||
// GetPrototypeFromConstructor except that instead of taking a string naming
|
||||
// the fallback prototype we just fall back to using null and assume that our
|
||||
// caller will then pick the right default. The actual defaulting behavior
|
||||
// here still needs to be defined in the Web IDL specification.
|
||||
//
|
||||
// Note that it's very important to do this property get on originalNewTarget,
|
||||
// not our unwrapped newTarget, since we want to get Xray behavior here as
|
||||
// needed.
|
||||
// XXXbz for speed purposes, using a preinterned id here sure would be nice.
|
||||
JS::Rooted<JS::Value> protoVal(aCx);
|
||||
if (!JS_GetProperty(aCx, originalNewTarget, "prototype", &protoVal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!protoVal.isObject()) {
|
||||
aDesiredProto.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
aDesiredProto.set(&protoVal.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
namespace binding_detail {
|
||||
void
|
||||
AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
if (!aGivenProto) {
|
||||
// Nothing to assert here
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> reflector(aCx, aReflector);
|
||||
JSAutoCompartment ac(aCx, reflector);
|
||||
JS::Rooted<JSObject*> reflectorProto(aCx);
|
||||
bool ok = JS_GetPrototype(aCx, reflector, &reflectorProto);
|
||||
MOZ_ASSERT(ok);
|
||||
// aGivenProto may not be in the right compartment here, so we
|
||||
// have to wrap it to compare.
|
||||
JS::Rooted<JSObject*> givenProto(aCx, aGivenProto);
|
||||
ok = JS_WrapObject(aCx, &givenProto);
|
||||
MOZ_ASSERT(ok);
|
||||
MOZ_ASSERT(givenProto == reflectorProto,
|
||||
"How are we supposed to change the proto now?");
|
||||
}
|
||||
} // namespace binding_detail
|
||||
#endif // DEBUG
|
||||
|
||||
void
|
||||
SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
|
||||
UseCounter aUseCounter)
|
||||
{
|
||||
nsGlobalWindow* win = xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject));
|
||||
if (win && win->GetDocument()) {
|
||||
win->GetDocument()->SetDocumentAndPageUseCounter(aUseCounter);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
+135
-32
@@ -43,6 +43,9 @@ class nsIJSID;
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
enum UseCounter : int16_t;
|
||||
|
||||
namespace dom {
|
||||
template<typename DataType> class MozMap;
|
||||
|
||||
@@ -99,9 +102,7 @@ ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
|
||||
prototypes::ID aProtoId);
|
||||
|
||||
inline bool
|
||||
ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv,
|
||||
const char* ifaceName,
|
||||
const char* memberName)
|
||||
ThrowMethodFailed(JSContext* cx, ErrorResult& rv)
|
||||
{
|
||||
if (rv.IsUncatchableException()) {
|
||||
// Nuke any existing exception on aCx, to make sure we're uncatchable.
|
||||
@@ -118,10 +119,6 @@ ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv,
|
||||
rv.ReportJSException(cx);
|
||||
return false;
|
||||
}
|
||||
if (rv.IsNotEnoughArgsError()) {
|
||||
rv.ReportNotEnoughArgsError(cx, ifaceName, memberName);
|
||||
return false;
|
||||
}
|
||||
rv.ReportGenericError(cx);
|
||||
return false;
|
||||
}
|
||||
@@ -908,30 +905,95 @@ struct TypeNeedsOuterization
|
||||
IsBaseOf<nsGlobalWindow, T>::value || IsSame<EventTarget, T>::value;
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value>
|
||||
struct CheckWrapperCacheTracing
|
||||
{
|
||||
static inline void Check(T* aObject)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct CheckWrapperCacheTracing<T, true>
|
||||
{
|
||||
static void Check(T* aObject)
|
||||
{
|
||||
// Rooting analysis thinks QueryInterface may GC, but we're dealing with
|
||||
// a subset of QueryInterface, C++ only types here.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
nsWrapperCache* wrapperCacheFromQI = nullptr;
|
||||
aObject->QueryInterface(NS_GET_IID(nsWrapperCache),
|
||||
reinterpret_cast<void**>(&wrapperCacheFromQI));
|
||||
|
||||
MOZ_ASSERT(wrapperCacheFromQI,
|
||||
"Missing nsWrapperCache from QueryInterface implementation?");
|
||||
|
||||
if (!wrapperCacheFromQI->GetWrapperPreserveColor()) {
|
||||
// Can't assert that we trace the wrapper, since we don't have any
|
||||
// wrapper to trace.
|
||||
return;
|
||||
}
|
||||
|
||||
nsISupports* ccISupports = nullptr;
|
||||
aObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
|
||||
reinterpret_cast<void**>(&ccISupports));
|
||||
MOZ_ASSERT(ccISupports,
|
||||
"nsWrapperCache object which isn't cycle collectable?");
|
||||
|
||||
nsXPCOMCycleCollectionParticipant* participant = nullptr;
|
||||
CallQueryInterface(ccISupports, &participant);
|
||||
MOZ_ASSERT(participant, "Can't QI to CycleCollectionParticipant?");
|
||||
|
||||
bool wasPreservingWrapper = wrapperCacheFromQI->PreservingWrapper();
|
||||
wrapperCacheFromQI->SetPreservingWrapper(true);
|
||||
wrapperCacheFromQI->CheckCCWrapperTraversal(ccISupports, participant);
|
||||
wrapperCacheFromQI->SetPreservingWrapper(wasPreservingWrapper);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
|
||||
JS::Handle<JSObject*> aGivenProto);
|
||||
#endif // DEBUG
|
||||
|
||||
template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
DoGetOrCreateDOMReflector(JSContext* cx, T* value,
|
||||
JS::Handle<JSObject*> givenProto,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
{
|
||||
MOZ_ASSERT(value);
|
||||
JSObject* obj = value->GetWrapperPreserveColor();
|
||||
// We can get rid of this when we remove support for hasXPConnectImpls.
|
||||
bool couldBeDOMBinding = CouldBeDOMBinding(value);
|
||||
JSObject* obj = value->GetWrapper();
|
||||
if (obj) {
|
||||
JS::ExposeObjectToActiveJS(obj);
|
||||
#ifdef DEBUG
|
||||
AssertReflectorHasGivenProto(cx, obj, givenProto);
|
||||
// Have to reget obj because AssertReflectorHasGivenProto can
|
||||
// trigger gc so the pointer may now be invalid.
|
||||
obj = value->GetWrapper();
|
||||
#endif
|
||||
} else {
|
||||
// Inline this here while we have non-dom objects in wrapper caches.
|
||||
if (!couldBeDOMBinding) {
|
||||
return false;
|
||||
}
|
||||
|
||||
obj = value->WrapObject(cx, nullptr);
|
||||
obj = value->WrapObject(cx, givenProto);
|
||||
if (!obj) {
|
||||
// At this point, obj is null, so just return false.
|
||||
// Callers seem to be testing JS_IsExceptionPending(cx) to
|
||||
// figure out whether WrapObject() threw.
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (IsBaseOf<nsWrapperCache, T>::value) {
|
||||
CheckWrapperCacheTracing<T>::Check(value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -984,10 +1046,12 @@ DoGetOrCreateDOMReflector(JSContext* cx, T* value,
|
||||
template <class T>
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
GetOrCreateDOMReflector(JSContext* cx, T* value,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
JS::MutableHandle<JS::Value> rval,
|
||||
JS::Handle<JSObject*> givenProto = nullptr)
|
||||
{
|
||||
using namespace binding_detail;
|
||||
return DoGetOrCreateDOMReflector<T, eWrapIntoContextCompartment>(cx, value,
|
||||
givenProto,
|
||||
rval);
|
||||
}
|
||||
|
||||
@@ -1001,6 +1065,7 @@ GetOrCreateDOMReflectorNoWrap(JSContext* cx, T* value,
|
||||
using namespace binding_detail;
|
||||
return DoGetOrCreateDOMReflector<T, eDontWrapIntoContextCompartment>(cx,
|
||||
value,
|
||||
nullptr,
|
||||
rval);
|
||||
}
|
||||
|
||||
@@ -1012,7 +1077,8 @@ inline bool
|
||||
WrapNewBindingNonWrapperCachedObject(JSContext* cx,
|
||||
JS::Handle<JSObject*> scopeArg,
|
||||
T* value,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
JS::MutableHandle<JS::Value> rval,
|
||||
JS::Handle<JSObject*> givenProto = nullptr)
|
||||
{
|
||||
static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
|
||||
MOZ_ASSERT(value);
|
||||
@@ -1026,15 +1092,19 @@ WrapNewBindingNonWrapperCachedObject(JSContext* cx,
|
||||
// more Maybe (one for a Rooted and one for a Handle) adds more
|
||||
// code (and branches!) than just adding a single rooted.
|
||||
JS::Rooted<JSObject*> scope(cx, scopeArg);
|
||||
JS::Rooted<JSObject*> proto(cx, givenProto);
|
||||
if (js::IsWrapper(scope)) {
|
||||
scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false);
|
||||
if (!scope)
|
||||
return false;
|
||||
ac.emplace(cx, scope);
|
||||
if (!JS_WrapObject(cx, &proto)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
|
||||
if (!value->WrapObject(cx, nullptr, &obj)) {
|
||||
if (!value->WrapObject(cx, proto, &obj)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1054,7 +1124,8 @@ inline bool
|
||||
WrapNewBindingNonWrapperCachedObject(JSContext* cx,
|
||||
JS::Handle<JSObject*> scopeArg,
|
||||
nsAutoPtr<T>& value,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
JS::MutableHandle<JS::Value> rval,
|
||||
JS::Handle<JSObject*> givenProto = nullptr)
|
||||
{
|
||||
static_assert(!IsRefcounted<T>::value, "Only pass owned classes in here.");
|
||||
// We do a runtime check on value, because otherwise we might in
|
||||
@@ -1072,15 +1143,19 @@ WrapNewBindingNonWrapperCachedObject(JSContext* cx,
|
||||
// more Maybe (one for a Rooted and one for a Handle) adds more
|
||||
// code (and branches!) than just adding a single rooted.
|
||||
JS::Rooted<JSObject*> scope(cx, scopeArg);
|
||||
JS::Rooted<JSObject*> proto(cx, givenProto);
|
||||
if (js::IsWrapper(scope)) {
|
||||
scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false);
|
||||
if (!scope)
|
||||
return false;
|
||||
ac.emplace(cx, scope);
|
||||
if (!JS_WrapObject(cx, &proto)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
|
||||
if (!value->WrapObject(cx, nullptr, &obj)) {
|
||||
if (!value->WrapObject(cx, proto, &obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1099,9 +1174,11 @@ template <template <typename> class SmartPtr, typename T,
|
||||
inline bool
|
||||
WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
|
||||
const SmartPtr<T>& value,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
JS::MutableHandle<JS::Value> rval,
|
||||
JS::Handle<JSObject*> givenProto = nullptr)
|
||||
{
|
||||
return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval);
|
||||
return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval,
|
||||
givenProto);
|
||||
}
|
||||
|
||||
// Only set allowNativeWrapper to false if you really know you need it, if in
|
||||
@@ -1621,9 +1698,10 @@ template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
|
||||
struct GetOrCreateDOMReflectorHelper
|
||||
{
|
||||
static inline bool GetOrCreate(JSContext* cx, const T& value,
|
||||
JS::Handle<JSObject*> givenProto,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
{
|
||||
return GetOrCreateDOMReflector(cx, value.get(), rval);
|
||||
return GetOrCreateDOMReflector(cx, value.get(), rval, givenProto);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1631,30 +1709,22 @@ template <class T>
|
||||
struct GetOrCreateDOMReflectorHelper<T, false>
|
||||
{
|
||||
static inline bool GetOrCreate(JSContext* cx, T& value,
|
||||
JS::Handle<JSObject*> givenProto,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
{
|
||||
static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
|
||||
return GetOrCreateDOMReflector(cx, &value, rval);
|
||||
return GetOrCreateDOMReflector(cx, &value, rval, givenProto);
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
inline bool
|
||||
GetOrCreateDOMReflector(JSContext* cx, T& value,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
JS::MutableHandle<JS::Value> rval,
|
||||
JS::Handle<JSObject*> givenProto = nullptr)
|
||||
{
|
||||
return GetOrCreateDOMReflectorHelper<T>::GetOrCreate(cx, value, rval);
|
||||
}
|
||||
|
||||
// We need this version of GetOrCreateDOMReflector for codegen, so it'll have
|
||||
// the same signature as WrapNewBindingNonWrapperCachedObject and
|
||||
// WrapNewBindingNonWrapperCachedOwnedObject, which still need the scope.
|
||||
template<class T>
|
||||
inline bool
|
||||
GetOrCreateDOMReflector(JSContext* cx, JS::Handle<JSObject*> scope, T& value,
|
||||
JS::MutableHandle<JS::Value> rval)
|
||||
{
|
||||
return GetOrCreateDOMReflector(cx, value, rval);
|
||||
return GetOrCreateDOMReflectorHelper<T>::GetOrCreate(cx, value, givenProto,
|
||||
rval);
|
||||
}
|
||||
|
||||
// Helper for calling GetOrCreateDOMReflectorNoWrap with smart pointers
|
||||
@@ -3258,6 +3328,39 @@ bool SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
// thrown.
|
||||
bool SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj);
|
||||
|
||||
// Slot indexes for maplike/setlike forEach functions
|
||||
#define FOREACH_CALLBACK_SLOT 0
|
||||
#define FOREACH_MAPLIKEORSETLIKEOBJ_SLOT 1
|
||||
|
||||
// Backing function for running .forEach() on maplike/setlike interfaces.
|
||||
// Unpacks callback and maplike/setlike object from reserved slots, then runs
|
||||
// callback for each key (and value, for maplikes)
|
||||
bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
|
||||
|
||||
// Unpacks backing object (ES6 map/set) from the reserved slot of a reflector
|
||||
// for a maplike/setlike interface. If backing object does not exist, creates
|
||||
// backing object in the compartment of the reflector involved, making this safe
|
||||
// to use across compartments/via xrays. Return values of these methods will
|
||||
// always be in the context compartment.
|
||||
bool GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated);
|
||||
bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
size_t aSlotIndex,
|
||||
JS::MutableHandle<JSObject*> aBackingObj,
|
||||
bool* aBackingObjCreated);
|
||||
|
||||
// Get the desired prototype object for an object construction from the given
|
||||
// CallArgs. Null is returned if the default prototype should be used.
|
||||
bool
|
||||
GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
|
||||
JS::MutableHandle<JSObject*> aDesiredProto);
|
||||
|
||||
void
|
||||
SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
|
||||
UseCounter aUseCounter);
|
||||
|
||||
// A callback to perform funToString on an interface object
|
||||
JSString*
|
||||
InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
|
||||
|
||||
+730
-52
File diff suppressed because it is too large
Load Diff
@@ -110,12 +110,6 @@ public:
|
||||
void ReportJSException(JSContext* cx);
|
||||
bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; }
|
||||
|
||||
void ThrowNotEnoughArgsError() { mResult = NS_ERROR_XPC_NOT_ENOUGH_ARGS; }
|
||||
void ReportNotEnoughArgsError(JSContext* cx,
|
||||
const char* ifaceName,
|
||||
const char* memberName);
|
||||
bool IsNotEnoughArgsError() const { return ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS; }
|
||||
|
||||
// Report a generic error. This should only be used if we're not
|
||||
// some more specific exception type.
|
||||
void ReportGenericError(JSContext* cx);
|
||||
@@ -189,8 +183,7 @@ private:
|
||||
MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
|
||||
MOZ_ASSERT(aRv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
|
||||
MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
|
||||
MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()");
|
||||
MOZ_ASSERT(!IsNotEnoughArgsError(), "Don't overwrite not enough args error");
|
||||
MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "May need to bring back ThrowNotEnoughArgsError");
|
||||
mResult = aRv;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ ToJSValue(JSContext* aCx,
|
||||
MOZ_ASSERT(!aArgument.IsUncatchableException(),
|
||||
"Doesn't make sense to convert uncatchable exception to a JS value!");
|
||||
AutoForceSetExceptionOnContext forceExn(aCx);
|
||||
DebugOnly<bool> throwResult = ThrowMethodFailedWithDetails(aCx, aArgument, "", "");
|
||||
DebugOnly<bool> throwResult = ThrowMethodFailed(aCx, aArgument);
|
||||
MOZ_ASSERT(!throwResult);
|
||||
DebugOnly<bool> getPendingResult = JS_GetPendingException(aCx, aValue);
|
||||
MOZ_ASSERT(getPendingResult);
|
||||
|
||||
@@ -218,6 +218,15 @@ ToJSValue(JSContext* aCx,
|
||||
return ToJSValue(aCx, *aArgument.get(), aValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
ToJSValue(JSContext* aCx,
|
||||
const NonNull<T>& aArgument,
|
||||
JS::MutableHandle<JS::Value> aValue)
|
||||
{
|
||||
return ToJSValue(aCx, *aArgument.get(), aValue);
|
||||
}
|
||||
|
||||
// Accept WebIDL dictionaries
|
||||
template <class T>
|
||||
MOZ_WARN_UNUSED_RESULT
|
||||
|
||||
@@ -84,6 +84,25 @@ SOURCES += [
|
||||
'StructuredClone.cpp',
|
||||
]
|
||||
|
||||
# Tests for maplike and setlike require bindings to be built, which means they
|
||||
# must be included in libxul. This breaks the "no test classes are exported"
|
||||
# rule stated in the test/ directory, but it's the only way this will work.
|
||||
# Test classes are only built in debug mode, and all tests requiring use of
|
||||
# them are only run in debug mode.
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
EXPORTS.mozilla.dom += [
|
||||
"test/TestInterfaceMaplike.h",
|
||||
"test/TestInterfaceMaplikeObject.h",
|
||||
"test/TestInterfaceSetlike.h",
|
||||
"test/TestInterfaceSetlikeNode.h"
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
"test/TestInterfaceMaplike.cpp",
|
||||
"test/TestInterfaceMaplikeObject.cpp",
|
||||
"test/TestInterfaceSetlike.cpp",
|
||||
"test/TestInterfaceSetlikeNode.cpp",
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']:
|
||||
|
||||
+421
-33
@@ -341,7 +341,10 @@ class IDLUnresolvedIdentifier(IDLObject):
|
||||
|
||||
assert len(name) > 0
|
||||
|
||||
if name[:2] == "__" and name != "__content" and name != "___noSuchMethod__" and not allowDoubleUnderscore:
|
||||
if name == "__noSuchMethod__":
|
||||
raise WebIDLError("__noSuchMethod__ is deprecated", [location])
|
||||
|
||||
if name[:2] == "__" and name != "__content" and not allowDoubleUnderscore:
|
||||
raise WebIDLError("Identifiers beginning with __ are reserved",
|
||||
[location])
|
||||
if name[0] == '_' and not allowDoubleUnderscore:
|
||||
@@ -616,6 +619,7 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
self._callback = False
|
||||
self._finished = False
|
||||
self.members = []
|
||||
self.maplikeOrSetlike = None
|
||||
self._partialInterfaces = []
|
||||
self._extendedAttrDict = {}
|
||||
# namedConstructors needs deterministic ordering because bindings code
|
||||
@@ -689,6 +693,27 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
self.addExtendedAttributes(partial.propagatedExtendedAttrs)
|
||||
self.members.extend(partial.members)
|
||||
|
||||
# Generate maplike/setlike interface members. Since generated members
|
||||
# need to be treated like regular interface members, do this before
|
||||
# things like exposure setting.
|
||||
for member in self.members:
|
||||
if member.isMaplikeOrSetlike():
|
||||
# Check that we only have one interface declaration (currently
|
||||
# there can only be one maplike/setlike declaration per
|
||||
# interface)
|
||||
if self.maplikeOrSetlike:
|
||||
raise WebIDLError("%s declaration used on "
|
||||
"interface that already has %s "
|
||||
"declaration" %
|
||||
(member.maplikeOrSetlikeType,
|
||||
self.maplikeOrSetlike.maplikeOrSetlikeType),
|
||||
[self.maplikeOrSetlike.location,
|
||||
member.location])
|
||||
self.maplikeOrSetlike = member
|
||||
# If we've got a maplike or setlike declaration, we'll be building all of
|
||||
# our required methods in Codegen. Generate members now.
|
||||
self.maplikeOrSetlike.expand(self.members, self.isJSImplemented())
|
||||
|
||||
# Now that we've merged in our partial interfaces, set the
|
||||
# _exposureGlobalNames on any members that don't have it set yet. Note
|
||||
# that any partial interfaces that had [Exposed] set have already set up
|
||||
@@ -718,7 +743,6 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
|
||||
if self.parent:
|
||||
self.parent.finish(scope)
|
||||
|
||||
self.parent._hasChildInterfaces = True
|
||||
|
||||
self.totalMembersInSlots = self.parent.totalMembersInSlots
|
||||
@@ -836,6 +860,17 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
"consequential interface %s is not exposed." %
|
||||
(self.identifier.name, iface.identifier.name),
|
||||
[self.location, iface.location])
|
||||
|
||||
# If we have a maplike or setlike, and the consequential interface
|
||||
# also does, throw an error.
|
||||
if iface.maplikeOrSetlike and self.maplikeOrSetlike:
|
||||
raise WebIDLError("Maplike/setlike interface %s cannot have "
|
||||
"maplike/setlike interface %s as a "
|
||||
"consequential interface" %
|
||||
(self.identifier.name,
|
||||
iface.identifier.name),
|
||||
[self.maplikeOrSetlike.location,
|
||||
iface.maplikeOrSetlike.location])
|
||||
additionalMembers = iface.originalMembers;
|
||||
for additionalMember in additionalMembers:
|
||||
for member in self.members:
|
||||
@@ -849,6 +884,15 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
|
||||
for ancestor in self.getInheritedInterfaces():
|
||||
ancestor.interfacesBasedOnSelf.add(self)
|
||||
if (ancestor.maplikeOrSetlike is not None and
|
||||
self.maplikeOrSetlike is not None):
|
||||
raise WebIDLError("Cannot have maplike/setlike on %s that "
|
||||
"inherits %s, which is already "
|
||||
"maplike/setlike" %
|
||||
(self.identifier.name,
|
||||
ancestor.identifier.name),
|
||||
[self.maplikeOrSetlike.location,
|
||||
ancestor.maplikeOrSetlike.location])
|
||||
for ancestorConsequential in ancestor.getConsequentialInterfaces():
|
||||
ancestorConsequential.interfacesBasedOnSelf.add(self)
|
||||
|
||||
@@ -885,12 +929,14 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
not hasattr(member, "originatingInterface")):
|
||||
member.originatingInterface = self
|
||||
|
||||
# Compute slot indices for our members before we pull in
|
||||
# unforgeable members from our parent.
|
||||
# Compute slot indices for our members before we pull in unforgeable
|
||||
# members from our parent. Also, maplike/setlike declarations get a
|
||||
# slot to hold their backing object.
|
||||
for member in self.members:
|
||||
if (member.isAttr() and
|
||||
(member.getExtendedAttribute("StoreInSlot") or
|
||||
member.getExtendedAttribute("Cached"))):
|
||||
if ((member.isAttr() and
|
||||
(member.getExtendedAttribute("StoreInSlot") or
|
||||
member.getExtendedAttribute("Cached"))) or
|
||||
member.isMaplikeOrSetlike()):
|
||||
member.slotIndex = self.totalMembersInSlots
|
||||
self.totalMembersInSlots += 1
|
||||
if member.getExtendedAttribute("StoreInSlot"):
|
||||
@@ -927,6 +973,18 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
# complicated and seems unnecessary.
|
||||
self.members.append(unforgeableMember)
|
||||
|
||||
# At this point, we have all of our members. If the current interface
|
||||
# uses maplike/setlike, check for collisions anywhere in the current
|
||||
# interface or higher in the inheritance chain.
|
||||
if self.maplikeOrSetlike:
|
||||
testInterface = self
|
||||
isAncestor = False
|
||||
while testInterface:
|
||||
self.maplikeOrSetlike.checkCollisions(testInterface.members,
|
||||
isAncestor)
|
||||
isAncestor = True
|
||||
testInterface = testInterface.parent
|
||||
|
||||
# Ensure that there's at most one of each {named,indexed}
|
||||
# {getter,setter,creator,deleter}, at most one stringifier,
|
||||
# and at most one legacycaller. Note that this last is not
|
||||
@@ -3251,7 +3309,8 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
|
||||
Tags = enum(
|
||||
'Const',
|
||||
'Attr',
|
||||
'Method'
|
||||
'Method',
|
||||
'MaplikeOrSetlike'
|
||||
)
|
||||
|
||||
Special = enum(
|
||||
@@ -3277,6 +3336,9 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
|
||||
def isConst(self):
|
||||
return self.tag == IDLInterfaceMember.Tags.Const
|
||||
|
||||
def isMaplikeOrSetlike(self):
|
||||
return self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
for attr in attrs:
|
||||
self.handleExtendedAttribute(attr)
|
||||
@@ -3350,6 +3412,256 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
|
||||
[self.location])
|
||||
self.aliases.append(alias)
|
||||
|
||||
|
||||
# MaplikeOrSetlike adds a trait to an interface, like map or iteration
|
||||
# functions. To handle them while still getting all of the generated binding
|
||||
# code taken care of, we treat them as macros that are expanded into members
|
||||
# based on parsed values.
|
||||
class IDLMaplikeOrSetlike(IDLInterfaceMember):
|
||||
|
||||
MaplikeOrSetlikeTypes = enum(
|
||||
'maplike',
|
||||
'setlike'
|
||||
)
|
||||
|
||||
def __init__(self, location, identifier, maplikeOrSetlikeType,
|
||||
readonly, keyType, valueType):
|
||||
IDLInterfaceMember.__init__(self, location, identifier,
|
||||
IDLInterfaceMember.Tags.MaplikeOrSetlike)
|
||||
|
||||
assert isinstance(keyType, IDLType)
|
||||
assert isinstance(valueType, IDLType)
|
||||
self.maplikeOrSetlikeType = maplikeOrSetlikeType
|
||||
self.readonly = readonly
|
||||
self.keyType = keyType
|
||||
self.valueType = valueType
|
||||
self.slotIndex = None
|
||||
self.disallowedMemberNames = []
|
||||
self.disallowedNonMethodNames = []
|
||||
|
||||
# When generating JSAPI access code, we need to know the backing object
|
||||
# type prefix to create the correct function. Generate here for reuse.
|
||||
if self.isMaplike():
|
||||
self.prefix = 'Map'
|
||||
elif self.isSetlike():
|
||||
self.prefix = 'Set'
|
||||
|
||||
def __str__(self):
|
||||
return "declared '%s' with key '%s'" % (self.maplikeOrSetlikeType, self.keyType)
|
||||
|
||||
def isMaplike(self):
|
||||
return self.maplikeOrSetlikeType == "maplike"
|
||||
|
||||
def isSetlike(self):
|
||||
return self.maplikeOrSetlikeType == "setlike"
|
||||
|
||||
def checkCollisions(self, members, isAncestor):
|
||||
for member in members:
|
||||
# Check that there are no disallowed members
|
||||
if (member.identifier.name in self.disallowedMemberNames and
|
||||
not ((member.isMethod() and member.isMaplikeOrSetlikeMethod()) or
|
||||
(member.isAttr() and member.isMaplikeOrSetlikeAttr()))):
|
||||
raise WebIDLError("Member '%s' conflicts "
|
||||
"with reserved %s name." %
|
||||
(member.identifier.name,
|
||||
self.maplikeOrSetlikeType),
|
||||
[self.location, member.location])
|
||||
# Check that there are no disallowed non-method members
|
||||
if (isAncestor or (member.isAttr() or member.isConst()) and
|
||||
member.identifier.name in self.disallowedNonMethodNames):
|
||||
raise WebIDLError("Member '%s' conflicts "
|
||||
"with reserved %s method." %
|
||||
(member.identifier.name,
|
||||
self.maplikeOrSetlikeType),
|
||||
[self.location, member.location])
|
||||
|
||||
def expand(self, members, isJSImplemented):
|
||||
"""
|
||||
In order to take advantage of all of the method machinery in Codegen,
|
||||
we generate our functions as if they were part of the interface
|
||||
specification during parsing.
|
||||
"""
|
||||
def addMethod(name, allowExistingOperations, returnType, args=[],
|
||||
chromeOnly=False, isPure=False, affectsNothing=False):
|
||||
"""
|
||||
Create an IDLMethod based on the parameters passed in. chromeOnly is only
|
||||
True for read-only js implemented classes, to implement underscore
|
||||
prefixed convenience functions would otherwise not be available,
|
||||
unlike the case of C++ bindings. isPure is only True for
|
||||
idempotent functions, so it is not valid for things like keys,
|
||||
values, etc. that return a new object every time.
|
||||
|
||||
"""
|
||||
|
||||
# Only add name to lists for collision checks if it's not chrome
|
||||
# only.
|
||||
if chromeOnly:
|
||||
name = "__" + name
|
||||
else:
|
||||
if not allowExistingOperations:
|
||||
self.disallowedMemberNames.append(name)
|
||||
else:
|
||||
self.disallowedNonMethodNames.append(name)
|
||||
|
||||
# If allowExistingOperations is True, and another operation exists
|
||||
# with the same name as the one we're trying to add, don't add the
|
||||
# maplike/setlike operation. However, if the operation is static,
|
||||
# then fail by way of creating the function, which will cause a
|
||||
# naming conflict, per the spec.
|
||||
if allowExistingOperations:
|
||||
for m in members:
|
||||
if m.identifier.name == name and m.isMethod() and not m.isStatic():
|
||||
return
|
||||
|
||||
method = IDLMethod(self.location,
|
||||
IDLUnresolvedIdentifier(self.location, name, allowDoubleUnderscore=chromeOnly),
|
||||
returnType, args, maplikeOrSetlike=self)
|
||||
|
||||
# We need to be able to throw from declaration methods
|
||||
method.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self.location, ("Throws",))])
|
||||
if chromeOnly:
|
||||
method.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self.location, ("ChromeOnly",))])
|
||||
if isPure:
|
||||
method.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self.location, ("Pure",))])
|
||||
# Following attributes are used for keys/values/entries. Can't mark
|
||||
# them pure, since they return a new object each time they are run.
|
||||
if affectsNothing:
|
||||
method.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self.location, ("DependsOn", "Everything")),
|
||||
IDLExtendedAttribute(self.location, ("Affects", "Nothing"))])
|
||||
members.append(method)
|
||||
|
||||
# Both maplike and setlike have a size attribute
|
||||
members.append(IDLAttribute(self.location,
|
||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
|
||||
BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
|
||||
True,
|
||||
maplikeOrSetlike=self))
|
||||
self.reserved_ro_names = ["size"]
|
||||
|
||||
# object entries()
|
||||
addMethod("entries", False, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||
affectsNothing=True)
|
||||
# object keys()
|
||||
addMethod("keys", False, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||
affectsNothing=True)
|
||||
# object values()
|
||||
addMethod("values", False, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||
affectsNothing=True)
|
||||
|
||||
# void forEach(callback(valueType, keyType), thisVal)
|
||||
foreachArguments = [IDLArgument(self.location,
|
||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
|
||||
"callback"),
|
||||
BuiltinTypes[IDLBuiltinType.Types.object]),
|
||||
IDLArgument(self.location,
|
||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
|
||||
"thisArg"),
|
||||
BuiltinTypes[IDLBuiltinType.Types.any],
|
||||
optional=True)]
|
||||
addMethod("forEach", False, BuiltinTypes[IDLBuiltinType.Types.void],
|
||||
foreachArguments)
|
||||
|
||||
def getKeyArg():
|
||||
return IDLArgument(self.location,
|
||||
IDLUnresolvedIdentifier(self.location, "key"),
|
||||
self.keyType)
|
||||
|
||||
# boolean has(keyType key)
|
||||
addMethod("has", False, BuiltinTypes[IDLBuiltinType.Types.boolean],
|
||||
[getKeyArg()], isPure=True)
|
||||
|
||||
if not self.readonly:
|
||||
# void clear()
|
||||
addMethod("clear", True, BuiltinTypes[IDLBuiltinType.Types.void],
|
||||
[])
|
||||
# boolean delete(keyType key)
|
||||
addMethod("delete", True,
|
||||
BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()])
|
||||
|
||||
# Always generate underscored functions (e.g. __add, __clear) for js
|
||||
# implemented interfaces as convenience functions.
|
||||
if isJSImplemented:
|
||||
# void clear()
|
||||
addMethod("clear", True, BuiltinTypes[IDLBuiltinType.Types.void],
|
||||
[], chromeOnly=True)
|
||||
# boolean delete(keyType key)
|
||||
addMethod("delete", True,
|
||||
BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()],
|
||||
chromeOnly=True)
|
||||
|
||||
if self.isSetlike():
|
||||
if not self.readonly:
|
||||
# Add returns the set object it just added to.
|
||||
# object add(keyType key)
|
||||
|
||||
addMethod("add", True,
|
||||
BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()])
|
||||
if isJSImplemented:
|
||||
addMethod("add", True,
|
||||
BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()],
|
||||
chromeOnly=True)
|
||||
return
|
||||
|
||||
# If we get this far, we're a maplike declaration.
|
||||
|
||||
# valueType get(keyType key)
|
||||
#
|
||||
# Note that instead of the value type, we're using any here. The
|
||||
# validity checks should happen as things are inserted into the map,
|
||||
# and using any as the return type makes code generation much simpler.
|
||||
#
|
||||
# TODO: Bug 1155340 may change this to use specific type to provide
|
||||
# more info to JIT.
|
||||
addMethod("get", False, BuiltinTypes[IDLBuiltinType.Types.any],
|
||||
[getKeyArg()], isPure=True)
|
||||
|
||||
def getValueArg():
|
||||
return IDLArgument(self.location,
|
||||
IDLUnresolvedIdentifier(self.location, "value"),
|
||||
self.valueType)
|
||||
|
||||
|
||||
if not self.readonly:
|
||||
addMethod("set", True, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||
[getKeyArg(), getValueArg()])
|
||||
if isJSImplemented:
|
||||
addMethod("set", True, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||
[getKeyArg(), getValueArg()], chromeOnly=True)
|
||||
|
||||
def resolve(self, parentScope):
|
||||
self.keyType.resolveType(parentScope)
|
||||
self.valueType.resolveType(parentScope)
|
||||
|
||||
def finish(self, scope):
|
||||
IDLInterfaceMember.finish(self, scope)
|
||||
if not self.keyType.isComplete():
|
||||
t = self.keyType.complete(scope)
|
||||
|
||||
assert not isinstance(t, IDLUnresolvedType)
|
||||
assert not isinstance(t, IDLTypedefType)
|
||||
assert not isinstance(t.name, IDLUnresolvedIdentifier)
|
||||
self.keyType = t
|
||||
if not self.valueType.isComplete():
|
||||
t = self.valueType.complete(scope)
|
||||
|
||||
assert not isinstance(t, IDLUnresolvedType)
|
||||
assert not isinstance(t, IDLTypedefType)
|
||||
assert not isinstance(t.name, IDLUnresolvedIdentifier)
|
||||
self.valueType = t
|
||||
|
||||
def validate(self):
|
||||
IDLInterfaceMember.validate(self)
|
||||
|
||||
def handleExtendedAttribute(self, attr):
|
||||
IDLInterfaceMember.handleExtendedAttribute(self, attr)
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set([self.keyType, self.valueType])
|
||||
|
||||
class IDLConst(IDLInterfaceMember):
|
||||
def __init__(self, location, identifier, type, value):
|
||||
IDLInterfaceMember.__init__(self, location, identifier,
|
||||
@@ -3414,7 +3726,7 @@ class IDLConst(IDLInterfaceMember):
|
||||
|
||||
class IDLAttribute(IDLInterfaceMember):
|
||||
def __init__(self, location, identifier, type, readonly, inherit=False,
|
||||
static=False, stringifier=False):
|
||||
static=False, stringifier=False, maplikeOrSetlike=None):
|
||||
IDLInterfaceMember.__init__(self, location, identifier,
|
||||
IDLInterfaceMember.Tags.Attr)
|
||||
|
||||
@@ -3429,6 +3741,8 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
self.enforceRange = False
|
||||
self.clamp = False
|
||||
self.slotIndex = None
|
||||
assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
|
||||
self.maplikeOrSetlike = maplikeOrSetlike
|
||||
self.dependsOn = "Everything"
|
||||
self.affects = "Everything"
|
||||
|
||||
@@ -3655,6 +3969,11 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
"readonly attributes" % attr.value(),
|
||||
[attr.location, self.location])
|
||||
self._setDependsOn(attr.value())
|
||||
elif identifier == "UseCounter":
|
||||
if self.stringifier:
|
||||
raise WebIDLError("[UseCounter] must not be used on a "
|
||||
"stringifier attribute",
|
||||
[attr.location, self.location])
|
||||
elif identifier == "Unscopable":
|
||||
if not attr.noArguments():
|
||||
raise WebIDLError("[Unscopable] must take no arguments",
|
||||
@@ -3696,6 +4015,14 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
def hasLenientThis(self):
|
||||
return self.lenientThis
|
||||
|
||||
def isMaplikeOrSetlikeAttr(self):
|
||||
"""
|
||||
True if this attribute was generated from an interface with
|
||||
maplike/setlike (e.g. this is the size attribute for
|
||||
maplike/setlike)
|
||||
"""
|
||||
return self.maplikeOrSetlike is not None
|
||||
|
||||
def isUnforgeable(self):
|
||||
return self._unforgeable
|
||||
|
||||
@@ -3934,7 +4261,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
def __init__(self, location, identifier, returnType, arguments,
|
||||
static=False, getter=False, setter=False, creator=False,
|
||||
deleter=False, specialType=NamedOrIndexed.Neither,
|
||||
legacycaller=False, stringifier=False, jsonifier=False):
|
||||
legacycaller=False, stringifier=False, jsonifier=False,
|
||||
maplikeOrSetlike=None):
|
||||
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
|
||||
IDLInterfaceMember.__init__(self, location, identifier,
|
||||
IDLInterfaceMember.Tags.Method)
|
||||
@@ -3962,6 +4290,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
self._stringifier = stringifier
|
||||
assert isinstance(jsonifier, bool)
|
||||
self._jsonifier = jsonifier
|
||||
assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
|
||||
self.maplikeOrSetlike = maplikeOrSetlike
|
||||
self._specialType = specialType
|
||||
self._unforgeable = False
|
||||
self.dependsOn = "Everything"
|
||||
@@ -4043,11 +4373,35 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
def isJsonifier(self):
|
||||
return self._jsonifier
|
||||
|
||||
def isMaplikeOrSetlikeMethod(self):
|
||||
"""
|
||||
True if this method was generated as part of a
|
||||
maplike/setlike/etc interface (e.g. has/get methods)
|
||||
"""
|
||||
return self.maplikeOrSetlike is not None
|
||||
|
||||
def isSpecial(self):
|
||||
return (self.isGetter() or
|
||||
self.isSetter() or
|
||||
self.isCreator() or
|
||||
self.isDeleter() or
|
||||
self.isLegacycaller() or
|
||||
self.isStringifier() or
|
||||
self.isJsonifier())
|
||||
|
||||
def hasOverloads(self):
|
||||
return self._hasOverloads
|
||||
|
||||
def isIdentifierLess(self):
|
||||
return self.identifier.name[:2] == "__" and self.identifier.name != "__noSuchMethod__"
|
||||
"""
|
||||
True if the method name started with __, and if the method is not a
|
||||
maplike/setlike method. Interfaces with maplike/setlike will generate
|
||||
methods starting with __ for chrome only backing object access in JS
|
||||
implemented interfaces, so while these functions use what is considered
|
||||
an non-identifier name, they actually DO have an identifier.
|
||||
"""
|
||||
return (self.identifier.name[:2] == "__" and
|
||||
not self.isMaplikeOrSetlikeMethod())
|
||||
|
||||
def resolve(self, parentScope):
|
||||
assert isinstance(parentScope, IDLScope)
|
||||
@@ -4316,6 +4670,11 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
raise WebIDLError("[Alias] takes an identifier or string",
|
||||
[attr.location])
|
||||
self._addAlias(attr.value())
|
||||
elif identifier == "UseCounter":
|
||||
if self.isSpecial():
|
||||
raise WebIDLError("[UseCounter] must not be used on a special "
|
||||
"operation",
|
||||
[attr.location, self.location])
|
||||
elif identifier == "Unscopable":
|
||||
if not attr.noArguments():
|
||||
raise WebIDLError("[Unscopable] must take no arguments",
|
||||
@@ -4553,7 +4912,9 @@ class Tokenizer(object):
|
||||
">": "GT",
|
||||
"ArrayBuffer": "ARRAYBUFFER",
|
||||
"SharedArrayBuffer": "SHAREDARRAYBUFFER",
|
||||
"or": "OR"
|
||||
"or": "OR",
|
||||
"maplike": "MAPLIKE",
|
||||
"setlike": "SETLIKE"
|
||||
}
|
||||
|
||||
tokens.extend(keywords.values())
|
||||
@@ -4789,7 +5150,7 @@ class Parser(Tokenizer):
|
||||
def p_InterfaceMember(self, p):
|
||||
"""
|
||||
InterfaceMember : Const
|
||||
| AttributeOrOperation
|
||||
| AttributeOrOperationOrMaplikeOrSetlike
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
@@ -5004,13 +5365,43 @@ class Parser(Tokenizer):
|
||||
"""
|
||||
p[0] = False
|
||||
|
||||
def p_AttributeOrOperation(self, p):
|
||||
def p_AttributeOrOperationOrMaplikeOrSetlike(self, p):
|
||||
"""
|
||||
AttributeOrOperation : Attribute
|
||||
| Operation
|
||||
AttributeOrOperationOrMaplikeOrSetlike : Attribute
|
||||
| Maplike
|
||||
| Setlike
|
||||
| Operation
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def p_Setlike(self, p):
|
||||
"""
|
||||
Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON
|
||||
"""
|
||||
readonly = p[1]
|
||||
maplikeOrSetlikeType = p[2]
|
||||
location = self.getLocation(p, 2)
|
||||
identifier = IDLUnresolvedIdentifier(location, "__setlike",
|
||||
allowDoubleUnderscore=True)
|
||||
keyType = p[4]
|
||||
valueType = keyType
|
||||
p[0] = IDLMaplikeOrSetlike(location, identifier, maplikeOrSetlikeType,
|
||||
readonly, keyType, valueType)
|
||||
|
||||
def p_Maplike(self, p):
|
||||
"""
|
||||
Maplike : ReadOnly MAPLIKE LT Type COMMA Type GT SEMICOLON
|
||||
"""
|
||||
readonly = p[1]
|
||||
maplikeOrSetlikeType = p[2]
|
||||
location = self.getLocation(p, 2)
|
||||
identifier = IDLUnresolvedIdentifier(location, "__maplike",
|
||||
allowDoubleUnderscore=True)
|
||||
keyType = p[4]
|
||||
valueType = p[6]
|
||||
p[0] = IDLMaplikeOrSetlike(location, identifier, maplikeOrSetlikeType,
|
||||
readonly, keyType, valueType)
|
||||
|
||||
def p_AttributeWithQualifier(self, p):
|
||||
"""
|
||||
Attribute : Qualifier AttributeRest
|
||||
@@ -5018,15 +5409,22 @@ class Parser(Tokenizer):
|
||||
static = IDLInterfaceMember.Special.Static in p[1]
|
||||
stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
|
||||
(location, identifier, type, readonly) = p[2]
|
||||
p[0] = IDLAttribute(location, identifier, type, readonly, static=static,
|
||||
stringifier=stringifier)
|
||||
p[0] = IDLAttribute(location, identifier, type, readonly,
|
||||
static=static, stringifier=stringifier)
|
||||
|
||||
def p_AttributeInherited(self, p):
|
||||
"""
|
||||
Attribute : INHERIT AttributeRest
|
||||
"""
|
||||
(location, identifier, type, readonly) = p[2]
|
||||
p[0] = IDLAttribute(location, identifier, type, readonly, inherit=True)
|
||||
|
||||
def p_Attribute(self, p):
|
||||
"""
|
||||
Attribute : Inherit AttributeRest
|
||||
Attribute : AttributeRest
|
||||
"""
|
||||
(location, identifier, type, readonly) = p[2]
|
||||
p[0] = IDLAttribute(location, identifier, type, readonly, inherit=p[1])
|
||||
(location, identifier, type, readonly) = p[1]
|
||||
p[0] = IDLAttribute(location, identifier, type, readonly, inherit=False)
|
||||
|
||||
def p_AttributeRest(self, p):
|
||||
"""
|
||||
@@ -5050,18 +5448,6 @@ class Parser(Tokenizer):
|
||||
"""
|
||||
p[0] = False
|
||||
|
||||
def p_Inherit(self, p):
|
||||
"""
|
||||
Inherit : INHERIT
|
||||
"""
|
||||
p[0] = True
|
||||
|
||||
def p_InheritEmpty(self, p):
|
||||
"""
|
||||
Inherit :
|
||||
"""
|
||||
p[0] = False
|
||||
|
||||
def p_Operation(self, p):
|
||||
"""
|
||||
Operation : Qualifiers OperationRest
|
||||
@@ -5358,9 +5744,11 @@ class Parser(Tokenizer):
|
||||
| INHERIT
|
||||
| INTERFACE
|
||||
| LEGACYCALLER
|
||||
| MAPLIKE
|
||||
| PARTIAL
|
||||
| REQUIRED
|
||||
| SERIALIZER
|
||||
| SETLIKE
|
||||
| SETTER
|
||||
| STATIC
|
||||
| STRINGIFIER
|
||||
|
||||
@@ -0,0 +1,528 @@
|
||||
import WebIDL
|
||||
import traceback
|
||||
def WebIDLTest(parser, harness):
|
||||
|
||||
def shouldPass(prefix, iface, expectedMembers, numProductions=1):
|
||||
p = parser.reset()
|
||||
p.parse(iface)
|
||||
results = p.finish()
|
||||
harness.check(len(results), numProductions,
|
||||
"%s - Should have production count %d" % (prefix, numProductions))
|
||||
harness.ok(isinstance(results[0], WebIDL.IDLInterface),
|
||||
"%s - Should be an IDLInterface" % (prefix))
|
||||
harness.check(len(results[0].members), len(expectedMembers),
|
||||
"%s - Should be %d members" % (prefix,
|
||||
len(expectedMembers)))
|
||||
for m in results[0].members:
|
||||
name = m.identifier.name
|
||||
if (name, type(m)) in expectedMembers:
|
||||
harness.ok(True, "%s - %s - Should be a %s" % (prefix, name,
|
||||
type(m)))
|
||||
elif isinstance(m, WebIDL.IDLMaplikeOrSetlike):
|
||||
harness.ok(True, "%s - %s - Should be a MaplikeOrSetlike" %
|
||||
(prefix, name))
|
||||
else:
|
||||
harness.ok(False, "%s - %s - Unknown symbol of type %s" %
|
||||
(prefix, name, type(m)))
|
||||
return results
|
||||
|
||||
def shouldFail(prefix, iface):
|
||||
try:
|
||||
p = parser.reset()
|
||||
p.parse(iface)
|
||||
p.finish()
|
||||
harness.ok(False,
|
||||
prefix + " - Interface passed when should've failed")
|
||||
except WebIDL.WebIDLError, e:
|
||||
harness.ok(True,
|
||||
prefix + " - Interface failed as expected")
|
||||
except Exception, e:
|
||||
harness.ok(False,
|
||||
prefix + " - Interface failed but not as a WebIDLError exception")
|
||||
|
||||
iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys",
|
||||
"values", "forEach"]]
|
||||
iterableMembers.extend([("size", WebIDL.IDLAttribute)])
|
||||
setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has"]] +
|
||||
[("__setlike", WebIDL.IDLMaplikeOrSetlike)] +
|
||||
iterableMembers)
|
||||
setRWMembers = ([(x, WebIDL.IDLMethod) for x in ["add",
|
||||
"clear",
|
||||
"delete"]] +
|
||||
setROMembers)
|
||||
setROChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__add",
|
||||
"__clear",
|
||||
"__delete"]] +
|
||||
setROMembers)
|
||||
setRWChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__add",
|
||||
"__clear",
|
||||
"__delete"]] +
|
||||
setRWMembers)
|
||||
mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has"]] +
|
||||
[("__maplike", WebIDL.IDLMaplikeOrSetlike)] +
|
||||
iterableMembers)
|
||||
mapRWMembers = ([(x, WebIDL.IDLMethod) for x in ["set",
|
||||
"clear",
|
||||
"delete"]] + mapROMembers)
|
||||
mapRWChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__set",
|
||||
"__clear",
|
||||
"__delete"]] +
|
||||
mapRWMembers)
|
||||
|
||||
disallowedMemberNames = ["keys", "entries", "values", "forEach", "has",
|
||||
"size"]
|
||||
mapDisallowedMemberNames = ["get"] + disallowedMemberNames
|
||||
disallowedNonMethodNames = ["clear", "delete"]
|
||||
mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames
|
||||
setDisallowedNonMethodNames = ["add"] + disallowedNonMethodNames
|
||||
|
||||
#
|
||||
# Simple Usage Tests
|
||||
#
|
||||
|
||||
shouldPass("Maplike (readwrite)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""", mapRWMembers)
|
||||
|
||||
shouldPass("Maplike (readonly)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
readonly maplike<long, long>;
|
||||
};
|
||||
""", mapROMembers)
|
||||
|
||||
shouldPass("Setlike (readwrite)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
setlike<long>;
|
||||
};
|
||||
""", setRWMembers)
|
||||
|
||||
shouldPass("Setlike (readonly)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
readonly setlike<long>;
|
||||
};
|
||||
""", setROMembers)
|
||||
|
||||
shouldPass("Inheritance of maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
};
|
||||
""", mapRWMembers, numProductions=2)
|
||||
|
||||
shouldPass("Implements with maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
};
|
||||
Foo2 implements Foo1;
|
||||
""", mapRWMembers, numProductions=3)
|
||||
|
||||
shouldPass("JS Implemented maplike interface",
|
||||
"""
|
||||
[JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
|
||||
Constructor()]
|
||||
interface Foo1 {
|
||||
setlike<long>;
|
||||
};
|
||||
""", setRWChromeMembers)
|
||||
|
||||
shouldPass("JS Implemented maplike interface",
|
||||
"""
|
||||
[JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
|
||||
Constructor()]
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""", mapRWChromeMembers)
|
||||
|
||||
#
|
||||
# Multiple maplike/setlike tests
|
||||
#
|
||||
|
||||
shouldFail("Two maplike/setlikes on same interface",
|
||||
"""
|
||||
interface Foo1 {
|
||||
setlike<long>;
|
||||
maplike<long, long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldFail("Two maplike/setlikes in partials",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
partial interface Foo1 {
|
||||
setlike<long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldFail("Conflicting maplike/setlikes across inheritance",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
setlike<long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldFail("Conflicting maplike/setlikes across multistep inheritance",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
};
|
||||
interface Foo3 : Foo2 {
|
||||
setlike<long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldFail("Consequential interface with conflicting maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
setlike<long>;
|
||||
};
|
||||
Foo2 implements Foo1;
|
||||
""")
|
||||
|
||||
shouldFail("Consequential interfaces with conflicting maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
setlike<long>;
|
||||
};
|
||||
interface Foo3 {
|
||||
};
|
||||
Foo3 implements Foo1;
|
||||
Foo3 implements Foo2;
|
||||
""")
|
||||
|
||||
#
|
||||
# Member name collision tests
|
||||
#
|
||||
|
||||
def testConflictingMembers(likeMember, conflictName, expectedMembers, methodPasses):
|
||||
"""
|
||||
Tests for maplike/setlike member generation against conflicting member
|
||||
names. If methodPasses is True, this means we expect the interface to
|
||||
pass in the case of method shadowing, and expectedMembers should be the
|
||||
list of interface members to check against on the passing interface.
|
||||
|
||||
"""
|
||||
if methodPasses:
|
||||
shouldPass("Conflicting method: %s and %s" % (likeMember, conflictName),
|
||||
"""
|
||||
interface Foo1 {
|
||||
%s;
|
||||
[Throws]
|
||||
void %s(long test1, double test2, double test3);
|
||||
};
|
||||
""" % (likeMember, conflictName), expectedMembers)
|
||||
else:
|
||||
shouldFail("Conflicting method: %s and %s" % (likeMember, conflictName),
|
||||
"""
|
||||
interface Foo1 {
|
||||
%s;
|
||||
[Throws]
|
||||
void %s(long test1, double test2, double test3);
|
||||
};
|
||||
""" % (likeMember, conflictName))
|
||||
# Inherited conflicting methods should ALWAYS fail
|
||||
shouldFail("Conflicting inherited method: %s and %s" % (likeMember, conflictName),
|
||||
"""
|
||||
interface Foo1 {
|
||||
void %s(long test1, double test2, double test3);
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
%s;
|
||||
};
|
||||
""" % (conflictName, likeMember))
|
||||
shouldFail("Conflicting static method: %s and %s" % (likeMember, conflictName),
|
||||
"""
|
||||
interface Foo1 {
|
||||
%s;
|
||||
static void %s(long test1, double test2, double test3);
|
||||
};
|
||||
""" % (likeMember, conflictName))
|
||||
shouldFail("Conflicting attribute: %s and %s" % (likeMember, conflictName),
|
||||
"""
|
||||
interface Foo1 {
|
||||
%s
|
||||
attribute double %s;
|
||||
};
|
||||
""" % (likeMember, conflictName))
|
||||
shouldFail("Conflicting const: %s and %s" % (likeMember, conflictName),
|
||||
"""
|
||||
interface Foo1 {
|
||||
%s;
|
||||
const double %s = 0;
|
||||
};
|
||||
""" % (likeMember, conflictName))
|
||||
shouldFail("Conflicting static attribute: %s and %s" % (likeMember, conflictName),
|
||||
"""
|
||||
interface Foo1 {
|
||||
%s;
|
||||
static attribute long %s;
|
||||
};
|
||||
""" % (likeMember, conflictName))
|
||||
|
||||
for member in mapDisallowedMemberNames:
|
||||
testConflictingMembers("maplike<long, long>", member, mapRWMembers, False)
|
||||
for member in disallowedMemberNames:
|
||||
testConflictingMembers("setlike<long>", member, setRWMembers, False)
|
||||
for member in mapDisallowedNonMethodNames:
|
||||
testConflictingMembers("maplike<long, long>", member, mapRWMembers, True)
|
||||
for member in setDisallowedNonMethodNames:
|
||||
testConflictingMembers("setlike<long>", member, setRWMembers, True)
|
||||
|
||||
shouldPass("Inheritance of maplike/setlike with child member collision",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
void entries();
|
||||
};
|
||||
""", mapRWMembers, numProductions=2)
|
||||
|
||||
shouldPass("Inheritance of multi-level maplike/setlike with child member collision",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
};
|
||||
interface Foo3 : Foo2 {
|
||||
void entries();
|
||||
};
|
||||
""", mapRWMembers, numProductions=3)
|
||||
|
||||
shouldFail("Interface with consequential maplike/setlike interface member collision",
|
||||
"""
|
||||
interface Foo1 {
|
||||
void entries();
|
||||
};
|
||||
interface Foo2 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
Foo1 implements Foo2;
|
||||
""")
|
||||
|
||||
shouldFail("Maplike interface with consequential interface member collision",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
void entries();
|
||||
};
|
||||
Foo1 implements Foo2;
|
||||
""")
|
||||
|
||||
shouldPass("Consequential Maplike interface with inherited interface member collision",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
void entries();
|
||||
};
|
||||
interface Foo3 : Foo2 {
|
||||
};
|
||||
Foo3 implements Foo1;
|
||||
""", mapRWMembers, numProductions=4)
|
||||
|
||||
shouldPass("Inherited Maplike interface with consequential interface member collision",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 {
|
||||
void entries();
|
||||
};
|
||||
interface Foo3 : Foo1 {
|
||||
};
|
||||
Foo3 implements Foo2;
|
||||
""", mapRWMembers, numProductions=4)
|
||||
|
||||
shouldFail("Inheritance of name collision with child maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
void entries();
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldFail("Inheritance of multi-level name collision with child maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
void entries();
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
};
|
||||
interface Foo3 : Foo2 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldPass("Inheritance of attribute collision with parent maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
attribute double size;
|
||||
};
|
||||
""", mapRWMembers, numProductions=2)
|
||||
|
||||
shouldPass("Inheritance of multi-level attribute collision with parent maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
};
|
||||
interface Foo3 : Foo2 {
|
||||
attribute double size;
|
||||
};
|
||||
""", mapRWMembers, numProductions=3)
|
||||
|
||||
shouldFail("Inheritance of attribute collision with child maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
attribute double size;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldFail("Inheritance of multi-level attribute collision with child maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
attribute double size;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
};
|
||||
interface Foo3 : Foo2 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldFail("Inheritance of attribute/rw function collision with child maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
attribute double set;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldFail("Inheritance of const/rw function collision with child maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
const double set = 0;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldPass("Inheritance of rw function with same name in child maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
void clear();
|
||||
};
|
||||
""", mapRWMembers, numProductions=2)
|
||||
|
||||
shouldFail("Inheritance of unforgeable attribute collision with child maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
[Unforgeable]
|
||||
attribute double size;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldFail("Inheritance of multi-level unforgeable attribute collision with child maplike/setlike",
|
||||
"""
|
||||
interface Foo1 {
|
||||
[Unforgeable]
|
||||
attribute double size;
|
||||
};
|
||||
interface Foo2 : Foo1 {
|
||||
};
|
||||
interface Foo3 : Foo2 {
|
||||
maplike<long, long>;
|
||||
};
|
||||
""")
|
||||
|
||||
shouldPass("Implemented interface with readonly allowable overrides",
|
||||
"""
|
||||
interface Foo1 {
|
||||
readonly setlike<long>;
|
||||
readonly attribute boolean clear;
|
||||
};
|
||||
""", setROMembers + [("clear", WebIDL.IDLAttribute)])
|
||||
|
||||
shouldPass("JS Implemented read-only interface with readonly allowable overrides",
|
||||
"""
|
||||
[JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
|
||||
Constructor()]
|
||||
interface Foo1 {
|
||||
readonly setlike<long>;
|
||||
readonly attribute boolean clear;
|
||||
};
|
||||
""", setROChromeMembers + [("clear", WebIDL.IDLAttribute)])
|
||||
|
||||
shouldFail("JS Implemented read-write interface with non-readwrite allowable overrides",
|
||||
"""
|
||||
[JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
|
||||
Constructor()]
|
||||
interface Foo1 {
|
||||
setlike<long>;
|
||||
readonly attribute boolean clear;
|
||||
};
|
||||
""")
|
||||
|
||||
r = shouldPass("Check proper override of clear/delete/set",
|
||||
"""
|
||||
interface Foo1 {
|
||||
maplike<long, long>;
|
||||
long clear(long a, long b, double c, double d);
|
||||
long set(long a, long b, double c, double d);
|
||||
long delete(long a, long b, double c, double d);
|
||||
};
|
||||
""", mapRWMembers)
|
||||
|
||||
for m in r[0].members:
|
||||
if m.identifier.name in ["clear", "set", "delete"]:
|
||||
harness.ok(m.isMethod(), "%s should be a method" % m.identifier.name)
|
||||
harness.check(m.maxArgCount, 4, "%s should have 4 arguments" % m.identifier.name)
|
||||
harness.ok(not m.isMaplikeOrSetlikeMethod(),
|
||||
"%s should not be a maplike/setlike function" % m.identifier.name)
|
||||
@@ -169,3 +169,16 @@ def WebIDLTest(parser, harness):
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should spell [Throws] correctly on methods")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse("""
|
||||
interface A {
|
||||
void __noSuchMethod__();
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should not allow __noSuchMethod__ methods")
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
component {2ac4e026-cf25-47d5-b067-78d553c3cad8} TestInterfaceJS.js
|
||||
contract @mozilla.org/dom/test-interface-js;1 {2ac4e026-cf25-47d5-b067-78d553c3cad8}
|
||||
component {4bc6f6f3-e005-4f0a-b42d-4d1663a9013a} TestInterfaceJSMaplike.js
|
||||
contract @mozilla.org/dom/test-interface-js-maplike;1 {4bc6f6f3-e005-4f0a-b42d-4d1663a9013a}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function TestInterfaceJSMaplike() {}
|
||||
|
||||
TestInterfaceJSMaplike.prototype = {
|
||||
classID: Components.ID("{4bc6f6f3-e005-4f0a-b42d-4d1663a9013a}"),
|
||||
contractID: "@mozilla.org/dom/test-interface-js-maplike;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
init: function(win) { this._win = win; },
|
||||
|
||||
__init: function () {},
|
||||
|
||||
setInternal: function(aKey, aValue) {
|
||||
return this.__DOM_IMPL__.__set(aKey, aValue);
|
||||
},
|
||||
|
||||
deleteInternal: function(aKey) {
|
||||
return this.__DOM_IMPL__.__delete(aKey);
|
||||
},
|
||||
|
||||
clearInternal: function() {
|
||||
return this.__DOM_IMPL__.__clear();
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJSMaplike])
|
||||
@@ -0,0 +1,84 @@
|
||||
/* 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 "mozilla/dom/TestInterfaceMaplike.h"
|
||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceMaplike, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceMaplike)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceMaplike)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceMaplike)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TestInterfaceMaplike::TestInterfaceMaplike(nsPIDOMWindow* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
}
|
||||
|
||||
//static
|
||||
already_AddRefed<TestInterfaceMaplike>
|
||||
TestInterfaceMaplike::Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TestInterfaceMaplike> r = new TestInterfaceMaplike(window);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TestInterfaceMaplike::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return TestInterfaceMaplikeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
TestInterfaceMaplike::GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void
|
||||
TestInterfaceMaplike::SetInternal(const nsAString& aKey, int32_t aValue)
|
||||
{
|
||||
ErrorResult rv;
|
||||
TestInterfaceMaplikeBinding::MaplikeHelpers::Set(this, aKey, aValue, rv);
|
||||
}
|
||||
|
||||
void
|
||||
TestInterfaceMaplike::ClearInternal()
|
||||
{
|
||||
ErrorResult rv;
|
||||
TestInterfaceMaplikeBinding::MaplikeHelpers::Clear(this, rv);
|
||||
}
|
||||
|
||||
bool
|
||||
TestInterfaceMaplike::DeleteInternal(const nsAString& aKey)
|
||||
{
|
||||
ErrorResult rv;
|
||||
return TestInterfaceMaplikeBinding::MaplikeHelpers::Delete(this, aKey, rv);
|
||||
}
|
||||
|
||||
bool
|
||||
TestInterfaceMaplike::HasInternal(const nsAString& aKey)
|
||||
{
|
||||
ErrorResult rv;
|
||||
return TestInterfaceMaplikeBinding::MaplikeHelpers::Has(this, aKey, rv);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TestInterfaceMaplike_h
|
||||
#define mozilla_dom_TestInterfaceMaplike_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
// Implementation of test binding for webidl maplike interfaces, using
|
||||
// primitives for key and value types.
|
||||
class TestInterfaceMaplike final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceMaplike)
|
||||
|
||||
explicit TestInterfaceMaplike(nsPIDOMWindow* aParent);
|
||||
nsPIDOMWindow* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
static already_AddRefed<TestInterfaceMaplike>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
|
||||
|
||||
// External access for testing internal convenience functions.
|
||||
void SetInternal(const nsAString& aKey, int32_t aValue);
|
||||
void ClearInternal();
|
||||
bool DeleteInternal(const nsAString& aKey);
|
||||
bool HasInternal(const nsAString& aKey);
|
||||
private:
|
||||
virtual ~TestInterfaceMaplike() {}
|
||||
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TestInterfaceMaplike_h
|
||||
@@ -0,0 +1,88 @@
|
||||
/* 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 "mozilla/dom/TestInterfaceMaplikeObject.h"
|
||||
#include "mozilla/dom/TestInterfaceMaplike.h"
|
||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceMaplikeObject, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceMaplikeObject)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceMaplikeObject)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceMaplikeObject)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TestInterfaceMaplikeObject::TestInterfaceMaplikeObject(nsPIDOMWindow* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
}
|
||||
|
||||
//static
|
||||
already_AddRefed<TestInterfaceMaplikeObject>
|
||||
TestInterfaceMaplikeObject::Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TestInterfaceMaplikeObject> r =
|
||||
new TestInterfaceMaplikeObject(window);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TestInterfaceMaplikeObject::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return TestInterfaceMaplikeObjectBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
TestInterfaceMaplikeObject::GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void
|
||||
TestInterfaceMaplikeObject::SetInternal(const nsAString& aKey)
|
||||
{
|
||||
nsRefPtr<TestInterfaceMaplike> p(new TestInterfaceMaplike(mParent));
|
||||
ErrorResult rv;
|
||||
TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Set(this, aKey, *p, rv);
|
||||
}
|
||||
|
||||
void
|
||||
TestInterfaceMaplikeObject::ClearInternal()
|
||||
{
|
||||
ErrorResult rv;
|
||||
TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Clear(this, rv);
|
||||
}
|
||||
|
||||
bool
|
||||
TestInterfaceMaplikeObject::DeleteInternal(const nsAString& aKey)
|
||||
{
|
||||
ErrorResult rv;
|
||||
return TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Delete(this, aKey, rv);
|
||||
}
|
||||
|
||||
bool
|
||||
TestInterfaceMaplikeObject::HasInternal(const nsAString& aKey)
|
||||
{
|
||||
ErrorResult rv;
|
||||
return TestInterfaceMaplikeObjectBinding::MaplikeHelpers::Has(this, aKey, rv);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TestInterfaceMaplikeObject_h
|
||||
#define mozilla_dom_TestInterfaceMaplikeObject_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
// Implementation of test binding for webidl maplike interfaces, using
|
||||
// primitives for key types and objects for value types.
|
||||
class TestInterfaceMaplikeObject final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceMaplikeObject)
|
||||
|
||||
explicit TestInterfaceMaplikeObject(nsPIDOMWindow* aParent);
|
||||
nsPIDOMWindow* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
static already_AddRefed<TestInterfaceMaplikeObject>
|
||||
Constructor(const GlobalObject& aGlobal,ErrorResult& rv);
|
||||
|
||||
// External access for testing internal convenience functions.
|
||||
void SetInternal(const nsAString& aKey);
|
||||
void ClearInternal();
|
||||
bool DeleteInternal(const nsAString& aKey);
|
||||
bool HasInternal(const nsAString& aKey);
|
||||
private:
|
||||
virtual ~TestInterfaceMaplikeObject() {}
|
||||
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TestInterfaceMaplikeObject_h
|
||||
@@ -0,0 +1,58 @@
|
||||
/* 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 "mozilla/dom/TestInterfaceSetlike.h"
|
||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceSetlike, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceSetlike)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceSetlike)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceSetlike)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TestInterfaceSetlike::TestInterfaceSetlike(JSContext* aCx,
|
||||
nsPIDOMWindow* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
}
|
||||
|
||||
//static
|
||||
already_AddRefed<TestInterfaceSetlike>
|
||||
TestInterfaceSetlike::Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TestInterfaceSetlike> r = new TestInterfaceSetlike(nullptr, window);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TestInterfaceSetlike::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return TestInterfaceSetlikeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
TestInterfaceSetlike::GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TestInterfaceSetlike_h
|
||||
#define mozilla_dom_TestInterfaceSetlike_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
// Implementation of test binding for webidl setlike interfaces, using
|
||||
// primitives for key type.
|
||||
class TestInterfaceSetlike final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceSetlike)
|
||||
explicit TestInterfaceSetlike(JSContext* aCx,
|
||||
nsPIDOMWindow* aParent);
|
||||
nsPIDOMWindow* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
static already_AddRefed<TestInterfaceSetlike>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
|
||||
private:
|
||||
virtual ~TestInterfaceSetlike() {}
|
||||
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TestInterfaceSetlike_h
|
||||
@@ -0,0 +1,58 @@
|
||||
/* 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 "mozilla/dom/TestInterfaceSetlikeNode.h"
|
||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceSetlikeNode, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceSetlikeNode)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceSetlikeNode)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceSetlikeNode)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
TestInterfaceSetlikeNode::TestInterfaceSetlikeNode(JSContext* aCx,
|
||||
nsPIDOMWindow* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
}
|
||||
|
||||
//static
|
||||
already_AddRefed<TestInterfaceSetlikeNode>
|
||||
TestInterfaceSetlikeNode::Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<TestInterfaceSetlikeNode> r = new TestInterfaceSetlikeNode(nullptr, window);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
TestInterfaceSetlikeNode::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return TestInterfaceSetlikeNodeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsPIDOMWindow*
|
||||
TestInterfaceSetlikeNode::GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_TestInterfaceSetlikeNode_h
|
||||
#define mozilla_dom_TestInterfaceSetlikeNode_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GlobalObject;
|
||||
|
||||
// Implementation of test binding for webidl setlike interfaces, using
|
||||
// primitives for key type.
|
||||
class TestInterfaceSetlikeNode final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceSetlikeNode)
|
||||
explicit TestInterfaceSetlikeNode(JSContext* aCx,
|
||||
nsPIDOMWindow* aParent);
|
||||
nsPIDOMWindow* GetParentObject() const;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
static already_AddRefed<TestInterfaceSetlikeNode>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
|
||||
private:
|
||||
virtual ~TestInterfaceSetlikeNode() {}
|
||||
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TestInterfaceSetlikeNode_h
|
||||
@@ -14,3 +14,5 @@ support-files =
|
||||
skip-if = e10s # prerendering doesn't work in e10s yet
|
||||
[test_kill_longrunning_prerendered_content.xul]
|
||||
skip-if = e10s # prerendering doesn't work in e10s yet
|
||||
[test_bug1123516_maplikesetlikechrome.xul]
|
||||
skip-if = debug == false
|
||||
|
||||
@@ -62,3 +62,5 @@ skip-if = debug == false
|
||||
[test_unforgeablesonexpando.html]
|
||||
[test_crossOriginWindowSymbolAccess.html]
|
||||
[test_callback_exceptions.html]
|
||||
[test_bug1123516_maplikesetlike.html]
|
||||
skip-if = debug == false
|
||||
@@ -17,6 +17,7 @@ Library('dombindings_test_s')
|
||||
EXTRA_COMPONENTS += [
|
||||
'TestInterfaceJS.js',
|
||||
'TestInterfaceJS.manifest',
|
||||
'TestInterfaceJSMaplike.js'
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Maplike Interface</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script class="testbody" type="application/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
|
||||
|
||||
base_properties = [["has", "function", 1],
|
||||
["entries", "function", 0],
|
||||
["keys", "function", 0],
|
||||
["values", "function", 0],
|
||||
["forEach", "function", 1],
|
||||
["size", "number"]];
|
||||
maplike_properties = base_properties.concat([["set", "function", 2]]);
|
||||
setlike_properties = base_properties;
|
||||
rw_properties = [["clear", "function", 0],
|
||||
["delete", "function", 1]];
|
||||
setlike_rw_properties = base_properties.concat(rw_properties).concat([["add", "function", 1]]);
|
||||
maplike_rw_properties = maplike_properties.concat(rw_properties).concat([["get", "function",1]]);
|
||||
var testExistence = function testExistence(prefix, obj, properties) {
|
||||
for (var [name, type, args] of properties) {
|
||||
// Properties are somewhere up the proto chain, hasOwnProperty won't work
|
||||
isnot(obj[name], undefined,
|
||||
`${prefix} object has property ${name}`);
|
||||
|
||||
is(typeof obj[name], type,
|
||||
`${prefix} object property ${name} is a ${type}`);
|
||||
// Check function length
|
||||
if (type == "function") {
|
||||
is(obj[name].length, args,
|
||||
`${prefix} object property ${name} is length ${args}`);
|
||||
is(obj[name].name, name,
|
||||
`${prefix} object method name is ${name}`);
|
||||
}
|
||||
|
||||
// Find where property is on proto chain, check for enumerablility there.
|
||||
var owner = obj;
|
||||
while (owner) {
|
||||
var propDesc = Object.getOwnPropertyDescriptor(owner, name);
|
||||
if (propDesc) {
|
||||
ok(!propDesc.enumerable,
|
||||
`${prefix} object property ${name} is not enumerable`);
|
||||
break;
|
||||
}
|
||||
owner = Object.getPrototypeOf(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var m;
|
||||
var testSet;
|
||||
var testIndex;
|
||||
var iterable;
|
||||
// Simple map creation and functionality test
|
||||
info("SimpleMap: Testing simple map creation and functionality");
|
||||
m = new TestInterfaceMaplike();
|
||||
ok(m, "SimpleMap: got a TestInterfaceMaplike object");
|
||||
testExistence("SimpleMap: ", m, maplike_rw_properties);
|
||||
is(m.size, 0, "SimpleMap: size should be zero");
|
||||
ok(!m.has("test"), "SimpleMap: maplike has should return false");
|
||||
is(m.get("test"), undefined, "SimpleMap: maplike get should return undefined on bogus lookup");
|
||||
m1 = m.set("test", 1);
|
||||
is(m, m1, "SimpleMap: return from set should be map object");
|
||||
is(m.size, 1, "SimpleMap: size should be 1");
|
||||
ok(m.has("test"), "SimpleMap: maplike has should return true");
|
||||
is(m.get("test"), 1, "SimpleMap: maplike get should return value entered");
|
||||
m2 = m.set("test2", 2);
|
||||
is(m.size, 2, "SimpleMap: size should be 2");
|
||||
testSet = [["test", 1], ["test2", 2]];
|
||||
testIndex = 0;
|
||||
m.forEach(function(v, k, o) {
|
||||
"use strict";
|
||||
is(o, m, "SimpleMap: foreach obj is correct");
|
||||
is(k, testSet[testIndex][0], "SimpleMap: foreach map key: " + k + " = " + testSet[testIndex][0]);
|
||||
is(v, testSet[testIndex][1], "SimpleMap: foreach map value: " + v + " = " + testSet[testIndex][1]);
|
||||
testIndex += 1;
|
||||
});
|
||||
is(testIndex, 2, "SimpleMap: foreach ran correct number of times");
|
||||
ok(m.has("test2"), "SimpleMap: maplike has should return true");
|
||||
is(m.get("test2"), 2, "SimpleMap: maplike get should return value entered");
|
||||
is(m.delete("test2"), true, "SimpleMap: maplike deletion should return boolean");
|
||||
is(m.size, 1, "SimpleMap: size should be 1");
|
||||
iterable = false;
|
||||
for (var e of m) {
|
||||
iterable = true;
|
||||
is(e[0], "test", "SimpleMap: iterable first array element should be key");
|
||||
is(e[1], 1, "SimpleMap: iterable second array element should be value");
|
||||
}
|
||||
is(m[Symbol.iterator].length, 0, "SimpleMap: @@iterator symbol is correct length");
|
||||
is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleMap: @@iterator symbol has correct name");
|
||||
ok(iterable, "SimpleMap: @@iterator symbol resolved correctly");
|
||||
for (var k of m.keys()) {
|
||||
is(k, "test", "SimpleMap: first keys element should be 'test'");
|
||||
}
|
||||
for (var v of m.values()) {
|
||||
is(v, 1, "SimpleMap: first values elements should be 1");
|
||||
}
|
||||
for (var e of m.entries()) {
|
||||
is(e[0], "test", "SimpleMap: entries first array element should be 'test'");
|
||||
is(e[1], 1, "SimpleMap: entries second array element should be 1");
|
||||
}
|
||||
m.clear();
|
||||
is(m.size, 0, "SimpleMap: size should be 0 after clear");
|
||||
|
||||
// Simple set creation and functionality test
|
||||
info("SimpleSet: Testing simple set creation and functionality");
|
||||
m = new TestInterfaceSetlike();
|
||||
ok(m, "SimpleSet: got a TestInterfaceSetlike object");
|
||||
testExistence("SimpleSet: ", m, setlike_rw_properties);
|
||||
is(m.size, 0, "SimpleSet: size should be zero");
|
||||
ok(!m.has("test"), "SimpleSet: maplike has should return false");
|
||||
m1 = m.add("test");
|
||||
is(m, m1, "SimpleSet: return from set should be map object");
|
||||
is(m.size, 1, "SimpleSet: size should be 1");
|
||||
ok(m.has("test"), "SimpleSet: maplike has should return true");
|
||||
m2 = m.add("test2");
|
||||
is(m.size, 2, "SimpleSet: size should be 2");
|
||||
testSet = ["test", "test2"];
|
||||
testIndex = 0;
|
||||
m.forEach(function(v, k, o) {
|
||||
"use strict";
|
||||
is(o, m, "SimpleSet: foreach obj is correct");
|
||||
is(k, testSet[testIndex], "SimpleSet: foreach set key: " + k + " = " + testSet[testIndex]);
|
||||
testIndex += 1;
|
||||
});
|
||||
is(testIndex, 2, "SimpleSet: foreach ran correct number of times");
|
||||
ok(m.has("test2"), "SimpleSet: maplike has should return true");
|
||||
is(m.delete("test2"), true, "SimpleSet: maplike deletion should return true");
|
||||
is(m.size, 1, "SimpleSet: size should be 1");
|
||||
iterable = false;
|
||||
for (var e of m) {
|
||||
iterable = true;
|
||||
is(e, "test", "SimpleSet: iterable first array element should be key");
|
||||
}
|
||||
is(m[Symbol.iterator].length, 0, "SimpleSet: @@iterator symbol is correct length");
|
||||
is(m[Symbol.iterator].name, "[Symbol.iterator]", "SimpleSet: @@iterator symbol has correct name");
|
||||
ok(iterable, "SimpleSet: @@iterator symbol resolved correctly");
|
||||
for (var k of m.keys()) {
|
||||
is(k, "test", "SimpleSet: first keys element should be 'test'");
|
||||
}
|
||||
for (var v of m.values()) {
|
||||
is(v, "test", "SimpleSet: first values elements should be 'test'");
|
||||
}
|
||||
for (var e of m.entries()) {
|
||||
is(e[0], "test", "SimpleSet: Entries first array element should be 'test'");
|
||||
is(e[1], "test", "SimpleSet: Entries second array element should be 'test'");
|
||||
}
|
||||
m.clear();
|
||||
is(m.size, 0, "SimpleSet: size should be 0 after clear");
|
||||
|
||||
// Map convenience function test
|
||||
info("Testing map convenience functions");
|
||||
m = new TestInterfaceMaplike();
|
||||
ok(m, "MapConvenience: got a TestInterfaceMaplike object");
|
||||
is(m.size, 0, "MapConvenience: size should be zero");
|
||||
ok(!m.hasInternal("test"), "MapConvenience: maplike hasInternal should return false");
|
||||
m.setInternal("test", 1);
|
||||
is(m.size, 1, "MapConvenience: size should be 1");
|
||||
ok(m.hasInternal("test"), "MapConvenience: maplike hasInternal should return true");
|
||||
is(m.get("test"), 1, "MapConvenience: maplike get should return value entered");
|
||||
m2 = m.setInternal("test2", 2);
|
||||
is(m.size, 2, "size should be 2");
|
||||
ok(m.hasInternal("test2"), "MapConvenience: maplike hasInternal should return true");
|
||||
is(m.get("test2"), 2, "MapConvenience: maplike get should return value entered");
|
||||
is(m.deleteInternal("test2"), true, "MapConvenience: maplike deleteInternal should return true");
|
||||
is(m.size, 1, "MapConvenience: size should be 1");
|
||||
m.clearInternal();
|
||||
is(m.size, 0, "MapConvenience: size should be 0 after clearInternal");
|
||||
|
||||
// Map convenience function test using objects and readonly
|
||||
|
||||
info("Testing Map convenience function test using objects and readonly");
|
||||
m = new TestInterfaceMaplikeObject();
|
||||
ok(m, "ReadOnlyMapConvenience: got a TestInterfaceMaplikeObject object");
|
||||
is(m.size, 0, "ReadOnlyMapConvenience: size should be zero");
|
||||
is(m["set"], undefined, "ReadOnlyMapConvenience: readonly map, should be no set function");
|
||||
is(m["clear"], undefined, "ReadOnlyMapConvenience: readonly map, should be no clear function");
|
||||
is(m["delete"], undefined, "ReadOnlyMapConvenience: readonly map, should be no delete function");
|
||||
ok(!m.hasInternal("test"), "ReadOnlyMapConvenience: maplike hasInternal should return false");
|
||||
m.setInternal("test");
|
||||
is(m.size, 1, "size should be 1");
|
||||
ok(m.hasInternal("test"), "ReadOnlyMapConvenience: maplike hasInternal should return true");
|
||||
m2 = m.setInternal("test2");
|
||||
is(m.size, 2, "size should be 2");
|
||||
ok(m.hasInternal("test2"), "ReadOnlyMapConvenience: maplike hasInternal should return true");
|
||||
is(m.deleteInternal("test2"), true, "ReadOnlyMapConvenience: maplike deleteInternal should return true");
|
||||
is(m.size, 1, "ReadOnlyMapConvenience: size should be 1");
|
||||
m.clearInternal();
|
||||
is(m.size, 0, "ReadOnlyMapConvenience: size should be 0 after clearInternal");
|
||||
|
||||
// JS implemented map creation convenience function test
|
||||
|
||||
info("JSMapConvenience: Testing JS implemented map creation convenience functions");
|
||||
m = new TestInterfaceJSMaplike();
|
||||
ok(m, "JSMapConvenience: got a TestInterfaceJSMaplike object");
|
||||
is(m.size, 0, "JSMapConvenience: size should be zero");
|
||||
ok(!m.has("test"), "JSMapConvenience: maplike has should return false");
|
||||
m.setInternal("test", 1);
|
||||
is(m.size, 1, "JSMapConvenience: size should be 1");
|
||||
ok(m.has("test"), "JSMapConvenience: maplike has should return true");
|
||||
is(m.get("test"), 1, "JSMapConvenience: maplike get should return value entered");
|
||||
m2 = m.setInternal("test2", 2);
|
||||
is(m.size, 2, "JSMapConvenience: size should be 2");
|
||||
ok(m.has("test2"), "JSMapConvenience: maplike has should return true");
|
||||
is(m.get("test2"), 2, "JSMapConvenience: maplike get should return value entered");
|
||||
is(m.deleteInternal("test2"), true, "JSMapConvenience: maplike deleteInternal should return true");
|
||||
is(m.size, 1, "JSMapConvenience: size should be 1");
|
||||
for (var k of m.keys()) {
|
||||
is(k, "test", "JSMapConvenience: first keys element should be 'test'");
|
||||
}
|
||||
for (var v of m.values()) {
|
||||
is(v, 1, "JSMapConvenience: first values elements should be 1");
|
||||
}
|
||||
for (var e of m.entries()) {
|
||||
is(e[0], "test", "JSMapConvenience: entries first array element should be 'test'");
|
||||
is(e[1], 1, "JSMapConvenience: entries second array element should be 1");
|
||||
}
|
||||
m.clearInternal();
|
||||
is(m.size, 0, "JSMapConvenience: size should be 0 after clearInternal");
|
||||
|
||||
// Test this override for forEach
|
||||
info("ForEachThisOverride: Testing this override for forEach");
|
||||
m = new TestInterfaceMaplike();
|
||||
m.set("test", 1);
|
||||
m.forEach(function(v, k, o) {
|
||||
"use strict";
|
||||
is(o, m, "ForEachThisOverride: foreach obj is correct");
|
||||
is(this, 5, "ForEachThisOverride: 'this' value should be correct");
|
||||
}, 5);
|
||||
|
||||
// Test defaulting arguments on maplike to undefined
|
||||
info("MapArgsDefault: Testing maplike defaulting arguments to undefined");
|
||||
m = new TestInterfaceMaplike();
|
||||
m.set();
|
||||
is(m.size, 1, "MapArgsDefault: should have 1 entry");
|
||||
m.forEach(function(v, k) {
|
||||
"use strict";
|
||||
is(typeof k, "string", "MapArgsDefault: key is a string");
|
||||
is(k, "undefined", "MapArgsDefault: key is the string undefined");
|
||||
is(v, 0, "MapArgsDefault: value is 0");
|
||||
});
|
||||
is(m.get(), 0, "MapArgsDefault: no argument to get() returns correct value");
|
||||
m.delete();
|
||||
is(m.size, 0, "MapArgsDefault: should have 0 entries");
|
||||
|
||||
// Test defaulting arguments on setlike to undefined
|
||||
info("SetArgsDefault: Testing setlike defaulting arguments to undefined");
|
||||
m = new TestInterfaceSetlike();
|
||||
m.add();
|
||||
is(m.size, 1, "SetArgsDefault: should have 1 entry");
|
||||
m.forEach(function(v, k) {
|
||||
"use strict";
|
||||
is(typeof k, "string", "SetArgsDefault: key is a string");
|
||||
is(k, "undefined", "SetArgsDefault: key is the string undefined");
|
||||
});
|
||||
m.delete();
|
||||
is(m.size, 0, "SetArgsDefault: should have 0 entries");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1123516
|
||||
-->
|
||||
<window title="Mozilla Bug 1123516"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<iframe id="t"></iframe>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1123516"
|
||||
target="_blank">Mozilla Bug 1123516</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for Bug 1123516 **/
|
||||
const Cu = Components.utils;
|
||||
function doTest() {
|
||||
var win = $("t").contentWindow;
|
||||
var sandbox = Components.utils.Sandbox(win, { sandboxPrototype: win });
|
||||
is(sandbox._content, undefined, "_content does nothing over Xray");
|
||||
// Test cross-compartment usage of maplike/setlike WebIDL structures.
|
||||
SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
|
||||
try {
|
||||
var maplike = Components.utils.evalInSandbox("var m = new TestInterfaceMaplike(); m;", sandbox);
|
||||
maplike.set("test2", 2);
|
||||
is(maplike.get("test2"), 2, "Should be able to create and use maplike/setlike across compartments");
|
||||
var test = Components.utils.evalInSandbox("m.get('test2');", sandbox);
|
||||
is(test, 2, "Maplike/setlike should still work in original compartment");
|
||||
is(maplike.size, 1, "Testing size retrieval across compartments");
|
||||
} catch(e) {
|
||||
ok(false, "Shouldn't throw when working with cross-compartment maplike/setlike interfaces " + e)
|
||||
};
|
||||
try {
|
||||
var setlike = Components.utils.evalInSandbox("var m = new TestInterfaceSetlikeNode(); m.add(document.documentElement); m;", sandbox);
|
||||
is(TestInterfaceSetlikeNode.prototype.has.call(setlike, win.document.documentElement), true,
|
||||
"Cross-compartment unwrapping/comparison has works");
|
||||
// TODO: Should throw until iterators are handled by Xrays, Bug 1023984
|
||||
try {
|
||||
var e = TestInterfaceSetlikeNode.prototype.keys.call(setlike);
|
||||
ok(false, "Calling iterators via xrays should fail");
|
||||
} catch(e) {
|
||||
ok(true, "Calling iterators via xrays should fail");
|
||||
}
|
||||
|
||||
setlike.forEach((v,k,t) => { is(v, win.document.documentElement, "Cross-compartment forEach works"); });
|
||||
TestInterfaceSetlikeNode.prototype.forEach.call(setlike,
|
||||
(v,k,t) => { is(v, win.document.documentElement, "Cross-compartment forEach works"); });
|
||||
is(TestInterfaceSetlikeNode.prototype.delete.call(setlike, win.document.documentElement), true,
|
||||
"Cross-compartment unwrapping/comparison delete works");
|
||||
} catch(e) {
|
||||
ok(false, "Shouldn't throw when working with cross-compartment maplike/setlike interfaces " + e)
|
||||
};
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTest);
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
||||
@@ -931,7 +931,7 @@ EventListenerManager::CompileEventHandlerInternal(Listener* aListener,
|
||||
NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Get the reflector for |aElement|, so that we can pass to setElement.
|
||||
if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, target, aElement, &v))) {
|
||||
if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, aElement, &v))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JS::CompileOptions options(cx);
|
||||
|
||||
@@ -195,11 +195,11 @@ public:
|
||||
}
|
||||
void GetReferrer(nsAString& aReferrer)
|
||||
{
|
||||
GetEnumAttr(nsGkAtoms::referrer, nullptr, aReferrer);
|
||||
GetHTMLAttr(nsGkAtoms::referrer, aReferrer);
|
||||
}
|
||||
|
||||
mozilla::net::ReferrerPolicy
|
||||
GetImageReferrerPolicy()
|
||||
net::ReferrerPolicy
|
||||
GetImageReferrerPolicy() override
|
||||
{
|
||||
return GetReferrerPolicy();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
|
||||
// nsWrapperCache
|
||||
using nsWrapperCache::GetWrapperPreserveColor;
|
||||
using nsWrapperCache::GetWrapper;
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
protected:
|
||||
virtual ~HTMLOptionsCollection();
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
|
||||
// nsWrapperCache
|
||||
using nsWrapperCache::GetWrapperPreserveColor;
|
||||
using nsWrapperCache::GetWrapper;
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
protected:
|
||||
virtual ~HTMLPropertiesCollection();
|
||||
|
||||
@@ -108,8 +108,6 @@
|
||||
#include "imgIContainer.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
||||
@@ -243,17 +243,6 @@ public:
|
||||
void GetInnerText(mozilla::dom::DOMString& aValue, mozilla::ErrorResult& aError);
|
||||
void SetInnerText(const nsAString& aValue);
|
||||
|
||||
mozilla::net::ReferrerPolicy
|
||||
GetReferrerPolicy()
|
||||
{
|
||||
nsAutoString aPolicyString;
|
||||
GetEnumAttr(nsGkAtoms::referrer, nullptr, aPolicyString);
|
||||
if (aPolicyString.IsEmpty()) {
|
||||
return mozilla::net::RP_Unset;
|
||||
}
|
||||
return mozilla::net::ReferrerPolicyFromString(aPolicyString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an attribute is an event (onclick, etc.)
|
||||
* @param aName the attribute
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "nsIDOMHTMLCollection.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
class nsINode;
|
||||
@@ -82,6 +83,14 @@ public:
|
||||
{
|
||||
return GetWrapperPreserveColorInternal();
|
||||
}
|
||||
JSObject* GetWrapper()
|
||||
{
|
||||
JSObject* obj = GetWrapperPreserveColor();
|
||||
if (obj) {
|
||||
JS::ExposeObjectToActiveJS(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) = 0;
|
||||
protected:
|
||||
virtual JSObject* GetWrapperPreserveColorInternal() = 0;
|
||||
|
||||
@@ -49,7 +49,7 @@ interface nsIJSRAIIHelper;
|
||||
interface nsIContentPermissionRequest;
|
||||
interface nsIObserver;
|
||||
|
||||
[scriptable, uuid(e7b44320-8255-4ad1-bbe9-d78a8a1867c9)]
|
||||
[scriptable, uuid(bbcb87fb-ce2e-4e05-906b-9258687664e2)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@@ -1739,7 +1739,8 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
* If the property is NOT currently being animated on the compositor thread,
|
||||
* returns an empty string.
|
||||
*/
|
||||
AString getOMTAStyle(in nsIDOMElement aElement, in AString aProperty);
|
||||
AString getOMTAStyle(in nsIDOMElement aElement, in AString aProperty,
|
||||
[optional] in AString aPseudoElement);
|
||||
|
||||
/**
|
||||
* Special function that gets a property syncronously from the last composite
|
||||
@@ -1849,6 +1850,15 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
* Decrease the chaos mode activation level. See enterChaosMode().
|
||||
*/
|
||||
void leaveChaosMode();
|
||||
|
||||
/**
|
||||
* Returns whether the document's style set's rule processor for the
|
||||
* specified level of the cascade is shared by multiple style sets.
|
||||
* (Used by tests to ensure that certain optimizations do not regress.)
|
||||
*
|
||||
* @param aSheetType One of the nsIStyleSheetService.*_SHEET constants.
|
||||
*/
|
||||
bool hasRuleProcessorUsedByMultipleStyleSets(in unsigned long aSheetType);
|
||||
};
|
||||
|
||||
[scriptable, uuid(c694e359-7227-4392-a138-33c0cc1f15a6)]
|
||||
|
||||
@@ -50,7 +50,7 @@ AbortablePromise::Create(nsIGlobalObject* aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsRefPtr<AbortablePromise> p = new AbortablePromise(aGlobal, aAbortCallback);
|
||||
p->CreateWrapper(aRv);
|
||||
p->CreateWrapper(nullptr, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -65,7 +65,8 @@ AbortablePromise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
|
||||
/* static */ already_AddRefed<AbortablePromise>
|
||||
AbortablePromise::Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||
AbortCallback& aAbortCallback, ErrorResult& aRv)
|
||||
AbortCallback& aAbortCallback, ErrorResult& aRv,
|
||||
JS::Handle<JSObject*> aDesiredProto)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global;
|
||||
global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
@@ -75,7 +76,7 @@ AbortablePromise::Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||
}
|
||||
|
||||
nsRefPtr<AbortablePromise> promise = new AbortablePromise(global);
|
||||
promise->CreateWrapper(aRv);
|
||||
promise->CreateWrapper(aDesiredProto, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,8 @@ public:
|
||||
|
||||
static already_AddRefed<AbortablePromise>
|
||||
Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||
AbortCallback& aAbortCallback, ErrorResult& aRv);
|
||||
AbortCallback& aAbortCallback, ErrorResult& aRv,
|
||||
JS::Handle<JSObject*> aDesiredProto);
|
||||
|
||||
void Abort();
|
||||
|
||||
|
||||
@@ -419,10 +419,11 @@ Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
|
||||
Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv,
|
||||
JS::Handle<JSObject*> aDesiredProto)
|
||||
{
|
||||
nsRefPtr<Promise> p = new Promise(aGlobal);
|
||||
p->CreateWrapper(aRv);
|
||||
p->CreateWrapper(aDesiredProto, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -430,7 +431,7 @@ Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
void
|
||||
Promise::CreateWrapper(ErrorResult& aRv)
|
||||
Promise::CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv)
|
||||
{
|
||||
AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(mGlobal)) {
|
||||
@@ -440,7 +441,7 @@ Promise::CreateWrapper(ErrorResult& aRv)
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JS::Rooted<JS::Value> wrapper(cx);
|
||||
if (!GetOrCreateDOMReflector(cx, this, &wrapper)) {
|
||||
if (!GetOrCreateDOMReflector(cx, this, &wrapper, aDesiredProto)) {
|
||||
JS_ClearPendingException(cx);
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
@@ -640,8 +641,8 @@ Promise::CreateThenableFunction(JSContext* aCx, Promise* aPromise, uint32_t aTas
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Promise>
|
||||
Promise::Constructor(const GlobalObject& aGlobal,
|
||||
PromiseInit& aInit, ErrorResult& aRv)
|
||||
Promise::Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||
ErrorResult& aRv, JS::Handle<JSObject*> aDesiredProto)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global;
|
||||
global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
@@ -650,7 +651,7 @@ Promise::Constructor(const GlobalObject& aGlobal,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Create(global, aRv);
|
||||
nsRefPtr<Promise> promise = Create(global, aRv, aDesiredProto);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+11
-8
@@ -99,7 +99,9 @@ public:
|
||||
// object, so we addref before doing that and return the addrefed pointer
|
||||
// here.
|
||||
static already_AddRefed<Promise>
|
||||
Create(nsIGlobalObject* aGlobal, ErrorResult& aRv);
|
||||
Create(nsIGlobalObject* aGlobal, ErrorResult& aRv,
|
||||
// Passing null for aDesiredProto will use Promise.prototype.
|
||||
JS::Handle<JSObject*> aDesiredProto = nullptr);
|
||||
|
||||
typedef void (Promise::*MaybeFunc)(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue);
|
||||
@@ -158,7 +160,7 @@ public:
|
||||
|
||||
static already_AddRefed<Promise>
|
||||
Constructor(const GlobalObject& aGlobal, PromiseInit& aInit,
|
||||
ErrorResult& aRv);
|
||||
ErrorResult& aRv, JS::Handle<JSObject*> aDesiredProto);
|
||||
|
||||
static already_AddRefed<Promise>
|
||||
Resolve(const GlobalObject& aGlobal,
|
||||
@@ -204,6 +206,10 @@ public:
|
||||
// Return a unique-to-the-process identifier for this Promise.
|
||||
uint64_t GetID();
|
||||
|
||||
// Queue an async microtask to current main or worker thread.
|
||||
static void
|
||||
DispatchToMicroTask(nsIRunnable* aRunnable);
|
||||
|
||||
protected:
|
||||
// Do NOT call this unless you're Promise::Create. I wish we could enforce
|
||||
// that from inside this class too, somehow.
|
||||
@@ -211,12 +217,9 @@ protected:
|
||||
|
||||
virtual ~Promise();
|
||||
|
||||
// Queue an async microtask to current main or worker thread.
|
||||
static void
|
||||
DispatchToMicroTask(nsIRunnable* aRunnable);
|
||||
|
||||
// Do JS-wrapping after Promise creation.
|
||||
void CreateWrapper(ErrorResult& aRv);
|
||||
// Do JS-wrapping after Promise creation. Passing null for aDesiredProto will
|
||||
// use the default prototype for the sort of Promise we have.
|
||||
void CreateWrapper(JS::Handle<JSObject*> aDesiredProto, ErrorResult& aRv);
|
||||
|
||||
// Create the JS resolving functions of resolve() and reject(). And provide
|
||||
// references to the two functions by calling PromiseInit passed from Promise
|
||||
|
||||
@@ -7,7 +7,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=741267
|
||||
<window title="Mozilla Bug 741267"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
|
||||
</script>
|
||||
<iframe id="t"></iframe>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
[Constructor(),
|
||||
Pref="dom.expose_test_interfaces"]
|
||||
interface TestInterfaceMaplike {
|
||||
maplike<DOMString, long>;
|
||||
void setInternal(DOMString aKey, long aValue);
|
||||
void clearInternal();
|
||||
boolean deleteInternal(DOMString aKey);
|
||||
boolean hasInternal(DOMString aKey);
|
||||
};
|
||||
|
||||
[Constructor(),
|
||||
Pref="dom.expose_test_interfaces"]
|
||||
interface TestInterfaceMaplikeObject {
|
||||
readonly maplike<DOMString, TestInterfaceMaplike>;
|
||||
void setInternal(DOMString aKey);
|
||||
void clearInternal();
|
||||
boolean deleteInternal(DOMString aKey);
|
||||
boolean hasInternal(DOMString aKey);
|
||||
};
|
||||
|
||||
[Pref="dom.expose_test_interfaces",
|
||||
JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
|
||||
Constructor()]
|
||||
interface TestInterfaceJSMaplike {
|
||||
readonly maplike<DOMString, long>;
|
||||
void setInternal(DOMString aKey, long aValue);
|
||||
void clearInternal();
|
||||
boolean deleteInternal(DOMString aKey);
|
||||
};
|
||||
|
||||
[Constructor(),
|
||||
Pref="dom.expose_test_interfaces"]
|
||||
interface TestInterfaceSetlike {
|
||||
setlike<DOMString>;
|
||||
};
|
||||
|
||||
[Constructor(),
|
||||
Pref="dom.expose_test_interfaces"]
|
||||
interface TestInterfaceSetlikeNode {
|
||||
setlike<Node>;
|
||||
};
|
||||
@@ -655,7 +655,9 @@ WEBIDL_FILES += [
|
||||
# We only expose our prefable test interfaces in debug builds, just to be on
|
||||
# the safe side.
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
WEBIDL_FILES += ['TestInterfaceJS.webidl', 'TestInterfaceJSDictionaries.webidl']
|
||||
WEBIDL_FILES += ['TestInterfaceJS.webidl',
|
||||
'TestInterfaceJSDictionaries.webidl',
|
||||
'TestInterfaceJSMaplikeSetlike.webidl']
|
||||
|
||||
if CONFIG['MOZ_B2G_BT']:
|
||||
if CONFIG['MOZ_B2G_BT_API_V1']:
|
||||
|
||||
+3
-4
@@ -83,9 +83,8 @@ Decoder::Init()
|
||||
// No re-initializing
|
||||
MOZ_ASSERT(!mInitialized, "Can't re-initialize a decoder!");
|
||||
|
||||
// Fire OnStartDecode at init time to support bug 512435.
|
||||
if (!IsSizeDecode()) {
|
||||
mProgress |= FLAG_DECODE_STARTED | FLAG_ONLOAD_BLOCKED;
|
||||
mProgress |= FLAG_DECODE_STARTED;
|
||||
}
|
||||
|
||||
// Implementation-specific initialization
|
||||
@@ -273,7 +272,7 @@ Decoder::CompleteDecode()
|
||||
} else {
|
||||
// We're not usable. Record some final progress indicating the error.
|
||||
if (!IsSizeDecode()) {
|
||||
mProgress |= FLAG_DECODE_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
|
||||
mProgress |= FLAG_DECODE_COMPLETE;
|
||||
}
|
||||
mProgress |= FLAG_HAS_ERROR;
|
||||
}
|
||||
@@ -615,7 +614,7 @@ Decoder::PostFrameStop(Opacity aFrameOpacity /* = Opacity::TRANSPARENT */,
|
||||
|
||||
mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod);
|
||||
|
||||
mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
|
||||
mProgress |= FLAG_FRAME_COMPLETE;
|
||||
|
||||
// If we're not sending partial invalidations, then we send an invalidation
|
||||
// here when the first frame is complete.
|
||||
|
||||
@@ -342,5 +342,11 @@ DynamicImage::Unwrap()
|
||||
return self.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DynamicImage::PropagateUseCounters(nsIDocument*)
|
||||
{
|
||||
// No use counters.
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -293,6 +293,12 @@ ImageWrapper::SetAnimationStartTime(const TimeStamp& aTime)
|
||||
mInnerImage->SetAnimationStartTime(aTime);
|
||||
}
|
||||
|
||||
void
|
||||
ImageWrapper::PropagateUseCounters(nsIDocument* aParentDocument)
|
||||
{
|
||||
mInnerImage->PropagateUseCounters(aParentDocument);
|
||||
}
|
||||
|
||||
nsIntSize
|
||||
ImageWrapper::OptimalImageSizeForDest(const gfxSize& aDest,
|
||||
uint32_t aWhichFrame,
|
||||
|
||||
@@ -1242,11 +1242,9 @@ RasterImage::NotifyForLoadEvent(Progress aProgress)
|
||||
// to draw. (We may have already sent some of these notifications from
|
||||
// NotifyForDecodeOnlyOnDraw(), but ProgressTracker will ensure no duplicate
|
||||
// notifications get sent.)
|
||||
aProgress |= FLAG_ONLOAD_BLOCKED |
|
||||
FLAG_DECODE_STARTED |
|
||||
aProgress |= FLAG_DECODE_STARTED |
|
||||
FLAG_FRAME_COMPLETE |
|
||||
FLAG_DECODE_COMPLETE |
|
||||
FLAG_ONLOAD_UNBLOCKED;
|
||||
FLAG_DECODE_COMPLETE;
|
||||
}
|
||||
|
||||
// If we encountered an error, make sure we notify for that as well.
|
||||
@@ -1268,7 +1266,7 @@ RasterImage::NotifyForDecodeOnlyOnDraw()
|
||||
return;
|
||||
}
|
||||
|
||||
NotifyProgress(FLAG_DECODE_STARTED | FLAG_ONLOAD_BLOCKED);
|
||||
NotifyProgress(FLAG_DECODE_STARTED);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -2174,6 +2172,12 @@ RasterImage::Unwrap()
|
||||
return self.forget();
|
||||
}
|
||||
|
||||
void
|
||||
RasterImage::PropagateUseCounters(nsIDocument*)
|
||||
{
|
||||
// No use counters.
|
||||
}
|
||||
|
||||
IntSize
|
||||
RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
|
||||
GraphicsFilter aFilter, uint32_t aFlags)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "mozilla/dom/SVGAnimatedLength.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "DOMSVGLength.h"
|
||||
#include "nsDocument.h"
|
||||
|
||||
// undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
|
||||
#undef GetCurrentTime
|
||||
|
||||
@@ -1261,6 +1261,15 @@ VectorImage::InvalidateObserversOnNextRefreshDriverTick()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VectorImage::PropagateUseCounters(nsIDocument* aParentDocument)
|
||||
{
|
||||
nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
|
||||
if (doc) {
|
||||
doc->PropagateUseCounters(aParentDocument);
|
||||
}
|
||||
}
|
||||
|
||||
nsIntSize
|
||||
VectorImage::OptimalImageSizeForDest(const gfxSize& aDest,
|
||||
uint32_t aWhichFrame,
|
||||
|
||||
+10
-1
@@ -18,6 +18,8 @@
|
||||
#include "nsSize.h"
|
||||
#include "limits.h"
|
||||
|
||||
class nsIDocument;
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
class LayerManager;
|
||||
@@ -107,6 +109,7 @@ native Orientation(mozilla::image::Orientation);
|
||||
native TempRefSourceSurface(already_AddRefed<mozilla::gfx::SourceSurface>);
|
||||
native TempRefImgIContainer(already_AddRefed<imgIContainer>);
|
||||
native nsIntSizeByVal(nsIntSize);
|
||||
[ptr] native nsIDocument(nsIDocument);
|
||||
|
||||
|
||||
/**
|
||||
@@ -116,7 +119,7 @@ native nsIntSizeByVal(nsIntSize);
|
||||
*
|
||||
* Internally, imgIContainer also manages animation of images.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(f5e1230a-3733-477f-b49b-fee8717a786b)]
|
||||
[scriptable, builtinclass, uuid(4880727a-5673-44f7-b248-f6c86e22a434)]
|
||||
interface imgIContainer : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -544,4 +547,10 @@ interface imgIContainer : nsISupports
|
||||
* Removes any ImageWrappers and returns the unwrapped base image.
|
||||
*/
|
||||
[notxpcom, nostdcall] TempRefImgIContainer unwrap();
|
||||
|
||||
/*
|
||||
* Propagate the use counters (if any) from this container to the passed in
|
||||
* document.
|
||||
*/
|
||||
[noscript, notxpcom] void propagateUseCounters(in nsIDocument aDocument);
|
||||
};
|
||||
|
||||
@@ -41,6 +41,23 @@ function forceDecodeImg() {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
}
|
||||
|
||||
function runAfterAsyncEvents(aCallback) {
|
||||
function handlePostMessage(aEvent) {
|
||||
if (aEvent.data == 'next') {
|
||||
window.removeEventListener('message', handlePostMessage, false);
|
||||
aCallback();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('message', handlePostMessage, false);
|
||||
|
||||
// We'll receive the 'message' event after everything else that's currently in
|
||||
// the event queue (which is a stronger guarantee than setTimeout, because
|
||||
// setTimeout events may be coalesced). This lets us ensure that we run
|
||||
// aCallback *after* any asynchronous events are delivered.
|
||||
window.postMessage('next', '*');
|
||||
}
|
||||
|
||||
function test() {
|
||||
// Enable the discarding pref.
|
||||
oldDiscardingPref = prefBranch.getBoolPref('discardable');
|
||||
@@ -72,6 +89,13 @@ function step2() {
|
||||
|
||||
// Check that the image is decoded.
|
||||
forceDecodeImg();
|
||||
|
||||
// The FRAME_COMPLETE notification is delivered asynchronously, so continue
|
||||
// after we're sure it has been delivered.
|
||||
runAfterAsyncEvents(() => step3(result, scriptedObserver, clonedRequest));
|
||||
}
|
||||
|
||||
function step3(result, scriptedObserver, clonedRequest) {
|
||||
ok(isImgDecoded(), 'Image should initially be decoded.');
|
||||
|
||||
// Focus the old tab, then fire a memory-pressure notification. This should
|
||||
@@ -81,18 +105,12 @@ function step2() {
|
||||
.getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(null, 'memory-pressure', 'heap-minimize');
|
||||
|
||||
// The discard notification is delivered asynchronously, so pump the event
|
||||
// loop before checking.
|
||||
window.addEventListener('message', function (event) {
|
||||
if (event.data == 'step3') {
|
||||
step3(result, scriptedObserver, clonedRequest);
|
||||
}
|
||||
}, false);
|
||||
|
||||
window.postMessage('step3', '*');
|
||||
// The DISCARD notification is delivered asynchronously, so continue after
|
||||
// we're sure it has been delivered.
|
||||
runAfterAsyncEvents(() => step4(result, scriptedObserver, clonedRequest));
|
||||
}
|
||||
|
||||
function step3(result, scriptedObserver, clonedRequest) {
|
||||
function step4(result, scriptedObserver, clonedRequest) {
|
||||
ok(result.wasDiscarded, 'Image should be discarded.');
|
||||
|
||||
// And we're done.
|
||||
|
||||
@@ -70,7 +70,6 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
|
||||
[test_bug496292.html]
|
||||
[test_bug497665.html]
|
||||
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
|
||||
[test_bug512435.html]
|
||||
[test_bug552605-1.html]
|
||||
[test_bug552605-2.html]
|
||||
[test_bug553982.html]
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=512435
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 512435</title>
|
||||
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/image/test/mochitest/imgutils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=512435">Mozilla Bug 512435</a>
|
||||
<img id="img_a">
|
||||
<img id="img_b">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
// Boilerplate
|
||||
const Ci = SpecialPowers.Ci;
|
||||
const Cc = SpecialPowers.Cc;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// We're relying on very particular behavior for certain images - clear the
|
||||
// image cache, _then_ set src
|
||||
clearImageCache();
|
||||
document.getElementById("img_a").src = "damon.jpg";
|
||||
document.getElementById("img_b").src = "shaver.png";
|
||||
|
||||
// Our handler
|
||||
function loadHandler() {
|
||||
|
||||
// The two images should be decoded
|
||||
ok(isFrameDecoded("img_a"), "img_a should be decoded before onload fires");
|
||||
ok(isFrameDecoded("img_b"), "img_b should be decoded before onload fires");
|
||||
|
||||
// All done
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// Set our onload handler
|
||||
window.onload = loadHandler;
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -291,9 +291,9 @@ SandboxFetch(JSContext* cx, JS::HandleObject scope, const CallArgs& args)
|
||||
FetchRequest(global, Constify(request), Constify(options), rv);
|
||||
rv.WouldReportJSException();
|
||||
if (rv.Failed()) {
|
||||
return ThrowMethodFailedWithDetails(cx, rv, "Sandbox", "fetch");
|
||||
return ThrowMethodFailed(cx, rv);
|
||||
}
|
||||
if (!GetOrCreateDOMReflector(cx, scope, response, args.rval())) {
|
||||
if (!GetOrCreateDOMReflector(cx, response, args.rval())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1558,6 +1558,15 @@ public:
|
||||
// Whether we should assume all images are visible.
|
||||
virtual bool AssumeAllImagesVisible() = 0;
|
||||
|
||||
/**
|
||||
* Returns whether the document's style set's rule processor for the
|
||||
* specified level of the cascade is shared by multiple style sets.
|
||||
*
|
||||
* @param aSheetType One of the nsIStyleSheetService.*_SHEET constants.
|
||||
*/
|
||||
nsresult HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
|
||||
bool* aRetVal);
|
||||
|
||||
/**
|
||||
* Refresh observer management.
|
||||
*/
|
||||
|
||||
@@ -10884,3 +10884,27 @@ nsIPresShell::SyncWindowProperties(nsView* aView)
|
||||
nsContainerFrame::SyncWindowProperties(mPresContext, frame, aView, &rcx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIPresShell::HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
|
||||
bool* aRetVal)
|
||||
{
|
||||
nsStyleSet::sheetType type;
|
||||
switch (aSheetType) {
|
||||
case nsIStyleSheetService::AGENT_SHEET:
|
||||
type = nsStyleSet::eAgentSheet;
|
||||
break;
|
||||
case nsIStyleSheetService::USER_SHEET:
|
||||
type = nsStyleSet::eUserSheet;
|
||||
break;
|
||||
case nsIStyleSheetService::AUTHOR_SHEET:
|
||||
type = nsStyleSet::eDocSheet;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false, "unexpected aSheetType value");
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
*aRetVal = mStyleSet->HasRuleProcessorUsedByMultipleStyleSets(type);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -713,6 +713,16 @@ nsBulletFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aDa
|
||||
InvalidateFrame();
|
||||
}
|
||||
|
||||
if (aType == imgINotificationObserver::DECODE_COMPLETE) {
|
||||
if (nsIDocument* parent = GetOurCurrentDoc()) {
|
||||
nsCOMPtr<imgIContainer> container;
|
||||
aRequest->GetImage(getter_AddRefs(container));
|
||||
if (container) {
|
||||
container->PropagateUseCounters(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@ fuzzy(16,460) fuzzy-if(Android,10,3667) == box-decoration-break-with-outset-box-
|
||||
random-if(!gtkWidget) HTTP(..) == box-decoration-break-border-image.html box-decoration-break-border-image-ref.html
|
||||
== box-decoration-break-block-border-padding.html box-decoration-break-block-border-padding-ref.html
|
||||
== box-decoration-break-block-margin.html box-decoration-break-block-margin-ref.html
|
||||
fuzzy-if(Android,8,6627) == box-decoration-break-first-letter.html box-decoration-break-first-letter-ref.html
|
||||
fuzzy-if(!Android,1,5) fuzzy-if(Android,8,6627) == box-decoration-break-first-letter.html box-decoration-break-first-letter-ref.html
|
||||
== box-decoration-break-bug-1249913.html box-decoration-break-bug-1249913-ref.html
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
== bug-364968.html bug-364968-ref.html
|
||||
random == bug-364968.html bug-364968-ref.html
|
||||
== bug-463204.html bug-463204-ref.html
|
||||
fails-if(Android&&AndroidVersion<15&&AndroidVersion!=10) == canvas-outside-document.html canvas-inside-document.html
|
||||
== mozsetimageelement-01.html mozsetimageelement-01-ref.html
|
||||
|
||||
@@ -416,6 +416,14 @@ ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData
|
||||
return OnFrameUpdate(aRequest);
|
||||
}
|
||||
|
||||
if (aType == imgINotificationObserver::DECODE_COMPLETE) {
|
||||
nsCOMPtr<imgIContainer> image;
|
||||
aRequest->GetImage(getter_AddRefs(image));
|
||||
if (image && mDocument) {
|
||||
image->PropagateUseCounters(mDocument);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +198,7 @@ public:
|
||||
mInRuleProcessorCache = aVal;
|
||||
}
|
||||
bool IsInRuleProcessorCache() const { return mInRuleProcessorCache; }
|
||||
bool IsUsedByMultipleStyleSets() const { return mStyleSetRefCnt > 1; }
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Cached theme identifier for the moz-windows-theme media query.
|
||||
|
||||
@@ -2469,3 +2469,15 @@ nsStyleSet::InitialStyleRule()
|
||||
}
|
||||
return mInitialStyleRule;
|
||||
}
|
||||
|
||||
bool
|
||||
nsStyleSet::HasRuleProcessorUsedByMultipleStyleSets(sheetType aSheetType)
|
||||
{
|
||||
MOZ_ASSERT(size_t(aSheetType) < ArrayLength(mRuleProcessors));
|
||||
if (!IsCSSSheetType(aSheetType) || !mRuleProcessors[aSheetType]) {
|
||||
return false;
|
||||
}
|
||||
nsCSSRuleProcessor* rp =
|
||||
static_cast<nsCSSRuleProcessor*>(mRuleProcessors[aSheetType].get());
|
||||
return rp->IsUsedByMultipleStyleSets();
|
||||
}
|
||||
|
||||
@@ -395,6 +395,8 @@ class nsStyleSet final
|
||||
|
||||
nsIStyleRule* InitialStyleRule();
|
||||
|
||||
bool HasRuleProcessorUsedByMultipleStyleSets(sheetType aSheetType);
|
||||
|
||||
private:
|
||||
nsStyleSet(const nsStyleSet& aCopy) = delete;
|
||||
nsStyleSet& operator=(const nsStyleSet& aCopy) = delete;
|
||||
|
||||
@@ -396,19 +396,23 @@ const ExpectComparisonTo = {
|
||||
};
|
||||
|
||||
(function() {
|
||||
window.omta_todo_is = function(elem, property, expected, runningOn, desc) {
|
||||
window.omta_todo_is = function(elem, property, expected, runningOn, desc,
|
||||
pseudo) {
|
||||
return omta_is_approx(elem, property, expected, 0, runningOn, desc,
|
||||
ExpectComparisonTo.Fail);
|
||||
ExpectComparisonTo.Fail, pseudo);
|
||||
};
|
||||
|
||||
window.omta_is = function(elem, property, expected, runningOn, desc) {
|
||||
return omta_is_approx(elem, property, expected, 0, runningOn, desc);
|
||||
window.omta_is = function(elem, property, expected, runningOn, desc,
|
||||
pseudo) {
|
||||
return omta_is_approx(elem, property, expected, 0, runningOn, desc,
|
||||
ExpectComparisonTo.Pass, pseudo);
|
||||
};
|
||||
|
||||
// Many callers of this method will pass 'undefined' for
|
||||
// expectedComparisonResult.
|
||||
window.omta_is_approx = function(elem, property, expected, tolerance,
|
||||
runningOn, desc, expectedComparisonResult) {
|
||||
runningOn, desc, expectedComparisonResult,
|
||||
pseudo) {
|
||||
// Check input
|
||||
const omtaProperties = [ "transform", "opacity" ];
|
||||
if (omtaProperties.indexOf(property) === -1) {
|
||||
@@ -426,8 +430,8 @@ const ExpectComparisonTo = {
|
||||
|
||||
// Get actual values
|
||||
var compositorStr =
|
||||
SpecialPowers.DOMWindowUtils.getOMTAStyle(elem, property);
|
||||
var computedStr = window.getComputedStyle(elem)[property];
|
||||
SpecialPowers.DOMWindowUtils.getOMTAStyle(elem, property, pseudo);
|
||||
var computedStr = window.getComputedStyle(elem, pseudo)[property];
|
||||
|
||||
// Prepare expected value
|
||||
var expectedValue = normalize(expected);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
support-files =
|
||||
bug453896_iframe.html
|
||||
media_queries_iframe.html
|
||||
newtab_share_rule_processors.html
|
||||
|
||||
[browser_bug453896.js]
|
||||
skip-if = e10s # Bug ?????? - test touches content (TypeError: doc.documentElement is null)
|
||||
[browser_newtab_share_rule_processors.js]
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
var theTab;
|
||||
var theBrowser;
|
||||
|
||||
function listener(evt) {
|
||||
if (evt.target == theBrowser.contentDocument) {
|
||||
doTest();
|
||||
}
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
var testURL = getRootDirectory(gTestPath) + "newtab_share_rule_processors.html";
|
||||
theTab = gBrowser.addTab(testURL);
|
||||
theBrowser = gBrowser.getBrowserForTab(theTab);
|
||||
theBrowser.addEventListener("load", listener, true);
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
theBrowser.removeEventListener("load", listener, true);
|
||||
var winUtils = theBrowser.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
// The initial set of agent-level sheets should have a rule processor that's
|
||||
// also being used by another document.
|
||||
ok(winUtils.hasRuleProcessorUsedByMultipleStyleSets(Ci.nsIStyleSheetService.AGENT_SHEET),
|
||||
"agent sheet rule processor is used by multiple style sets");
|
||||
// Document-level sheets currently never get shared rule processors.
|
||||
ok(!winUtils.hasRuleProcessorUsedByMultipleStyleSets(Ci.nsIStyleSheetService.AUTHOR_SHEET),
|
||||
"author sheet rule processor is not used by multiple style sets");
|
||||
// Adding a unique style sheet to the agent level will cause it to have a
|
||||
// rule processor that is unique.
|
||||
theBrowser.contentWindow.wrappedJSObject.addAgentSheet();
|
||||
ok(!winUtils.hasRuleProcessorUsedByMultipleStyleSets(Ci.nsIStyleSheetService.AGENT_SHEET),
|
||||
"agent sheet rule processor is not used by multiple style sets after " +
|
||||
"having a unique sheet added to it");
|
||||
gBrowser.removeTab(theTab);
|
||||
finish();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
p { color: blue; }
|
||||
</style>
|
||||
<p>Hello</p>
|
||||
<script>
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
|
||||
.getService(Ci.nsIStyleSheetService);
|
||||
var io = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
var winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
function addAgentSheet() {
|
||||
var sheetURI = io.newURI("data:text/css,p{background-color:yellow}", null, null);
|
||||
var sheet = sss.preloadSheet(sheetURI, Ci.nsIStyleSheetService.AGENT_SHEET);
|
||||
winUtils.addSheet(sheet, Ci.nsIDOMWindowUtils.AGENT_SHEET);
|
||||
}
|
||||
</script>
|
||||
@@ -86,6 +86,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=964646
|
||||
from, to { transform: translate(50px) }
|
||||
}
|
||||
|
||||
#withbefore::before, #withafter::after {
|
||||
content: "test";
|
||||
animation: anim4 1s linear alternate 3;
|
||||
display:block;
|
||||
}
|
||||
|
||||
@keyframes multiprop {
|
||||
0% {
|
||||
transform: translate(10px); opacity: 0.3;
|
||||
@@ -1435,8 +1441,72 @@ addAsyncAnimTest(function *() {
|
||||
* http://dev.w3.org/csswg/css3-animations/#the-animation-shorthand-property-
|
||||
*/
|
||||
|
||||
// (Unlike test_animations.html, pseudo-elements are not tested here since they
|
||||
// are currently not run on the compositor thread.)
|
||||
/**
|
||||
* Basic tests of animations on pseudo-elements
|
||||
*/
|
||||
addAsyncAnimTest(function *() {
|
||||
new_div("");
|
||||
listen();
|
||||
gDiv.id = "withbefore";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { ty: 0 }, RunningOn.Compositor,
|
||||
":before test at 0ms", "::before");
|
||||
advance_clock(400);
|
||||
omta_is("transform", { ty: 40 }, RunningOn.Compositor,
|
||||
":before test at 400ms", "::before");
|
||||
advance_clock(800);
|
||||
omta_is("transform", { ty: 80 }, RunningOn.Compositor,
|
||||
":before test at 1200ms", "::before");
|
||||
omta_is("transform", { ty: 0 }, RunningOn.MainThread,
|
||||
":before animation should not affect element");
|
||||
advance_clock(800);
|
||||
omta_is("transform", { ty: 0 }, RunningOn.Compositor,
|
||||
":before test at 2000ms", "::before");
|
||||
advance_clock(300);
|
||||
omta_is("transform", { ty: 30 }, RunningOn.Compositor,
|
||||
":before test at 2300ms", "::before");
|
||||
advance_clock(700);
|
||||
check_events([ { type: "animationstart", animationName: "anim4",
|
||||
elapsedTime: 0, pseudoElement: "::before" },
|
||||
{ type: "animationiteration", animationName: "anim4",
|
||||
elapsedTime: 1, pseudoElement: "::before" },
|
||||
{ type: "animationiteration", animationName: "anim4",
|
||||
elapsedTime: 2, pseudoElement: "::before" },
|
||||
{ type: "animationend", animationName: "anim4",
|
||||
elapsedTime: 3, pseudoElement: "::before" }]);
|
||||
done_div();
|
||||
|
||||
new_div("");
|
||||
listen();
|
||||
gDiv.id = "withafter";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { ty: 0 }, RunningOn.Compositor,
|
||||
":after test at 0ms", "::after");
|
||||
advance_clock(400);
|
||||
omta_is("transform", { ty: 40 }, RunningOn.Compositor,
|
||||
":after test at 400ms", "::after");
|
||||
advance_clock(800);
|
||||
omta_is("transform", { ty: 80 }, RunningOn.Compositor,
|
||||
":after test at 1200ms", "::after");
|
||||
omta_is("transform", { ty: 0 }, RunningOn.MainThread,
|
||||
":before animation should not affect element");
|
||||
advance_clock(800);
|
||||
omta_is("transform", { ty: 0 }, RunningOn.Compositor,
|
||||
":after test at 2000ms", "::after");
|
||||
advance_clock(300);
|
||||
omta_is("transform", { ty: 30 }, RunningOn.Compositor,
|
||||
":after test at 2300ms", "::after");
|
||||
advance_clock(700);
|
||||
check_events([ { type: "animationstart", animationName: "anim4",
|
||||
elapsedTime: 0, pseudoElement: "::after" },
|
||||
{ type: "animationiteration", animationName: "anim4",
|
||||
elapsedTime: 1, pseudoElement: "::after" },
|
||||
{ type: "animationiteration", animationName: "anim4",
|
||||
elapsedTime: 2, pseudoElement: "::after" },
|
||||
{ type: "animationend", animationName: "anim4",
|
||||
elapsedTime: 3, pseudoElement: "::after" }]);
|
||||
done_div();
|
||||
});
|
||||
|
||||
/**
|
||||
* Test handling of properties that are present in only some of the
|
||||
|
||||
@@ -19,7 +19,7 @@ enum ReferrerPolicy {
|
||||
|
||||
/* spec tokens: default no-referrer-when-downgrade */
|
||||
RP_No_Referrer_When_Downgrade = nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE,
|
||||
RP_Default = nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE,
|
||||
RP_Default = nsIHttpChannel::REFERRER_POLICY_DEFAULT,
|
||||
|
||||
/* spec tokens: origin-when-cross-origin */
|
||||
RP_Origin_When_Crossorigin = nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
|
||||
|
||||
@@ -15,6 +15,7 @@ import sys
|
||||
import traceback
|
||||
|
||||
from mozbuild.util import FileAvoidWrite
|
||||
from mozbuild.makeutil import Makefile
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser('Generate a file from a Python script',
|
||||
@@ -25,12 +26,24 @@ def main(argv):
|
||||
help='The method of the script to invoke')
|
||||
parser.add_argument('output_file', metavar='output-file', type=str,
|
||||
help='The file to generate')
|
||||
parser.add_argument('dep_file', metavar='dep-file', type=str,
|
||||
help='File to write any additional make dependencies to')
|
||||
parser.add_argument('additional_arguments', metavar='arg', nargs='*',
|
||||
help="Additional arguments to the script's main() method")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
script = args.python_script
|
||||
# Permit the script to import modules from the same directory in which it
|
||||
# resides. The justification for doing this is that if we were invoking
|
||||
# the script as:
|
||||
#
|
||||
# python script arg1...
|
||||
#
|
||||
# then importing modules from the script's directory would come for free.
|
||||
# Since we're invoking the script in a roundabout way, we provide this
|
||||
# bit of convenience.
|
||||
sys.path.append(os.path.dirname(script))
|
||||
with open(script, 'r') as fh:
|
||||
module = imp.load_module('script', fh, script,
|
||||
('.py', 'r', imp.PY_SOURCE))
|
||||
@@ -44,6 +57,16 @@ def main(argv):
|
||||
try:
|
||||
with FileAvoidWrite(args.output_file) as output:
|
||||
ret = module.__dict__[method](output, *args.additional_arguments)
|
||||
# We treat sets as a statement of success. Everything else
|
||||
# is an error (so scripts can conveniently |return 1| or
|
||||
# similar).
|
||||
if isinstance(ret, set) and ret:
|
||||
mk = Makefile()
|
||||
mk.create_rule([args.output_file]).add_dependencies(ret)
|
||||
with FileAvoidWrite(args.dep_file) as dep_file:
|
||||
mk.dump(dep_file)
|
||||
# The script succeeded, so reset |ret| to indicate that.
|
||||
ret = None
|
||||
except IOError as e:
|
||||
print('Error opening file "{0}"'.format(e.filename), file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -502,12 +502,16 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
self._process_exports(obj, obj.exports, backend_file)
|
||||
|
||||
elif isinstance(obj, GeneratedFile):
|
||||
dep_file = "%s.pp" % obj.output
|
||||
backend_file.write('GENERATED_FILES += %s\n' % obj.output)
|
||||
backend_file.write('EXTRA_MDDEPEND_FILES += %s\n' % dep_file)
|
||||
if obj.script:
|
||||
backend_file.write("""{output}: {script}{inputs}
|
||||
\t$(call py_action,file_generate,{script} {method} {output}{inputs})
|
||||
\t$(REPORT_BUILD)
|
||||
\t$(call py_action,file_generate,{script} {method} {output} $(MDDEPDIR)/{dep_file}{inputs})
|
||||
|
||||
""".format(output=obj.output,
|
||||
dep_file=dep_file,
|
||||
inputs=' ' + ' '.join(obj.inputs) if obj.inputs else '',
|
||||
script=obj.script,
|
||||
method=obj.method))
|
||||
|
||||
@@ -693,6 +693,9 @@ VARIABLES = {
|
||||
GENERATED_FILES += ['bar.c']
|
||||
bar = GENERATED_FILES['bar.c']
|
||||
bar.script = 'generate.py:make_bar'
|
||||
|
||||
The chosen script entry point may optionally return a set of strings,
|
||||
indicating extra files the output depends on.
|
||||
""", 'export'),
|
||||
|
||||
'DEFINES': (OrderedDict, dict,
|
||||
|
||||
@@ -375,14 +375,19 @@ class TestRecursiveMakeBackend(BackendTester):
|
||||
|
||||
expected = [
|
||||
'GENERATED_FILES += bar.c',
|
||||
'EXTRA_MDDEPEND_FILES += bar.c.pp',
|
||||
'bar.c: %s/generate-bar.py' % env.topsrcdir,
|
||||
'$(call py_action,file_generate,%s/generate-bar.py baz bar.c)' % env.topsrcdir,
|
||||
'$(REPORT_BUILD)',
|
||||
'$(call py_action,file_generate,%s/generate-bar.py baz bar.c $(MDDEPDIR)/bar.c.pp)' % env.topsrcdir,
|
||||
'',
|
||||
'GENERATED_FILES += foo.c',
|
||||
'EXTRA_MDDEPEND_FILES += foo.c.pp',
|
||||
'foo.c: %s/generate-foo.py %s/foo-data' % (env.topsrcdir, env.topsrcdir),
|
||||
'$(call py_action,file_generate,%s/generate-foo.py main foo.c %s/foo-data)' % (env.topsrcdir, env.topsrcdir),
|
||||
'$(REPORT_BUILD)',
|
||||
'$(call py_action,file_generate,%s/generate-foo.py main foo.c $(MDDEPDIR)/foo.c.pp %s/foo-data)' % (env.topsrcdir, env.topsrcdir),
|
||||
'',
|
||||
'GENERATED_FILES += quux.c',
|
||||
'EXTRA_MDDEPEND_FILES += quux.c.pp',
|
||||
]
|
||||
|
||||
self.maxDiff = None
|
||||
|
||||
@@ -3497,6 +3497,13 @@ GetHistogramById(ID id)
|
||||
return h;
|
||||
}
|
||||
|
||||
const char*
|
||||
GetHistogramName(Telemetry::ID id)
|
||||
{
|
||||
const TelemetryHistogram& h = gHistograms[id];
|
||||
return h.id();
|
||||
}
|
||||
|
||||
void
|
||||
RecordSlowSQLStatement(const nsACString &statement,
|
||||
const nsACString &dbName,
|
||||
|
||||
@@ -76,6 +76,8 @@ void AccumulateTimeDelta(ID id, TimeStamp start, TimeStamp end = TimeStamp::Now(
|
||||
*/
|
||||
base::Histogram* GetHistogramById(ID id);
|
||||
|
||||
const char* GetHistogramName(ID id);
|
||||
|
||||
/**
|
||||
* Return a raw histogram for keyed histograms.
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,8 @@ def main(argv):
|
||||
if seen_use_counters:
|
||||
print " HistogramUseCounterCount = HistogramLastUseCounter - HistogramFirstUseCounter + 1"
|
||||
else:
|
||||
print " HistogramFirstUseCounter = 0,"
|
||||
print " HistogramLastUseCounter = 0,"
|
||||
print " HistogramUseCounterCount = 0"
|
||||
print "};"
|
||||
|
||||
|
||||
@@ -2,16 +2,27 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import buildconfig
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Need to update sys.path to be able to find usecounters.
|
||||
sys.path.append(os.path.join(buildconfig.topsrcdir, 'dom/base/'))
|
||||
import usecounters
|
||||
# histogram_tools.py is used by scripts from a mozilla-central build tree
|
||||
# and also by outside consumers, such as the telemetry server. We need
|
||||
# to ensure that importing things works in both contexts. Therefore,
|
||||
# unconditionally importing things that are local to the build tree, such
|
||||
# as buildconfig, is a no-no.
|
||||
try:
|
||||
import buildconfig
|
||||
|
||||
# Need to update sys.path to be able to find usecounters.
|
||||
sys.path.append(os.path.join(buildconfig.topsrcdir, 'dom/base/'))
|
||||
except ImportError:
|
||||
# Must be in an out-of-tree usage scenario. Trust that whoever is
|
||||
# running this script knows we need the usecounters module and has
|
||||
# ensured it's in our sys.path.
|
||||
pass
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
@@ -270,9 +281,17 @@ def from_UseCounters_conf(filename):
|
||||
|
||||
FILENAME_PARSERS = {
|
||||
'Histograms.json': from_Histograms_json,
|
||||
'UseCounters.conf': from_UseCounters_conf,
|
||||
}
|
||||
|
||||
# Similarly to the dance above with buildconfig, usecounters may not be
|
||||
# available, so handle that gracefully.
|
||||
try:
|
||||
import usecounters
|
||||
|
||||
FILENAME_PARSERS['UseCounters.conf'] = from_UseCounters_conf
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def from_files(filenames):
|
||||
"""Return an iterator that provides a sequence of Histograms for
|
||||
the histograms defined in filenames.
|
||||
|
||||
Reference in New Issue
Block a user