Skip to content

Commit 9fe641c

Browse files
authored
Don't cache parsed value of selector(All) property types (#5521)
Co-authored-by: Noeri Huisman <mrxz@users.noreply.github.com>
1 parent e55b306 commit 9fe641c

File tree

4 files changed

+38
-8
lines changed

4 files changed

+38
-8
lines changed

‎src/core/component.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,8 @@ Component.prototype = {
426426

427427
// Parse the new value into attrValue (re-using objects where possible)
428428
var newAttrValue = key ? this.attrValue[key] : this.attrValue;
429-
newAttrValue = parseProperty(newValue, propertySchema, newAttrValue);
429+
// Some property types (like selectors) depend on external state (e.g. DOM) during parsing and can't be cached.
430+
newAttrValue = propertySchema.isCacheable ? parseProperty(newValue, propertySchema, newAttrValue) : newValue;
430431
// In case the output is a string, store the unparsed value (no double parsing and helps inspector)
431432
if (typeof newAttrValue === 'string') {
432433
// Quirk: empty strings aren't considered values for single-property schemas

‎src/core/propertyTypes.js‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
var coordinates = require('../utils/coordinates');
22
var debug = require('debug');
33

4-
var error = debug('core:propertyTypes:warn');
54
var warn = debug('core:propertyTypes:warn');
65

76
var propertyTypes = module.exports.propertyTypes = {};
@@ -13,15 +12,15 @@ registerPropertyType('audio', '', assetParse);
1312
registerPropertyType('array', [], arrayParse, arrayStringify, arrayEquals);
1413
registerPropertyType('asset', '', assetParse);
1514
registerPropertyType('boolean', false, boolParse);
16-
registerPropertyType('color', '#FFF', defaultParse, defaultStringify);
15+
registerPropertyType('color', '#FFF');
1716
registerPropertyType('int', 0, intParse);
1817
registerPropertyType('number', 0, numberParse);
1918
registerPropertyType('map', '', assetParse);
2019
registerPropertyType('model', '', assetParse);
21-
registerPropertyType('selector', null, selectorParse, selectorStringify);
22-
registerPropertyType('selectorAll', null, selectorAllParse, selectorAllStringify);
20+
registerPropertyType('selector', null, selectorParse, selectorStringify, defaultEquals, false);
21+
registerPropertyType('selectorAll', null, selectorAllParse, selectorAllStringify, arrayEquals, false);
2322
registerPropertyType('src', '', srcParse);
24-
registerPropertyType('string', '', defaultParse, defaultStringify);
23+
registerPropertyType('string', '');
2524
registerPropertyType('time', 0, intParse);
2625
registerPropertyType('vec2', {x: 0, y: 0}, vecParse, coordinates.stringify, coordinates.equals);
2726
registerPropertyType('vec3', {x: 0, y: 0, z: 0}, vecParse, coordinates.stringify, coordinates.equals);
@@ -37,8 +36,9 @@ registerPropertyType('vec4', {x: 0, y: 0, z: 0, w: 1}, vecParse, coordinates.str
3736
* @param {function} [parse=defaultParse] - Parse string function.
3837
* @param {function} [stringify=defaultStringify] - Stringify to DOM function.
3938
* @param {function} [equals=defaultEquals] - Equality comparator.
39+
* @param {boolean} [cachable=false] - Whether or not the parsed value of a property can be cached.
4040
*/
41-
function registerPropertyType (type, defaultValue, parse, stringify, equals) {
41+
function registerPropertyType (type, defaultValue, parse, stringify, equals, cacheable) {
4242
if (type in propertyTypes) {
4343
throw new Error('Property type ' + type + ' is already registered.');
4444
}
@@ -47,7 +47,8 @@ function registerPropertyType (type, defaultValue, parse, stringify, equals) {
4747
default: defaultValue,
4848
parse: parse || defaultParse,
4949
stringify: stringify || defaultStringify,
50-
equals: equals || defaultEquals
50+
equals: equals || defaultEquals,
51+
isCacheable: cacheable !== false
5152
};
5253
}
5354
module.exports.registerPropertyType = registerPropertyType;

‎src/core/schema.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ function processPropertyDefinition (propDefinition, componentName) {
8383
propDefinition.parse = propDefinition.parse || propType.parse;
8484
propDefinition.stringify = propDefinition.stringify || propType.stringify;
8585
propDefinition.equals = propDefinition.equals || propType.equals;
86+
propDefinition.isCacheable = propDefinition.isCacheable === true || propType.isCacheable;
8687

8788
// Fill in type name.
8889
propDefinition.type = typeName;

‎tests/core/a-entity.test.js‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,33 @@ suite('a-entity', function () {
455455
assert.notOk(geometry.width);
456456
});
457457

458+
test('do not cache properties with non-cacheable types', function () {
459+
el.setAttribute('light', { target: '#some-element' });
460+
assert.deepEqual(el.components.light.attrValue, {target: '#some-element'});
461+
462+
const light = el.getAttribute('light');
463+
assert.notOk(light.target);
464+
});
465+
466+
test('non-cacheable selectors are parsed at component initialization', function (done) {
467+
const newEl = document.createElement('a-entity');
468+
newEl.setAttribute('light', { target: '#target-element' });
469+
470+
// Light refers to target that doesn't exist yet.
471+
assert.deepEqual(newEl.components.light.attrValue, {target: '#target-element'});
472+
assert.notOk(newEl.getAttribute('light').target);
473+
474+
// Setup target element.
475+
el.id = 'target-element';
476+
el.appendChild(newEl);
477+
478+
newEl.addEventListener('loaded', function () {
479+
assert.deepEqual(newEl.components.light.attrValue, {target: '#target-element'});
480+
assert.strictEqual(newEl.getAttribute('light').target, el);
481+
done();
482+
});
483+
});
484+
458485
test('parses individual properties when passing object', function (done) {
459486
AFRAME.registerComponent('foo', {
460487
schema: {

0 commit comments

Comments
 (0)