Last active
August 29, 2015 13:56
-
-
Save arv/8857167 to your computer and use it in GitHub Desktop.
Secure shadow dom
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
'use strict'; | |
function uncurryThis(f) { | |
return f.call.bind(f); | |
} | |
var ShadowRoot = window.ShadowRoot; | |
var createShadowRoot = uncurryThis(Element.prototype.createShadowRoot); | |
var ownerDocumentGetter = uncurryThis(Object.getOwnPropertyDescriptor(Node.prototype, 'ownerDocument').get); | |
var contains = uncurryThis(Node.prototype.contains); | |
var parentNodeGetter = uncurryThis(Object.getOwnPropertyDescriptor(Node.prototype, 'parentNode').get); | |
var hostGetter = uncurryThis(Object.getOwnPropertyDescriptor(ShadowRoot.prototype, 'host').get); | |
var shadowRootGetter = uncurryThis(Object.getOwnPropertyDescriptor(Element.prototype, 'shadowRoot').get); | |
var olderShadowRootGetter = uncurryThis(Object.getOwnPropertyDescriptor(ShadowRoot.prototype, 'olderShadowRoot').get); | |
var defineProperty = Object.defineProperty; | |
var weakMapSet = uncurryThis(WeakMap.prototype.set); | |
var weakMapGet = uncurryThis(WeakMap.prototype.get); | |
var disallowBrand = new WeakMap; | |
Element.prototype.createEncapsulatedShadowRoot = function() { | |
var sr = createShadowRoot(this); | |
weakMapSet(disallowBrand, sr, true); | |
return sr; | |
}; | |
Object.defineProperty(Element.prototype, 'shadowRoot', { | |
get: function() { | |
var sr = shadowRootGetter(this); | |
if (weakMapGet(disallowBrand, sr)) | |
return null; | |
return sr; | |
} | |
}); | |
Object.defineProperty(ShadowRoot.prototype, 'olderShadowRoot', { | |
get: function() { | |
var sr = olderShadowRootGetter(this); | |
if (weakMapGet(disallowBrand, sr)) | |
return null; | |
return sr; | |
} | |
}); | |
function getShadowRootAncestor(node) { | |
// document.contains is O(1) and nodes in a shadow dom are not in document. | |
if (!node || contains(ownerDocumentGetter(node), node)) | |
return null; | |
if (node instanceof ShadowRoot) | |
return node; | |
return getShadowRootAncestor(parentNodeGetter(node)); | |
} | |
function inDisallowedRoot(node) { | |
var sr = getShadowRootAncestor(node); | |
if (!sr) | |
return false; | |
if (weakMapGet(disallowBrand, sr)) | |
return true; | |
return inDisallowedRoot(hostGetter(sr)); | |
} | |
function filterStaticNodeList(nodeList) { | |
var staticNodeList = {__proto__: NodeList.prototype}; // best effort | |
var len = 0; | |
for (var i = 0; i < nodeList.length; i++) { | |
if (!inDisallowedRoot(nodeList[i])) { | |
defineProperty(staticNodeList, len++, { | |
value: nodeList[i], | |
writable: false, | |
configurable: true, | |
enumerable: true | |
}); | |
} | |
} | |
staticNodeList.length = len; // wrong, should be getter in proto | |
return staticNodeList; | |
} | |
function makeFilterElementFunction(prototype, name) { | |
var original = prototype[name]; | |
prototype[name] = function() { | |
var element = original.apply(this, arguments); // FIXME: apply! | |
if (inDisallowedRoot(element)) | |
return null; | |
return element; | |
}; | |
} | |
function makeFilterNodeListFunction(prototype, name) { | |
var original = prototype[name]; | |
prototype[name] = function() { | |
return filterStaticNodeList(original.apply(this, arguments)); // FIXME: apply! | |
}; | |
} | |
makeFilterElementFunction(Document.prototype, 'querySelector'); | |
makeFilterElementFunction(DocumentFragment.prototype, 'querySelector'); | |
makeFilterElementFunction(Element.prototype, 'querySelector'); | |
makeFilterNodeListFunction(Document.prototype, 'querySelectorAll'); | |
makeFilterNodeListFunction(DocumentFragment.prototype, 'querySelectorAll'); | |
makeFilterNodeListFunction(Element.prototype, 'querySelectorAll'); | |
makeFilterNodeListFunction(Element.prototype, 'getDestinationInsertionPoints'); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
'use strict'; | |
var shadowRootGetter = Object.getOwnPropertyDescriptor(Element.prototype, 'shadowRoot').get; | |
delete Element.prototype.shadowRoot; | |
var getDestinationInsertionPoints = Element.prototype.getDestinationInsertionPoints; | |
delete Element.prototype.getDestinationInsertionPoints; | |
var olderShadowRootGetter = Object.getOwnPropertyDescriptor(ShadowRoot.prototype, 'olderShadowRoot').get; | |
delete ShadowRoot.prototype.olderShadowRoot; | |
// TODO(arv): Return null from querySelector/querySelectorAll if the selector contains ^/^^ | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment