diff --git a/build.json b/build.json index 1468013..12ec786 100644 --- a/build.json +++ b/build.json @@ -19,5 +19,7 @@ "src/wrappers/Document.js", "src/wrappers/Window.js", "src/wrappers/MutationObserver.js", + "src/wrappers/Range.js", + "src/wrappers/elements-with-form-property.js", "src/wrappers/override-constructors.js" ] diff --git a/shadowdom.js b/shadowdom.js index b697830..55691ef 100644 --- a/shadowdom.js +++ b/shadowdom.js @@ -33,9 +33,11 @@ 'wrappers/generic.js', 'wrappers/ShadowRoot.js', 'ShadowRenderer.js', + 'wrappers/elements-with-form-property.js', 'wrappers/Document.js', 'wrappers/Window.js', 'wrappers/MutationObserver.js', + 'wrappers/Range.js', 'wrappers/override-constructors.js' ].forEach(function(src) { document.write(''); diff --git a/src/wrappers.js b/src/wrappers.js index da76593..ec37611 100644 --- a/src/wrappers.js +++ b/src/wrappers.js @@ -9,6 +9,7 @@ var ShadowDOMPolyfill = {}; var wrapperTable = new SideTable(); var constructorTable = new SideTable(); + var nativePrototypeTable = new SideTable(); var wrappers = Object.create(null); function assert(b) { @@ -140,9 +141,8 @@ var ShadowDOMPolyfill = {}; /** * @param {Function} nativeConstructor * @param {Function} wrapperConstructor - * @param {string|Object=} opt_instance If present, this is used to extract - * properties from an instance object. If this is a string - * |document.createElement| is used to create an instance. + * @param {Object=} opt_instance If present, this is used to extract + * properties from an instance object. */ function register(nativeConstructor, wrapperConstructor, opt_instance) { var nativePrototype = nativeConstructor.prototype; @@ -153,7 +153,10 @@ var ShadowDOMPolyfill = {}; function registerInternal(nativePrototype, wrapperConstructor, opt_instance) { var wrapperPrototype = wrapperConstructor.prototype; assert(constructorTable.get(nativePrototype) === undefined); + constructorTable.set(nativePrototype, wrapperConstructor); + nativePrototypeTable.set(wrapperPrototype, nativePrototype); + addForwardingProperties(nativePrototype, wrapperPrototype); if (opt_instance) registerInstanceProperties(wrapperPrototype, opt_instance); @@ -199,10 +202,12 @@ var ShadowDOMPolyfill = {}; var OriginalEvent = Event; var OriginalNode = Node; var OriginalWindow = Window; + var OriginalRange = Range; function isWrapper(object) { return object instanceof wrappers.EventTarget || object instanceof wrappers.Event || + object instanceof wrappers.Range || object instanceof wrappers.DOMImplementation; } @@ -210,6 +215,7 @@ var ShadowDOMPolyfill = {}; return object instanceof OriginalNode || object instanceof OriginalEvent || object instanceof OriginalWindow || + object instanceof OriginalRange || object instanceof OriginalDOMImplementation; } @@ -310,11 +316,13 @@ var ShadowDOMPolyfill = {}; } scope.assert = assert; + scope.constructorTable = constructorTable; scope.defineGetter = defineGetter; scope.defineWrapGetter = defineWrapGetter; scope.forwardMethodsToWrapper = forwardMethodsToWrapper; scope.isWrapperFor = isWrapperFor; scope.mixin = mixin; + scope.nativePrototypeTable = nativePrototypeTable; scope.registerObject = registerObject; scope.registerWrapper = register; scope.rewrap = rewrap; diff --git a/src/wrappers/Document.js b/src/wrappers/Document.js index 6a31289..bd6def3 100644 --- a/src/wrappers/Document.js +++ b/src/wrappers/Document.js @@ -45,13 +45,15 @@ } [ - 'getElementById', + 'createComment', + 'createDocumentFragment', 'createElement', 'createElementNS', - 'createTextNode', - 'createDocumentFragment', 'createEvent', 'createEventNS', + 'createRange', + 'createTextNode', + 'getElementById', ].forEach(wrapMethod); var originalAdoptNode = document.adoptNode; @@ -97,15 +99,99 @@ } }); + if (document.register) { + var originalRegister = document.register; + Document.prototype.register = function(tagName, object) { + var prototype = object.prototype; + + // If we already used the object as a prototype for another custom + // element. + if (scope.nativePrototypeTable.get(prototype)) { + // TODO(arv): DOMException + throw new Error('NotSupportedError'); + } + + // Find first object on the prototype chain that already have a native + // prototype. Keep track of all the objects before that so we can create + // a similar structure for the native case. + var proto = Object.getPrototypeOf(prototype); + var nativePrototype; + var prototypes = []; + while (proto) { + nativePrototype = scope.nativePrototypeTable.get(proto); + if (nativePrototype) + break; + prototypes.push(proto); + proto = Object.getPrototypeOf(proto); + } + + if (!nativePrototype) { + // TODO(arv): DOMException + throw new Error('NotSupportedError'); + } + + // This works by creating a new prototype object that is empty, but has + // the native prototype as its proto. The original prototype object + // passed into register is used as the wrapper prototype. + + var newPrototype = Object.create(nativePrototype); + for (var i = prototypes.length - 1; i >= 0; i--) { + newPrototype = Object.create(newPrototype); + } + + // Add callbacks if present. + // Names are taken from: + // https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/bindings/v8/CustomElementConstructorBuilder.cpp&sq=package:chromium&type=cs&l=156 + // and not from the spec since the spec is out of date. + [ + 'createdCallback', + 'enteredDocumentCallback', + 'leftDocumentCallback', + 'attributeChangedCallback', + ].forEach(function(name) { + var f = prototype[name]; + if (!f) + return; + newPrototype[name] = function() { + f.apply(wrap(this), arguments); + }; + }); + + var nativeConstructor = originalRegister.call(unwrap(this), tagName, + {prototype: newPrototype}); + + function GeneratedWrapper(node) { + if (!node) + return document.createElement(tagName); + this.impl = node; + } + GeneratedWrapper.prototype = prototype; + GeneratedWrapper.prototype.constructor = GeneratedWrapper; + + scope.constructorTable.set(newPrototype, GeneratedWrapper); + scope.nativePrototypeTable.set(prototype, newPrototype); + + return GeneratedWrapper; + }; + + forwardMethodsToWrapper([ + window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument + ], [ + 'register', + ]); + } + // We also override some of the methods on document.body and document.head // for convenience. forwardMethodsToWrapper([ window.HTMLBodyElement, window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument window.HTMLHeadElement, + window.HTMLHtmlElement, ], [ 'appendChild', 'compareDocumentPosition', + 'contains', 'getElementsByClassName', 'getElementsByTagName', 'getElementsByTagNameNS', @@ -120,11 +206,14 @@ window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument ], [ 'adoptNode', + 'contains', + 'createComment', 'createDocumentFragment', 'createElement', 'createElementNS', 'createEvent', 'createEventNS', + 'createRange', 'createTextNode', 'elementFromPoint', 'getElementById', diff --git a/src/wrappers/Node.js b/src/wrappers/Node.js index 2b4d9fc..2bc3e6d 100644 --- a/src/wrappers/Node.js +++ b/src/wrappers/Node.js @@ -13,6 +13,7 @@ var registerWrapper = scope.registerWrapper; var unwrap = scope.unwrap; var wrap = scope.wrap; + var wrapIfNeeded = scope.wrapIfNeeded; function assertIsNodeWrapper(node) { assert(node instanceof Node); @@ -363,6 +364,8 @@ if (!child) return false; + child = wrapIfNeeded(child); + // TODO(arv): Optimize using ownerDocument etc. if (child === this) return true; diff --git a/src/wrappers/Range.js b/src/wrappers/Range.js new file mode 100644 index 0000000..019805e --- /dev/null +++ b/src/wrappers/Range.js @@ -0,0 +1,86 @@ +// Copyright 2013 The Polymer Authors. All rights reserved. +// Use of this source code is goverened by a BSD-style +// license that can be found in the LICENSE file. + +(function(scope) { + 'use strict'; + + var registerWrapper = scope.registerWrapper; + var unwrap = scope.unwrap; + var unwrapIfNeeded = scope.unwrapIfNeeded; + var wrap = scope.wrap; + + function Range(impl) { + this.impl = impl; + } + Range.prototype = { + get startContainer() { + return wrap(this.impl.startContainer); + }, + get endContainer() { + return wrap(this.impl.endContainer); + }, + get commonAncestorContainer() { + return wrap(this.impl.commonAncestorContainer); + }, + setStart: function(refNode,offset) { + this.impl.setStart(unwrapIfNeeded(refNode), offset); + }, + setEnd: function(refNode,offset) { + this.impl.setEnd(unwrapIfNeeded(refNode), offset); + }, + setStartBefore: function(refNode) { + this.impl.setStartBefore(unwrapIfNeeded(refNode)); + }, + setStartAfter: function(refNode) { + this.impl.setStartAfter(unwrapIfNeeded(refNode)); + }, + setEndBefore: function(refNode) { + this.impl.setEndBefore(unwrapIfNeeded(refNode)); + }, + setEndAfter: function(refNode) { + this.impl.setEndAfter(unwrapIfNeeded(refNode)); + }, + selectNode: function(refNode) { + this.impl.selectNode(unwrapIfNeeded(refNode)); + }, + selectNodeContents: function(refNode) { + this.impl.selectNodeContents(unwrapIfNeeded(refNode)); + }, + compareBoundaryPoints: function(how, sourceRange) { + return this.impl.compareBoundaryPoints(how, unwrap(sourceRange)); + }, + extractContents: function() { + return wrap(this.impl.extractContents()); + }, + cloneContents: function() { + return wrap(this.impl.cloneContents()); + }, + insertNode: function(node) { + this.impl.insertNode(unwrapIfNeeded(node)); + }, + surroundContents: function(newParent) { + this.impl.surroundContents(unwrapIfNeeded(newParent)); + }, + cloneRange: function() { + return wrap(this.impl.cloneRange()); + }, + isPointInRange: function(node, offset) { + return this.impl.isPointInRange(unwrapIfNeeded(node), offset); + }, + comparePoint: function(node, offset) { + return this.impl.comparePoint(unwrapIfNeeded(node), offset); + }, + intersectsNode: function(node) { + return this.impl.intersectsNode(unwrapIfNeeded(node)); + }, + createContextualFragment: function(html) { + return wrap(this.impl.createContextualFragment(html)); + } + }; + + registerWrapper(window.Range, Range); + + scope.wrappers.Range = Range; + +})(this.ShadowDOMPolyfill); diff --git a/src/wrappers/elements-with-form-property.js b/src/wrappers/elements-with-form-property.js new file mode 100644 index 0000000..1617ada --- /dev/null +++ b/src/wrappers/elements-with-form-property.js @@ -0,0 +1,54 @@ +// Copyright 2013 The Polymer Authors. All rights reserved. +// Use of this source code is goverened by a BSD-style +// license that can be found in the LICENSE file. + +(function(scope) { + 'use strict'; + + var HTMLElement = scope.wrappers.HTMLElement; + var assert = scope.assert; + var mixin = scope.mixin; + var registerWrapper = scope.registerWrapper; + var unwrap = scope.unwrap; + var wrap = scope.wrap; + + var elementsWithFormProperty = [ + 'HTMLButtonElement', + 'HTMLFieldSetElement', + 'HTMLInputElement', + 'HTMLKeygenElement', + 'HTMLLabelElement', + 'HTMLLegendElement', + 'HTMLObjectElement', + 'HTMLOptionElement', + 'HTMLOutputElement', + 'HTMLSelectElement', + 'HTMLTextAreaElement', + ]; + + function createWrapperConstructor(name) { + if (!window[name]) + return; + + // Ensure we are not overriding an already existing constructor. + assert(!scope.wrappers[name]); + + var GeneratedWrapper = function(node) { + // At this point all of them extend HTMLElement. + HTMLElement.call(this, node); + } + GeneratedWrapper.prototype = Object.create(HTMLElement.prototype); + mixin(GeneratedWrapper.prototype, { + get form() { + return wrap(unwrap(this).form); + }, + }); + + registerWrapper(window[name], GeneratedWrapper, + document.createElement(name.slice(4, -7))); + scope.wrappers[name] = GeneratedWrapper; + } + + elementsWithFormProperty.forEach(createWrapperConstructor); + +})(this.ShadowDOMPolyfill); diff --git a/test/js/Document.js b/test/js/Document.js index 35f9a94..e4517ee 100644 --- a/test/js/Document.js +++ b/test/js/Document.js @@ -248,5 +248,204 @@ htmlSuite('Document', function() { assert.equal(doc.elementFromPoint(5, 5), div); }); + test('document.contains', function() { + assert.isTrue(document.contains(document.body)); + assert.isTrue(document.contains(document.querySelector('body'))); + + assert.isTrue(document.contains(document.head)); + assert.isTrue(document.contains(document.querySelector('head'))); + + assert.isTrue(document.contains(document.documentElement)); + assert.isTrue(document.contains(document.querySelector('html'))); + }); + + test('document.register', function() { + if (!document.register) + return; + + var aPrototype = Object.create(HTMLElement.prototype); + aPrototype.getName = function() { + return 'a'; + }; + + var A = document.register('x-a', {prototype: aPrototype}); + + var a1 = document.createElement('x-a'); + assert.equal('x-a', a1.localName); + assert.equal(Object.getPrototypeOf(a1), aPrototype); + assert.instanceOf(a1, A); + assert.instanceOf(a1, HTMLElement); + assert.equal(a1.getName(), 'a'); + + var a2 = new A(); + assert.equal('x-a', a2.localName); + assert.equal(Object.getPrototypeOf(a2), aPrototype); + assert.instanceOf(a2, A); + assert.instanceOf(a2, HTMLElement); + assert.equal(a2.getName(), 'a'); + + ////////////////////////////////////////////////////////////////////// + + var bPrototype = Object.create(A.prototype); + bPrototype.getName = function() { + return 'b'; + }; + + var B = document.register('x-b', {prototype: bPrototype}); + + var b1 = document.createElement('x-b'); + assert.equal('x-b', b1.localName); + assert.equal(Object.getPrototypeOf(b1), bPrototype); + assert.instanceOf(b1, B); + assert.instanceOf(b1, A); + assert.instanceOf(b1, HTMLElement); + assert.equal(b1.getName(), 'b'); + + var b2 = new B(); + assert.equal('x-b', b2.localName); + assert.equal(Object.getPrototypeOf(b2), bPrototype); + assert.instanceOf(b2, B); + assert.instanceOf(b2, A); + assert.instanceOf(b2, HTMLElement); + assert.equal(b2.getName(), 'b'); + }); + + test('document.register deeper', function() { + if (!document.register) + return; + + function C() {} + C.prototype = { + __proto__: HTMLElement.prototype + }; + + function B() {} + B.prototype = { + __proto__: C.prototype + }; + + function A() {} + A.prototype = { + __proto__: B.prototype + }; + + A = document.register('x-a5', A); + + var a1 = document.createElement('x-a5'); + assert.equal('x-a5', a1.localName); + assert.equal(a1.__proto__, A.prototype); + assert.equal(a1.__proto__.__proto__, B.prototype); + assert.equal(a1.__proto__.__proto__.__proto__, C.prototype); + assert.equal(a1.__proto__.__proto__.__proto__.__proto__, + HTMLElement.prototype); + + var a2 = new A(); + assert.equal('x-a5', a2.localName); + assert.equal(a2.__proto__, A.prototype); + assert.equal(a2.__proto__.__proto__, B.prototype); + assert.equal(a2.__proto__.__proto__.__proto__, C.prototype); + assert.equal(a2.__proto__.__proto__.__proto__.__proto__, + HTMLElement.prototype); + }); + + test('document.register createdCallback', function() { + if (!document.register) + return; + + var self; + var createdCalls = 0; + + function A() {} + A.prototype = { + __proto__: HTMLElement.prototype, + createdCallback: function() { + createdCalls++; + assert.isUndefined(a); + assert.instanceOf(this, A); + self = this; + } + } + + A = document.register('x-a2', A); + + var a = new A; + assert.equal(createdCalls, 1); + assert.equal(self, a); + }); + + test('document.register enteredDocumentCallback, leftDocumentCallback', + function() { + if (!document.register) + return; + + var enteredDocumentCalls = 0; + var leftDocumentCalls = 0; + + function A() {} + A.prototype = { + __proto__: HTMLElement.prototype, + enteredDocumentCallback: function() { + enteredDocumentCalls++; + assert.instanceOf(this, A); + assert.equal(a, this); + }, + leftDocumentCallback: function() { + leftDocumentCalls++; + assert.instanceOf(this, A); + assert.equal(a, this); + } + } + + A = document.register('x-a3', A); + + var a = new A; + document.body.appendChild(a); + assert.equal(enteredDocumentCalls, 1); + document.body.removeChild(a); + assert.equal(leftDocumentCalls, 1); + }); + + test('document.register attributeChangedCallback', function() { + if (!document.register) + return; + + var attributeChangedCalls = 0; + + function A() {} + A.prototype = { + __proto__: HTMLElement.prototype, + attributeChangedCallback: function(name, oldValue, newValue) { + attributeChangedCalls++; + assert.equal(name, 'foo'); + switch (attributeChangedCalls) { + case 1: + assert.isNull(oldValue); + assert.equal(newValue, 'bar'); + break; + case 2: + assert.equal(oldValue, 'bar'); + assert.equal(newValue, 'baz'); + break; + case 3: + assert.equal(oldValue, 'baz'); + assert.isNull(newValue); + break; + } + console.log(arguments); + } + } + + A = document.register('x-a4', A); + + var a = new A; + assert.equal(attributeChangedCalls, 0); + a.setAttribute('foo', 'bar'); + assert.equal(attributeChangedCalls, 1); + a.setAttribute('foo', 'baz'); + assert.equal(attributeChangedCalls, 2); + a.removeAttribute('foo'); + assert.equal(attributeChangedCalls, 3); + }); + htmlTest('html/document-write.html'); }); diff --git a/test/js/HTMLBodyElement.js b/test/js/HTMLBodyElement.js index fb9bdbc..ac02abc 100644 --- a/test/js/HTMLBodyElement.js +++ b/test/js/HTMLBodyElement.js @@ -105,5 +105,13 @@ htmlSuite('HTMLBodyElement', function() { assert.equal(calls, 2); }); + test('document.body.contains', function() { + var doc = wrap(document); + assert.isTrue(doc.body.contains(doc.body.firstChild)); + assert.isTrue(doc.body.contains(document.body.firstChild)); + assert.isTrue(document.body.contains(doc.body.firstChild)); + assert.isTrue(document.body.contains(document.body.firstChild)); + }); + htmlTest('html/document-body-inner-html.html'); }); diff --git a/test/js/HTMLButtonElement.js b/test/js/HTMLButtonElement.js new file mode 100644 index 0000000..ede1c15 --- /dev/null +++ b/test/js/HTMLButtonElement.js @@ -0,0 +1,16 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLButtonElement', function() { + + test('form', function() { + var form = document.createElement('form'); + var button = document.createElement('button'); + form.appendChild(button); + assert.equal(button.form, form); + }); + +}); diff --git a/test/js/HTMLFieldSetElement.js b/test/js/HTMLFieldSetElement.js new file mode 100644 index 0000000..cc6f5df --- /dev/null +++ b/test/js/HTMLFieldSetElement.js @@ -0,0 +1,16 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLFieldSetElement', function() { + + test('form', function() { + var form = document.createElement('form'); + var fieldSet = document.createElement('fieldset'); + form.appendChild(fieldSet); + assert.equal(fieldSet.form, form); + }); + +}); diff --git a/test/js/HTMLHeadElement.js b/test/js/HTMLHeadElement.js index d0ad020..4e0bd1d 100644 --- a/test/js/HTMLHeadElement.js +++ b/test/js/HTMLHeadElement.js @@ -85,4 +85,12 @@ suite('HTMLHeadElement', function() { doc.body.removeChild(div); assert.isNull(div.parentNode); }); + + test('document.head.contains', function() { + var doc = wrap(document); + assert.isTrue(doc.head.contains(doc.head.firstChild)); + assert.isTrue(doc.head.contains(document.head.firstChild)); + assert.isTrue(document.head.contains(doc.head.firstChild)); + assert.isTrue(document.head.contains(document.head.firstChild)); + }); }); diff --git a/test/js/HTMLHtmlElement.js b/test/js/HTMLHtmlElement.js new file mode 100644 index 0000000..3955917 --- /dev/null +++ b/test/js/HTMLHtmlElement.js @@ -0,0 +1,30 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLHtmlElement', function() { + + var wrap = ShadowDOMPolyfill.wrap; + + test('instanceof', function() { + var doc = wrap(document); + assert.instanceOf(doc.documentElement, HTMLHtmlElement); + assert.equal(wrap(document.documentElement), doc.documentElement); + }); + + test('appendChild', function() { + var doc = wrap(document); + + var a = document.createComment('a'); + var b = document.createComment('b'); + + document.documentElement.appendChild(a); + assert.equal(doc.documentElement.lastChild, a); + + doc.documentElement.appendChild(b); + assert.equal(doc.documentElement.lastChild, b); + }); + +}); diff --git a/test/js/HTMLInputElement.js b/test/js/HTMLInputElement.js new file mode 100644 index 0000000..c5ee42a --- /dev/null +++ b/test/js/HTMLInputElement.js @@ -0,0 +1,16 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLInputElement', function() { + + test('form', function() { + var form = document.createElement('form'); + var input = document.createElement('input'); + form.appendChild(input); + assert.equal(input.form, form); + }); + +}); diff --git a/test/js/HTMLKeygenElement.js b/test/js/HTMLKeygenElement.js new file mode 100644 index 0000000..609d1c6 --- /dev/null +++ b/test/js/HTMLKeygenElement.js @@ -0,0 +1,19 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLKeygenElement', function() { + // Not implemented in Firefox. + if (!window.HTMLKeygenElement) + return; + + test('form', function() { + var form = document.createElement('form'); + var keygen = document.createElement('keygen'); + form.appendChild(keygen); + assert.equal(keygen.form, form); + }); + +}); diff --git a/test/js/HTMLLabelElement.js b/test/js/HTMLLabelElement.js new file mode 100644 index 0000000..b059704 --- /dev/null +++ b/test/js/HTMLLabelElement.js @@ -0,0 +1,16 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLLabelElement', function() { + + test('form', function() { + var form = document.createElement('form'); + var label = document.createElement('label'); + form.appendChild(label); + assert.equal(label.form, form); + }); + +}); diff --git a/test/js/HTMLLegendElement.js b/test/js/HTMLLegendElement.js new file mode 100644 index 0000000..09ac37c --- /dev/null +++ b/test/js/HTMLLegendElement.js @@ -0,0 +1,18 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLLegendElement', function() { + + test('form', function() { + var form = document.createElement('form'); + var fieldSet = document.createElement('fieldset'); + var legend = document.createElement('legend'); + form.appendChild(fieldSet); + fieldSet.appendChild(legend); + assert.equal(legend.form, form); + }); + +}); diff --git a/test/js/HTMLObjectElement.js b/test/js/HTMLObjectElement.js new file mode 100644 index 0000000..8b1ba08 --- /dev/null +++ b/test/js/HTMLObjectElement.js @@ -0,0 +1,16 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLObjectElement', function() { + + test('form', function() { + var form = document.createElement('form'); + var object = document.createElement('object'); + form.appendChild(object); + assert.equal(object.form, form); + }); + +}); diff --git a/test/js/HTMLOptionElement.js b/test/js/HTMLOptionElement.js new file mode 100644 index 0000000..3823a35 --- /dev/null +++ b/test/js/HTMLOptionElement.js @@ -0,0 +1,18 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLOptionElement', function() { + + test('form', function() { + var form = document.createElement('form'); + var select = document.createElement('select'); + var option = document.createElement('option'); + form.appendChild(select); + select.appendChild(option); + assert.equal(option.form, form); + }); + +}); diff --git a/test/js/HTMLOutputElement.js b/test/js/HTMLOutputElement.js new file mode 100644 index 0000000..ebe3dcd --- /dev/null +++ b/test/js/HTMLOutputElement.js @@ -0,0 +1,19 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLOutputElement', function() { + // Not implemented in IE10. + if (!window.HTMLOutputElement) + return; + + test('form', function() { + var form = document.createElement('form'); + var output = document.createElement('output'); + form.appendChild(output); + assert.equal(output.form, form); + }); + +}); diff --git a/test/js/HTMLSelectElement.js b/test/js/HTMLSelectElement.js new file mode 100644 index 0000000..f9e3792 --- /dev/null +++ b/test/js/HTMLSelectElement.js @@ -0,0 +1,16 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLSelectElement', function() { + + test('form', function() { + var form = document.createElement('form'); + var select = document.createElement('select'); + form.appendChild(select); + assert.equal(select.form, form); + }); + +}); diff --git a/test/js/HTMLTextAreaElement.js b/test/js/HTMLTextAreaElement.js new file mode 100644 index 0000000..a40b63b --- /dev/null +++ b/test/js/HTMLTextAreaElement.js @@ -0,0 +1,16 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('HTMLTextAreaElement', function() { + + test('form', function() { + var form = document.createElement('form'); + var textArea = document.createElement('textarea'); + form.appendChild(textArea); + assert.equal(textArea.form, form); + }); + +}); diff --git a/test/js/Range.js b/test/js/Range.js new file mode 100644 index 0000000..84617bd --- /dev/null +++ b/test/js/Range.js @@ -0,0 +1,31 @@ +/* + * Copyright 2013 The Polymer Authors. All rights reserved. + * Use of this source code is goverened by a BSD-style + * license that can be found in the LICENSE file. + */ + +suite('Range', function() { + + var wrap = ShadowDOMPolyfill.wrap; + + test('instanceof', function() { + var range = document.createRange(); + assert.instanceOf(range, Range); + + var range2 = wrap(document).createRange(); + assert.instanceOf(range2, Range); + }); + + test('createContextualFragment', function() { + var range = document.createRange(); + var container = document.body || document.head; + + range.selectNode(container); + var fragment = range.createContextualFragment(''); + + assert.instanceOf(fragment, DocumentFragment); + assert.equal(fragment.firstChild.localName, 'b'); + assert.equal(fragment.childNodes.length, 1); + }); + +}); diff --git a/test/test.main.js b/test/test.main.js index 92fa4af..2a91cde 100644 --- a/test/test.main.js +++ b/test/test.main.js @@ -55,16 +55,29 @@ var modules = [ 'Document.js', 'Element.js', 'HTMLBodyElement.js', + 'HTMLButtonElement.js', 'HTMLContentElement.js', + 'HTMLFieldSetElement.js', 'HTMLHeadElement.js', + 'HTMLHtmlElement.js', + 'HTMLInputElement.js', + 'HTMLKeygenElement.js', + 'HTMLLabelElement.js', + 'HTMLLegendElement.js', + 'HTMLObjectElement.js', + 'HTMLOptionElement.js', + 'HTMLOutputElement.js', + 'HTMLSelectElement.js', 'HTMLShadowElement.js', 'HTMLTemplateElement.js', + 'HTMLTextAreaElement.js', 'MutationObserver.js', 'Node.js', 'ParentNodeInterface.js', 'ShadowRoot.js', 'Text.js', 'Window.js', + 'Range.js', 'custom-element.js', 'events.js', 'paralleltrees.js',