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

Commit

Permalink
Merge pull request #202 from Polymer/master
Browse files Browse the repository at this point in the history
8/8 master -> stable
  • Loading branch information
dfreedm committed Aug 8, 2013
2 parents fd48678 + 6a6e388 commit 31eee3f
Show file tree
Hide file tree
Showing 24 changed files with 725 additions and 6 deletions.
2 changes: 2 additions & 0 deletions build.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
2 changes: 2 additions & 0 deletions shadowdom.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<script src="' + base + src + '"></script>');
Expand Down
14 changes: 11 additions & 3 deletions src/wrappers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -199,17 +202,20 @@ 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;
}

function isNative(object) {
return object instanceof OriginalNode ||
object instanceof OriginalEvent ||
object instanceof OriginalWindow ||
object instanceof OriginalRange ||
object instanceof OriginalDOMImplementation;
}

Expand Down Expand Up @@ -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;
Expand Down
95 changes: 92 additions & 3 deletions src/wrappers/Document.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@
}

[
'getElementById',
'createComment',
'createDocumentFragment',
'createElement',
'createElementNS',
'createTextNode',
'createDocumentFragment',
'createEvent',
'createEventNS',
'createRange',
'createTextNode',
'getElementById',
].forEach(wrapMethod);

var originalAdoptNode = document.adoptNode;
Expand Down Expand Up @@ -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',
Expand All @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions src/wrappers/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -363,6 +364,8 @@
if (!child)
return false;

child = wrapIfNeeded(child);

// TODO(arv): Optimize using ownerDocument etc.
if (child === this)
return true;
Expand Down
86 changes: 86 additions & 0 deletions src/wrappers/Range.js
Original file line number Diff line number Diff line change
@@ -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);
54 changes: 54 additions & 0 deletions src/wrappers/elements-with-form-property.js
Original file line number Diff line number Diff line change
@@ -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);
Loading

0 comments on commit 31eee3f

Please sign in to comment.