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)
- Bug 1180770 part 1. Remove the unused ThrowNotEnoughArgsError. r=peterv (8bc1690f5)
- Bug 1180770 part 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:
2021-06-29 09:54:02 +08:00
parent 3697b9103c
commit 604a6d61ce
92 changed files with 4040 additions and 376 deletions
+1
View File
@@ -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
+1
View File
@@ -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
+3 -9
View File
@@ -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
View File
@@ -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
+20 -6
View File
@@ -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>
+14
View File
@@ -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;
}
+2
View File
@@ -1105,6 +1105,8 @@ public:
*/
float FontSizeInflation();
net::ReferrerPolicy GetReferrerPolicy();
protected:
/*
* Named-bools for use with SetAttrAndNotify to make call sites easier to
+5 -2
View File
@@ -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"
+26
View File
@@ -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)
+206
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+49 -44
View File
@@ -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.
-1
View File
@@ -424,7 +424,6 @@ private:
bool mBroken : 1;
bool mUserDisabled : 1;
bool mSuppressed : 1;
bool mFireEventsOnDecode : 1;
protected:
/**
+2
View File
@@ -315,8 +315,10 @@ private:
nsScriptObjectTracer* aTracer);
#ifdef DEBUG
public:
void CheckCCWrapperTraversal(void* aScriptObjectHolder,
nsScriptObjectTracer* aTracer);
private:
#endif // DEBUG
/**
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+1 -8
View File
@@ -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;
}
+1 -1
View File
@@ -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);
+9
View File
@@ -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
+19
View File
@@ -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
View File
@@ -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)
+13
View File
@@ -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);
}
}
}
+52
View File
@@ -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;
}
}
}
+46
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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
+1
View File
@@ -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>
+1 -1
View File
@@ -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);
+3 -3
View File
@@ -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();
}
+1
View File
@@ -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();
+1
View File
@@ -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();
-2
View File
@@ -108,8 +108,6 @@
#include "imgIContainer.h"
#include "nsComputedDOMStyle.h"
#include "mozilla/net/ReferrerPolicy.h"
using namespace mozilla;
using namespace mozilla::dom;
-11
View File
@@ -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
+9
View File
@@ -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;
+12 -2
View File
@@ -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)]
+4 -3
View File
@@ -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;
}
+2 -1
View File
@@ -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();
+8 -7
View File
@@ -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
View File
@@ -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>;
};
+3 -1
View File
@@ -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
View File
@@ -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.
+6
View File
@@ -342,5 +342,11 @@ DynamicImage::Unwrap()
return self.forget();
}
void
DynamicImage::PropagateUseCounters(nsIDocument*)
{
// No use counters.
}
} // namespace image
} // namespace mozilla
+6
View File
@@ -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,
+9 -5
View File
@@ -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)
+1
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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);
};
+28 -10
View File
@@ -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.
-1
View File
@@ -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]
-49
View File
@@ -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>
+2 -2
View File
@@ -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;
+9
View File
@@ -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.
*/
+24
View File
@@ -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;
}
+10
View File
@@ -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;
}
+1 -1
View File
@@ -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 -1
View File
@@ -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
+8
View File
@@ -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;
}
+1
View File
@@ -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.
+12
View File
@@ -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();
}
+2
View File
@@ -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;
+11 -7
View File
@@ -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
View File
@@ -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>
+72 -2
View File
@@ -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
+1 -1
View File
@@ -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,
+2
View File
@@ -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.