Skip to content

Commit

Permalink
Add <content>.getDistributedNodes observation. Refactor flush.
Browse files Browse the repository at this point in the history
  • Loading branch information
Steven Orvell committed Oct 14, 2015
1 parent 0ede79a commit 8b1face
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 45 deletions.
66 changes: 30 additions & 36 deletions src/lib/dom-api-flush.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,59 +23,53 @@
_FLUSH_MAX: 100,
_needsTakeRecords: !Polymer.Settings.useNativeCustomElements,
_debouncers: [],
_preFlushList: [],
_staticFlushList: [],
_finishDebouncer: null,

// flush and debounce exposed as statics on Polymer.dom
flush: function() {
for (var i=0; i < this._preFlushList.length; i++) {
this._preFlushList[i]();
this._flushGuard = 0;
this._prepareFlush();
while (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) {
for (var i=0; i < this._debouncers.length; i++) {
this._debouncers[i].complete();
}
// clear the list of debouncers
if (this._finishDebouncer) {
this._finishDebouncer.complete();
}
this._prepareFlush();
this._flushGuard++;
}
if (this._flushGuard >= this._FLUSH_MAX) {
console.warn('Polymer.dom.flush aborted. Flush may not be complete.')
}
this._flush();
},

_flush: function() {
// flush debouncers
for (var i=0; i < this._debouncers.length; i++) {
this._debouncers[i].complete();
}
// clear the list of debouncers
if (this._finishDebouncer) {
this._finishDebouncer.complete();
}
_prepareFlush: function() {
// TODO(sorvell): There is currently not a good way
// to process all custom elements mutations under SD polyfill because
// these mutations may be inside shadowRoots.
// again make any pending CE mutations that might trigger debouncer
// additions go...
this._flushPolyfills();
// flush again if there are now any debouncers to process
if (this._debouncers.length && this._flushGuard < this._FLUSH_MAX) {
this._flushGuard++;
this._flush();
} else {
if (this._flushGuard >= this._FLUSH_MAX) {
console.warn('Polymer.dom.flush aborted. Flush may not be complete.')
}
this._flushGuard = 0;
}
},

// TODO(sorvell): There is currently not a good way
// to process all custom elements mutations under SD polyfill because
// these mutations may be inside shadowRoots.
_flushPolyfills: function() {
if (this._needsTakeRecords) {
CustomElements.takeRecords();
}
for (var i=0; i < this._staticFlushList.length; i++) {
this._staticFlushList[i]();
}
},

addPreflush: function(fn) {
this._preFlushList.push(fn);
// add to the static list of methods to call when flushing
addStaticFlush: function(fn) {
this._staticFlushList.push(fn);
},

// TODO(sorvell): Map when we can?
removePreflush: function(fn) {
var i = this._preFlushList.indexOf(fn);
// remove a function from the static list of methods to call when flushing
removeStaticFlush: function(fn) {
var i = this._staticFlushList.indexOf(fn);
if (i >= 0) {
this._preFlushList.splice(i, 1);
this._staticFlushList.splice(i, 1);
}
},

Expand Down
4 changes: 2 additions & 2 deletions src/lib/dom-api-observe-distributed-nodes.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
// (but note that <slot> will)
_beforeCallListeners: function() {},

_generateListenerInfo: function() {
return true;
_getEffectiveNodes: function() {
return this.domApi.getDistributedNodes();
}

});
Expand Down
23 changes: 17 additions & 6 deletions src/lib/dom-api-observe-nodes.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@
},

_observeContent: function(content) {
return Polymer.dom(content).observeNodes(this._scheduleNotify.bind(this));
var h = Polymer.dom(content).observeNodes(this._scheduleNotify.bind(this));
h._avoidChangeCalculation = true;
return h;
},

_unobserveContentElements: function(elements) {
Expand All @@ -127,7 +129,7 @@

_callListeners: function() {
var o$ = this._listeners;
var nodes = this.domApi.getEffectiveChildNodes();
var nodes = this._getEffectiveNodes();
for (var i=0, o; (i < o$.length) && (o=o$[i]); i++) {
var info = this._generateListenerInfo(o, nodes);
if (info || o._alwaysCallListener) {
Expand All @@ -136,7 +138,14 @@
}
},

_getEffectiveNodes: function() {
return this.domApi.getEffectiveChildNodes()
},

_generateListenerInfo: function(listener, newNodes) {
if (listener._avoidChangeCalculation) {
return true;
}
var oldNodes = listener._nodes;
var info = {
target: this.node,
Expand Down Expand Up @@ -185,11 +194,13 @@
if (!this._observer) {
var self = this;
this._mutationHandler = function(mxns) {
self._scheduleNotify();
if (mxns && mxns.length) {
self._scheduleNotify();
}
};
this._observer = new MutationObserver(this._mutationHandler);
this._preflush = this._flush.bind(this);
Polymer.dom.addPreflush(this._preflush);
this._boundFlush = this._flush.bind(this);
Polymer.dom.addStaticFlush(this._boundFlush);
// NOTE: subtree true is way too aggressive, but it easily catches
// attribute changes on children. These changes otherwise require
// attribute observers on every child. Testing has shown this
Expand All @@ -205,7 +216,7 @@
this._observer.disconnect();
this._observer = null;
this._mutationHandler = null;
Polymer.dom.removePreflush(this._preflush);
Polymer.dom.removeStaticFlush(this._boundFlush);
baseCleanup.call(this);
},

Expand Down
112 changes: 111 additions & 1 deletion test/unit/polymer-dom-observeNodes.html
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,31 @@
document.body.removeChild(el);
});

test('observe children changes to distributing element that provoke additional changes', function() {
var el = document.createElement('test-content');
document.body.appendChild(el);

var recordedInfo, elAddedInObserver;
var observerCallCount = 0;
var handle = Polymer.dom(el).observeNodes(function(info) {
if (Polymer.dom(info.target).childNodes.length < 5) {
elAddedInObserver = document.createElement('div');
Polymer.dom(info.target).appendChild(elAddedInObserver);
}
observerCallCount++;
recordedInfo = info;
});
// add
var d = document.createElement('div');
Polymer.dom(el).appendChild(d);
Polymer.dom.flush();
assert.equal(observerCallCount, 5);
assert.equal(recordedInfo.addedNodes.length, 1);
assert.equal(recordedInfo.addedNodes[0], elAddedInObserver);
assert.equal(Polymer.dom(el).childNodes.length, 5);
document.body.removeChild(el);
});

test('observe children changes to distributing element (async)', function(done) {
var el = document.createElement('test-content');
document.body.appendChild(el);
Expand Down Expand Up @@ -321,7 +346,7 @@
document.body.removeChild(el);
});

test('observe changes to inner node with <content>', function() {
test('observe changes to inner node wrapping <content>', function() {
var el = document.createElement('test-content');
document.body.appendChild(el);
var observedInfo;
Expand All @@ -335,6 +360,7 @@
Polymer.dom(el).appendChild(d);
Polymer.dom(el).appendChild(d1);
Polymer.dom.flush();
assert.equal(observedInfo.target, el.$.contentContainer);
assert.equal(observedInfo.addedNodes.length, 2);
assert.equal(observedInfo.removedNodes.length, 0);
// remove
Expand All @@ -359,6 +385,45 @@
document.body.removeChild(el);
});

test('observe changes to <content>', function() {
var el = document.createElement('test-content');
document.body.appendChild(el);
var observedInfo;
var handle = Polymer.dom(el.$.content).observeNodes(function(info) {
observedInfo = info;
});
Polymer.dom.flush();
// add
var d = document.createElement('div');
var d1 = document.createElement('div');
Polymer.dom(el).appendChild(d);
Polymer.dom(el).appendChild(d1);
Polymer.dom.flush();
assert.equal(observedInfo.target, el.$.content);
assert.equal(observedInfo.addedNodes.length, 2);
assert.equal(observedInfo.removedNodes.length, 0);
// remove
Polymer.dom(el).removeChild(d);
Polymer.dom(el).removeChild(d1);
Polymer.dom.flush();
assert.equal(observedInfo.addedNodes.length, 0);
assert.equal(observedInfo.removedNodes.length, 2);
// add
Polymer.dom(el).appendChild(d);
Polymer.dom(el).appendChild(d1);
Polymer.dom.flush();
assert.equal(observedInfo.addedNodes.length, 2);
assert.equal(observedInfo.removedNodes.length, 0);
// reset, unobserve and remove
observedInfo = null;
Polymer.dom(el.$.content).unobserveNodes(handle);
Polymer.dom(el).removeChild(d);
Polymer.dom(el).removeChild(d1);
Polymer.dom.flush();
assert.equal(observedInfo, null);
document.body.removeChild(el);
});

test('observe effective children inside distributing element', function() {
var el = document.createElement('test-content1');
document.body.appendChild(el);
Expand Down Expand Up @@ -446,6 +511,51 @@
document.body.removeChild(el);
});

test('observe <content> inside deep distributing element', function() {
var el = document.createElement('test-content3');
document.body.appendChild(el);

var recorded;
var content = el.$.content.$.content.$.content.$.content;
assert.equal(content.localName, 'content');
var handle = Polymer.dom(content).observeNodes(function(info) {
recorded = info;
});
// add
var d = document.createElement('div');
var d1 = document.createElement('div');
Polymer.dom(el).appendChild(d);
Polymer.dom(el).appendChild(d1);
Polymer.dom.flush();
assert.equal(recorded.addedNodes.length, 2);
assert.equal(recorded.removedNodes.length, 0);
assert.equal(recorded.addedNodes[0], d);
assert.equal(recorded.addedNodes[1], d1);
// remove
Polymer.dom(el).removeChild(d);
Polymer.dom(el).removeChild(d1);
Polymer.dom.flush();
assert.equal(recorded.addedNodes.length, 0);
assert.equal(recorded.removedNodes.length, 2);
assert.equal(recorded.removedNodes[0], d);
assert.equal(recorded.removedNodes[1], d1);
// add
Polymer.dom(el).appendChild(d);
Polymer.dom(el).appendChild(d1);
Polymer.dom.flush();
assert.equal(recorded.addedNodes.length, 2);
assert.equal(recorded.addedNodes[0], d);
assert.equal(recorded.addedNodes[1], d1);
// reset, unobserve and remove
recorded = null;
Polymer.dom(content).unobserveNodes(handle);
Polymer.dom(el).removeChild(d);
Polymer.dom(el).removeChild(d1);
Polymer.dom.flush();
assert.equal(recorded, null);
document.body.removeChild(el);
});

test('observe effective children inside deep distributing element (async)', function(done) {
var el = document.createElement('test-content3');
document.body.appendChild(el);
Expand Down

0 comments on commit 8b1face

Please sign in to comment.