Support upgrading existing elements already on the page.
R=justinfagnani@google.com
Review URL: https://codereview.chromium.org//333073002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/web_components@37338 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/dart_support.js b/lib/dart_support.js
index 81e7d9b..efa1301 100644
--- a/lib/dart_support.js
+++ b/lib/dart_support.js
@@ -70,6 +70,9 @@
(function (doc) {
var upgraders = {}; // upgrader associated with a custom-tag.
var unpatchableTags = {}; // set of custom-tags that can't be patched.
+ var pendingElements = {}; // will upgrade when/if an upgrader is installed.
+ var upgradeOldElements = true;
+
var originalRegisterElement = doc.registerElement;
if (!originalRegisterElement) {
throw new Error('document.registerElement is not present.');
@@ -93,9 +96,19 @@
var original = proto.createdCallback;
var newCallback = function() {
original.call(this);
- var name = this.getAttribute('is') || this.localName;
- var upgrader = upgraders[name.toLowerCase()];
- if (upgrader) upgrader(this);
+ var name = (this.getAttribute('is') || this.localName).toLowerCase();
+ var upgrader = upgraders[name];
+ if (upgrader) {
+ upgrader(this);
+ } else if (upgradeOldElements) {
+ // Save this element in case we can upgrade it later when an upgrader is
+ // registered.
+ var list = pendingElements[name];
+ if (!list) {
+ list = pendingElements[name] = [];
+ }
+ list.push(this);
+ }
};
var descriptor = Object.getOwnPropertyDescriptor(proto, 'createdCallback');
@@ -121,8 +134,25 @@
}
upgraders[name] = upgrader;
if (unpatchableTags[name]) reportError(name);
+ if (upgradeOldElements) {
+ // Upgrade elements that were created before the upgrader was registered.
+ var list = pendingElements[name];
+ if (list) {
+ for (var i = 0; i < list.length; i++) {
+ upgrader(list[i]);
+ }
+ }
+ delete pendingElements[name];
+ } else {
+ console.warn("Didn't expect more Dart types to be registered. '" + name
+ + "' elements that already exist in the page might not be wrapped.");
+ }
}
+ function onlyUpgradeNewElements() {
+ upgradeOldElements = false;
+ pendingElements = null;
+ }
// Native custom elements outside the app in Chrome have constructor
// names like "x-tag", which need to be translated to the DOM
@@ -151,5 +181,6 @@
}
doc._registerDartTypeUpgrader = registerDartTypeUpgrader;
+ doc._onlyUpgradeNewElements = onlyUpgradeNewElements;
doc.registerElement = registerElement;
})(document);
diff --git a/lib/interop.dart b/lib/interop.dart
index 058ce76..415c7fe 100644
--- a/lib/interop.dart
+++ b/lib/interop.dart
@@ -31,3 +31,13 @@
dartType, extendsTag: extendsTag);
_doc.callMethod('_registerDartTypeUpgrader', [tagName, upgrader.upgrade]);
}
+
+/// This function is mainly used to save resources. By default, we save a log of
+/// elements that are created but have no Dart type associated with them. This
+/// is so we can upgrade them as soon as [registerDartType] is invoked. This
+/// function can be called to indicate that we no longer are interested in
+/// logging element creations and that it is sufficient to only upgrade new
+/// elements as they are being created. Typically this is called after the last
+/// call to [registerDartType] or as soon as you know that no element will be
+/// created until the call to [registerDartType] is made.
+void onlyUpgradeNewElements() => _doc.callMethod('_onlyUpgradeNewElements');
diff --git a/pubspec.yaml b/pubspec.yaml
index a94addf..b8f0bc5 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: web_components
-version: 0.3.5-dev.1
+version: 0.3.5-dev.2
author: Polymer.dart Authors <web-ui-dev@dartlang.org>
homepage: https://www.dartlang.org/polymer-dart/
description: >
diff --git a/test/interop_test.dart b/test/interop_test.dart
index 64dfb64..cbad6e1 100644
--- a/test/interop_test.dart
+++ b/test/interop_test.dart
@@ -12,15 +12,9 @@
import 'package:web_components/interop.dart';
import 'package:web_components/polyfill.dart';
-final globalSetup = customElementsReady.then((_) {
- registerDartType('x-a', XAWrapper);
- registerDartType('x-b', XBWrapper, extendsTag: 'div');
- registerDartType('x-c', XCWrapper);
- });
-
main() {
useHtmlConfiguration();
- setUp(() => globalSetup);
+ setUp(() => customElementsReady);
test('interop is supported', () {
expect(isSupported, isTrue);
@@ -37,21 +31,53 @@
expect(b is XBWrapper, isFalse, reason: 'x-b should not be upgraded yet');
expect(_readX(b), 1);
+ var d = document.querySelector('x-d');
+ expect(d is HtmlElement, isTrue, reason: 'x-d is HtmlElement');
+ expect(d is XDWrapper, isFalse, reason: 'x-d should not be upgraded yet');
+ expect(_readX(d), 2);
+
+ /// Note: this registration has a global side-effect and is assumed in the
+ /// following tests.
+ registerDartType('x-a', XAWrapper);
+ registerDartType('x-b', XBWrapper, extendsTag: 'div');
+ registerDartType('x-c', XCWrapper);
+ onlyUpgradeNewElements();
+ registerDartType('x-d', XDWrapper); // late on purpose.
+
+ a = document.querySelector('x-a');
+ expect(a is HtmlElement, isTrue, reason: 'x-a is HtmlElement');
+ expect(a is XAWrapper, isTrue, reason: 'x-a is upgraded to XAWrapper');
+ expect(a.x, 0);
+ expect(a.wrapperCount, 0);
+
+ b = document.querySelector('[is=x-b]');
+ expect(b is DivElement, isTrue, reason: 'x-b is DivElement');
+ expect(b is XBWrapper, isTrue, reason: 'x-b is upgraded to XBWrapper');
+ expect(b.x, 1);
+ expect(b.wrapperCount, 1);
+
+ // x-d was not upgraded because its registration came after we stopped
+ // upgrading old elements:
+ d = document.querySelector('x-d');
+ expect(d is HtmlElement, isTrue, reason: 'x-d is HtmlElement');
+ expect(d is XDWrapper, isFalse, reason: 'x-d should not be upgraded yet');
+ expect(_readX(d), 2);
+
var c = document.querySelector('x-c');
expect(c is HtmlElement, isTrue, reason: 'x-c is HtmlElement');
expect(c is XCWrapper, isFalse, reason: 'x-c should not be upgraded yet');
expect(_readX(c), null, reason: 'x-c has not been registered in JS yet');
});
- test('events seen for anything created after registering Dart type', () {
+ test('anything created after registering Dart type is upgraded', () {
context.callMethod('addA');
var list = document.querySelectorAll('x-a');
expect(list.length, 2);
var a = list[1];
expect(a is HtmlElement, isTrue, reason: 'x-a is HtmlElement');
expect(a is XAWrapper, isTrue, reason: 'x-a is upgraded to XAWrapper');
- expect(a.x, 2);
- expect(a.wrapperCount, 0);
+ expect(a.x, 3);
+ expect(a.wrapperCount, 2);
context.callMethod('addB');
list = document.querySelectorAll('[is=x-b]');
@@ -59,8 +85,18 @@
var b = list[1];
expect(b is DivElement, isTrue, reason: 'x-b is DivElement');
expect(b is XBWrapper, isTrue, reason: 'x-b is upgraded to XBWrapper');
- expect(b.x, 3);
- expect(b.wrapperCount, 1);
+ expect(b.x, 4);
+ expect(b.wrapperCount, 3);
+
+ // New instances of x-d should be upgraded regardless.
+ context.callMethod('addD');
+ list = document.querySelectorAll('x-d');
+ expect(list.length, 2);
+ var d = list[1];
+ expect(d is HtmlElement, isTrue, reason: 'x-d is HtmlElement');
+ expect(d is XDWrapper, isTrue, reason: 'x-d is upgraded to XDWrapper');
+ expect(d.x, 5);
+ expect(d.wrapperCount, 4);
});
test('events seen if Dart type is registered before registerElement', () {
@@ -71,8 +107,8 @@
context.callMethod('registerC');
c = document.querySelector('x-c');
expect(c is XCWrapper, isTrue);
- expect(c.x, 4);
- expect(c.wrapperCount, 2);
+ expect(c.x, 6);
+ expect(c.wrapperCount, 5);
context.callMethod('addC');
var list = document.querySelectorAll('x-c');
@@ -81,8 +117,8 @@
c = list[1];
expect(c is HtmlElement, isTrue, reason: 'x-c is HtmlElement');
expect(c is XCWrapper, isTrue, reason: 'x-c is upgraded to XCWrapper');
- expect(c.x, 5);
- expect(c.wrapperCount, 3);
+ expect(c.x, 7);
+ expect(c.wrapperCount, 6);
});
}
int _count = 0;
@@ -105,3 +141,7 @@
class XCWrapper extends HtmlElement with Wrapper {
XCWrapper.created() : super.created();
}
+
+class XDWrapper extends HtmlElement with Wrapper {
+ XDWrapper.created() : super.created();
+}
diff --git a/test/interop_test.html b/test/interop_test.html
index 3c4f10e..036c7ef 100644
--- a/test/interop_test.html
+++ b/test/interop_test.html
@@ -27,8 +27,14 @@
B.prototype.createdCallback = function() { this.x = counter++; };
B.extends = 'div';
+ var D = function() {};
+ D.prototype = Object.create(HTMLElement.prototype);
+ D.prototype.inc = function() { this.x = counter++; };
+ D.prototype.createdCallback = function() { this.inc(); };
+
document.registerElement('x-a', A);
document.registerElement('x-b', B);
+ document.registerElement('x-d', D);
function registerC() {
var proto = Object.create(HTMLElement.prototype, {
@@ -48,10 +54,14 @@
function addC() {
document.body.appendChild(document.createElement('x-c'));
}
+ function addD() {
+ document.body.appendChild(document.createElement('x-d'));
+ }
</script>
<x-a id="i1"></x-a>
<div is="x-b" id="i2"></div>
- <x-c id="i3"></x-b>
+ <x-c id="i3"></x-c>
+ <x-d id="i4"></x-d>
<script type="text/javascript"
src="/root_dart/tools/testing/dart/test_controller.js"></script>
%TEST_SCRIPTS%