Skip to content
This repository was archived by the owner on Mar 13, 2018. It is now read-only.

Commit e1d3e26

Browse files
author
Steve Orvell
committed
Merge pull request #388 from arv/tree-scope
Introduce a TreeScope so we can get to the root node in O(1)
2 parents 4cc6fd4 + 1637a92 commit e1d3e26

11 files changed

+168
-43
lines changed

build.json

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"src/wrappers.js",
55
"src/microtask.js",
66
"src/MutationObserver.js",
7+
"src/TreeScope.js",
78
"src/wrappers/events.js",
89
"src/wrappers/NodeList.js",
910
"src/wrappers/HTMLCollection.js",

shadowdom.js

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
'src/wrappers.js',
2121
'src/microtask.js',
2222
'src/MutationObserver.js',
23+
"src/TreeScope.js",
2324
'src/wrappers/events.js',
2425
'src/wrappers/NodeList.js',
2526
'src/wrappers/HTMLCollection.js',

src/ShadowRenderer.js

+14-7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
var Node = scope.wrappers.Node;
1212
var ShadowRoot = scope.wrappers.ShadowRoot;
1313
var assert = scope.assert;
14+
var getTreeScope = scope.getTreeScope;
1415
var mixin = scope.mixin;
1516
var oneOf = scope.oneOf;
1617
var unwrap = scope.unwrap;
@@ -230,7 +231,11 @@
230231
// TODO(arv): Order these in document order. That way we do not have to
231232
// render something twice.
232233
for (var i = 0; i < pendingDirtyRenderers.length; i++) {
233-
pendingDirtyRenderers[i].render();
234+
var renderer = pendingDirtyRenderers[i];
235+
var parentRenderer = renderer.parentRenderer;
236+
if (parentRenderer && parentRenderer.dirty)
237+
continue;
238+
renderer.render();
234239
}
235240

236241
pendingDirtyRenderers = [];
@@ -256,10 +261,9 @@
256261
}
257262

258263
function getShadowRootAncestor(node) {
259-
for (; node; node = node.parentNode) {
260-
if (node instanceof ShadowRoot)
261-
return node;
262-
}
264+
var root = getTreeScope(node).root;
265+
if (root instanceof ShadowRoot)
266+
return root;
263267
return null;
264268
}
265269

@@ -376,6 +380,10 @@
376380
this.dirty = false;
377381
},
378382

