web_components: fix extending another custom element

R=sigmund@google.com

Review URL: https://codereview.chromium.org//341003002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/web_components@37472 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/interop.dart b/lib/interop.dart
index 415c7fe..de1ef2f 100644
--- a/lib/interop.dart
+++ b/lib/interop.dart
@@ -29,7 +29,17 @@
 
   var upgrader = document.createElementUpgrader(
       dartType, extendsTag: extendsTag);
-  _doc.callMethod('_registerDartTypeUpgrader', [tagName, upgrader.upgrade]);
+
+  // Unfortunately the dart:html upgrader will throw on an already-upgraded
+  // element, so we need to duplicate the type check to prevent that.
+  // An element can be upgraded twice if it extends another element and calls
+  // createdCallback on the superclass. Since that's a valid use case we must
+  // wrap at both levels, and guard against it here.
+  upgradeElement(e) {
+    if (e.runtimeType != dartType) upgrader.upgrade(e);
+  }
+
+  _doc.callMethod('_registerDartTypeUpgrader', [tagName, upgradeElement]);
 }
 
 /// This function is mainly used to save resources. By default, we save a log of
diff --git a/pubspec.yaml b/pubspec.yaml
index b8f0bc5..223d136 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: web_components
-version: 0.3.5-dev.2
+version: 0.3.5-dev.3
 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 cbad6e1..20cce29 100644
--- a/test/interop_test.dart
+++ b/test/interop_test.dart
@@ -120,6 +120,16 @@
     expect(c.x, 7);
     expect(c.wrapperCount, 6);
   });
+
+  test('element can extend another element', () {
+    registerDartType('x-e', XEWrapper);
+    context.callMethod('addE');
+
+    var e = document.querySelector('x-e');
+    expect(e is XEWrapper, isTrue);
+    expect(e.x, 8);
+    expect(e.y, 9);
+  });
 }
 int _count = 0;
 
@@ -145,3 +155,9 @@
 class XDWrapper extends HtmlElement with Wrapper {
   XDWrapper.created() : super.created();
 }
+
+class XEWrapper extends HtmlElement with Wrapper {
+  XEWrapper.created() : super.created();
+
+  int get y => new JsObject.fromBrowserObject(this)['y'];
+}
diff --git a/test/interop_test.html b/test/interop_test.html
index 036c7ef..d49dcb9 100644
--- a/test/interop_test.html
+++ b/test/interop_test.html
@@ -32,13 +32,23 @@
     D.prototype.inc = function() { this.x = counter++; };
     D.prototype.createdCallback = function() { this.inc(); };
 
+    var E = { prototype: Object.create(D.prototype) };
+    E.prototype.inc2 = function() {
+      this.y = counter++;
+    };
+    E.prototype.createdCallback = function() {
+      D.prototype.createdCallback.call(this);
+      this.inc2();
+    };
+
     document.registerElement('x-a', A);
     document.registerElement('x-b', B);
     document.registerElement('x-d', D);
+    document.registerElement('x-e', E);
 
     function registerC() {
       var proto = Object.create(HTMLElement.prototype, {
-        inc: { value: function() { this.x = counter++; } }, 
+        inc: { value: function() { this.x = counter++; } },
         createdCallback: {
           value: function() { this.inc(); },
           configurable: true},
@@ -57,6 +67,9 @@
     function addD() {
       document.body.appendChild(document.createElement('x-d'));
     }
+    function addE() {
+      document.body.appendChild(document.createElement('x-e'));
+    }
   </script>
   <x-a id="i1"></x-a>
   <div is="x-b" id="i2"></div>