383+
get parentRenderer() {
384+
return getTreeScope(this.host).renderer;
385+
},
386+
379387
invalidate: function() {
380388
if (!this.dirty) {
381389
this.dirty = true;
@@ -406,8 +414,7 @@
406414

407415
if (isShadowHost(node)) {
408416
var renderer = getRendererForHost(node);
409-
// renderNode.skip = !renderer.dirty;
410-
renderer.invalidate();
417+
renderNode.skip = !renderer.dirty;
411418
renderer.render(renderNode);
412419
} else {
413420
for (var child = node.firstChild; child; child = child.nextSibling) {

src/TreeScope.js

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright 2014 The Polymer Authors. All rights reserved.
3+
* Use of this source code is goverened by a BSD-style
4+
* license that can be found in the LICENSE file.
5+
*/
6+
7+
(function(scope) {
8+
'use strict';
9+
10+
/**
11+
* A tree scope represents the root of a tree. All nodes in a tree point to
12+
* the same TreeScope object. The tree scope of a node get set the first time
13+
* it is accessed or when a node is added or remove to a tree.
14+
* @constructor
15+
*/
16+
function TreeScope(root, parent) {
17+
this.root = root;
18+
this.parent = parent;
19+
}
20+
21+
TreeScope.prototype = {
22+
get renderer() {
23+
if (this.root instanceof scope.wrappers.ShadowRoot) {
24+
return scope.getRendererForHost(this.root.host);
25+
}
26+
return null;
27+
},
28+
29+
contains: function(treeScope) {
30+
for (; treeScope; treeScope = treeScope.parent) {
31+
if (treeScope === this)
32+
return true;
33+
}
34+
return false;
35+
}
36+
};
37+
38+
function setTreeScope(node, treeScope) {
39+
if (node.treeScope_ !== treeScope) {
40+
node.treeScope_ = treeScope;
41+
for (var child = node.firstChild; child; child = child.nextSibling) {
42+
setTreeScope(child, treeScope);
43+
}
44+
}
45+
}
46+
47+
function getTreeScope(node) {
48+
if (node.treeScope_)
49+
return node.treeScope_;
50+
var parent = node.parentNode;
51+
var treeScope;
52+
if (parent)
53+
treeScope = getTreeScope(parent);
54+
else
55+
treeScope = new TreeScope(node, null);
56+
return node.treeScope_ = treeScope;
57+
}
58+
59+
scope.TreeScope = TreeScope;
60+
scope.getTreeScope = getTreeScope;
61+
scope.setTreeScope = setTreeScope;
62+
63+
})(window.ShadowDOMPolyfill);

src/wrappers/Document.js

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
var Selection = scope.wrappers.Selection;
1212
var SelectorsInterface = scope.SelectorsInterface;
1313
var ShadowRoot = scope.wrappers.ShadowRoot;
14+
var TreeScope = scope.TreeScope;
1415
var cloneNode = scope.cloneNode;
1516
var defineWrapGetter = scope.defineWrapGetter;
1617
var elementFromPoint = scope.elementFromPoint;
@@ -29,6 +30,7 @@
2930

3031
function Document(node) {
3132
Node.call(this, node);
33+
this.treeScope_ = new TreeScope(this, null);
3234
}
3335
Document.prototype = Object.create(Node.prototype);
3436

src/wrappers/HTMLElement.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@
190190
});
191191

192192
nodesWereRemoved(removedNodes);
193-
nodesWereAdded(addedNodes);
193+
nodesWereAdded(addedNodes, this);
194194
},
195195

196196
get outerHTML() {

src/wrappers/Node.js

+26-13
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1-
// Copyright 2012 The Polymer Authors. All rights reserved.
2-
// Use of this source code is goverened by a BSD-style
3-
// license that can be found in the LICENSE file.
1+
/**
2+
* Copyright 2012 The Polymer Authors. All rights reserved.
3+
* Use of this source code is goverened by a BSD-style
4+
* license that can be found in the LICENSE file.
5+
*/
46

57
(function(scope) {
68
'use strict';
79

810
var EventTarget = scope.wrappers.EventTarget;
911
var NodeList = scope.wrappers.NodeList;
12+
var TreeScope = scope.TreeScope;
1013
var assert = scope.assert;
1114
var defineWrapGetter = scope.defineWrapGetter;
1215
var enqueueMutation = scope.enqueueMutation;
16+
var getTreeScope = scope.getTreeScope;
1317
var isWrapper = scope.isWrapper;
1418
var mixin = scope.mixin;
1519
var registerTransientObservers = scope.registerTransientObservers;
1620
var registerWrapper = scope.registerWrapper;
21+
var setTreeScope = scope.setTreeScope;
1722
var unwrap = scope.unwrap;
1823
var wrap = scope.wrap;
1924
var wrapIfNeeded = scope.wrapIfNeeded;
@@ -130,23 +135,27 @@
130135
}
131136

132137
// http://dom.spec.whatwg.org/#node-is-inserted
133-
function nodeWasAdded(node) {
138+
function nodeWasAdded(node, treeScope) {
139+
setTreeScope(node, treeScope);
134140
node.nodeIsInserted_();
135141
}
136142

137-
function nodesWereAdded(nodes) {
143+
function nodesWereAdded(nodes, parent) {
144+
var treeScope = getTreeScope(parent);
138145
for (var i = 0; i < nodes.length; i++) {
139-
nodeWasAdded(nodes[i]);
146+
nodeWasAdded(nodes[i], treeScope);
140147
}
141148
}
142149

143150
// http://dom.spec.whatwg.org/#node-is-removed
144151
function nodeWasRemoved(node) {
145-
// Nothing at this point in time.
152+
setTreeScope(node, new TreeScope(node, null));
146153
}
147154

148155
function nodesWereRemoved(nodes) {
149-
// Nothing at this point in time.
156+
for (var i = 0; i < nodes.length; i++) {
157+
nodeWasRemoved(nodes[i]);
158+
}
150159
}
151160

152161
function ensureSameOwnerDocument(parent, child) {
@@ -265,7 +274,9 @@
265274
}
266275

267276
function contains(self, child) {
268-
// TODO(arv): Optimize using ownerDocument etc.
277+
if (!child || getTreeScope(self) !== getTreeScope(child))
278+
return false;
279+
269280
for (var node = child; node; node = node.parentNode) {
270281
if (node === self)
271282
return true;
@@ -319,6 +330,8 @@
319330
* @private
320331
*/
321332
this.previousSibling_ = undefined;
333+
334+
this.treeScope_ = undefined;
322335
}
323336

324337
var OriginalDocumentFragment = window.DocumentFragment;
@@ -407,7 +420,7 @@
407420
previousSibling: previousNode
408421
});
409422

410-
nodesWereAdded(nodes);
423+
nodesWereAdded(nodes, this);
411424

412425
return childWrapper;
413426
},
@@ -539,7 +552,7 @@
539552
});
540553

541554
nodeWasRemoved(oldChildWrapper);
542-
nodesWereAdded(nodes);
555+
nodesWereAdded(nodes, this);
543556

544557
return oldChildWrapper;
545558
},
@@ -631,7 +644,7 @@
631644
});
632645

633646
nodesWereRemoved(removedNodes);
634-
nodesWereAdded(addedNodes);
647+
nodesWereAdded(addedNodes, this);
635648
},
636649

637650
get childNodes() {
@@ -706,12 +719,12 @@
706719
delete Node.prototype.querySelectorAll;
707720
Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype);
708721

722+
scope.cloneNode = cloneNode;
709723
scope.nodeWasAdded = nodeWasAdded;
710724
scope.nodeWasRemoved = nodeWasRemoved;
711725
scope.nodesWereAdded = nodesWereAdded;
712726
scope.nodesWereRemoved = nodesWereRemoved;
713727
scope.snapshotNodeList = snapshotNodeList;
714728
scope.wrappers.Node = Node;
715-
scope.cloneNode = cloneNode;
716729

717730
})(window.ShadowDOMPolyfill);

src/wrappers/ShadowRoot.js

+4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
'use strict';
77

88
var DocumentFragment = scope.wrappers.DocumentFragment;
9+
var TreeScope = scope.TreeScope;
910
var elementFromPoint = scope.elementFromPoint;
1011
var getInnerHTML = scope.getInnerHTML;
12+
var getTreeScope = scope.getTreeScope;
1113
var mixin = scope.mixin;
1214
var rewrap = scope.rewrap;
1315
var setInnerHTML = scope.setInnerHTML;
@@ -26,6 +28,8 @@
2628
// DocumentFragment instance. Override that.
2729
rewrap(node, this);
2830

31+
this.treeScope_ = new TreeScope(this, getTreeScope(hostWrapper));
32+
2933
var oldShadowRoot = hostWrapper.shadowRoot;
3034
nextOlderShadowTreeTable.set(this, oldShadowRoot);
3135

src/wrappers/events.js

+5-21
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
'use strict';
77

88
var forwardMethodsToWrapper = scope.forwardMethodsToWrapper;
9+
var getTreeScope = scope.getTreeScope;
910
var mixin = scope.mixin;
1011
var registerWrapper = scope.registerWrapper;
1112
var unwrap = scope.unwrap;
@@ -160,27 +161,10 @@
160161
return getInsertionParent(node);
161162
}
162163

163-
function rootOfNode(node) {
164-
var p;
165-
while (p = node.parentNode) {
166-
node = p;
167-
}
168-
return node;
169-
}
170-
171164
function inSameTree(a, b) {
172-
return rootOfNode(a) === rootOfNode(b);
173-
}
174-
175-
function enclosedBy(a, b) {
176-
if (a === b)
177-
return true;
178-
if (a instanceof wrappers.ShadowRoot)
179-
return enclosedBy(rootOfNode(a.host), b);
180-
return false;
165+
return getTreeScope(a) === getTreeScope(b);
181166
}
182167

183-
184168
function dispatchOriginalEvent(originalEvent) {
185169
// Make sure this event is only dispatched once.
186170
if (handledEventsTable.get(originalEvent))
@@ -395,12 +379,12 @@
395379
if (eventPath) {
396380
var index = 0;
397381
var lastIndex = eventPath.length - 1;
398-
var baseRoot = rootOfNode(currentTargetTable.get(this));
382+
var baseRoot = getTreeScope(currentTargetTable.get(this));
399383

400384
for (var i = 0; i <= lastIndex; i++) {
401385
var currentTarget = eventPath[i].currentTarget;
402-
var currentRoot = rootOfNode(currentTarget);
403-
if (enclosedBy(baseRoot, currentRoot) &&
386+
var currentRoot = getTreeScope(currentTarget);
387+
if (currentRoot.contains(baseRoot) &&
404388
// Make sure we do not add Window to the path.
405389
(i !== lastIndex || currentTarget instanceof wrappers.Node)) {
406390
nodeList[index++] = currentTarget;

0 commit comments

Comments
 (0)