# HG changeset patch # User William Chen # Date 1425514181 28800 # Wed Mar 04 16:09:41 2015 -0800 # Node ID fcb8ca04e1e2a8a1e7a10fd5a069f55f20361321 # Parent a1cee560f762203187eb3a219831fbfd8eaa6bcb [mq]: diff diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -416,19 +416,20 @@ Element::WrapObject(JSContext *aCx) // Custom element prototype swizzling. CustomElementData* data = GetCustomElementData(); if (obj && data) { // If this is a registered custom element then fix the prototype. nsDocument* document = static_cast(OwnerDoc()); JS::Rooted prototype(aCx); document->GetCustomPrototype(NodeInfo()->NamespaceID(), data->mType, &prototype); if (prototype) { - // We want to set custom prototype in the compartment where the prototype - // was registered so that the custom prototype is visible to custom - // element callbacks in the compartment where registration occurred. + // We want to set the custom prototype in the compartment where it was + // registered. In the case that |obj| and |prototype| are in different + // compartments, this will set the prototype on the |obj|'s wrapper and + // thus only visible in the wrapper's compartment. JSAutoCompartment ac(aCx, prototype); if (!JS_WrapObject(aCx, &obj) || !JS_SetPrototype(aCx, obj, prototype)) { dom::Throw(aCx, NS_ERROR_FAILURE); return nullptr; } } } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -6220,33 +6220,29 @@ nsDocument::RegisterElement(JSContext* a protoObject = JS_NewObjectWithGivenProto(aCx, nullptr, htmlProto); if (!protoObject) { rv.Throw(NS_ERROR_UNEXPECTED); return; } } else { protoObject = aOptions.mPrototype; - // Check that the caller subsumes the underlying object of protoObject - // because we're going to enter its compartment. - if (js::IsWrapper(protoObject) && - !xpc::AccessCheck::wrapperSubsumes(protoObject)) { - // If the caller's compartment (containing protoObject) does not - // have permission to access the underlying object, just throw. + // Get the unwrapped prototype to do some checks. + JS::Rooted protoObjectUnwrapped(aCx, js::CheckedUnwrap(protoObject)); + if (!protoObjectUnwrapped) { + // If the caller's compartment does not have permission to access the + // unwrapped prototype then throw. rv.Throw(NS_ERROR_DOM_SECURITY_ERR); return; } - // Enter the compartment of the protoObject just for some checks. - JSAutoCompartment ac(aCx, protoObject); - // If PROTOTYPE is already an interface prototype object for any interface // object or PROTOTYPE has a non-configurable property named constructor, // throw a NotSupportedError and stop. - const js::Class* clasp = js::GetObjectClass(protoObject); + const js::Class* clasp = js::GetObjectClass(protoObjectUnwrapped); if (IsDOMIfaceAndProtoClass(clasp)) { rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return; } JS::Rooted descRoot(aCx); JS::MutableHandle desc(&descRoot); // This check may go through a wrapper, but as we checked above @@ -6368,16 +6364,20 @@ nsDocument::RegisterElement(JSContext* a continue; } MOZ_ASSERT(elem->IsHTMLElement(nameAtom)); nsWrapperCache* cache; CallQueryInterface(elem, &cache); MOZ_ASSERT(cache, "Element doesn't support wrapper cache?"); + // We want to set the custom prototype in the caller's comparment. + // In the case that element is in a different compartment, + // this will set the prototype on the element's wrapper and + // thus only visible in the wrapper's compartment. JS::RootedObject wrapper(aCx); if ((wrapper = cache->GetWrapper()) && JS_WrapObject(aCx, &wrapper)) { if (!JS_SetPrototype(aCx, wrapper, wrappedProto)) { continue; } } EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition); diff --git a/dom/base/test/chrome/chrome.ini b/dom/base/test/chrome/chrome.ini --- a/dom/base/test/chrome/chrome.ini +++ b/dom/base/test/chrome/chrome.ini @@ -11,16 +11,18 @@ support-files = file_bug816340.xul file_bug990812-1.xul file_bug990812-2.xul file_bug990812-3.xul file_bug990812-4.xul file_bug990812-5.xul fileconstructor_file.png frame_bug814638.xul + frame_registerElement_content.html + registerElement_ep.js host_bug814638.xul window_nsITextInputProcessor.xul title_window.xul [test_bug206691.xul] [test_bug339494.xul] [test_bug357450.xul] [test_bug380418.html] @@ -53,13 +55,15 @@ skip-if = buildapp == 'mulet' [test_bug814638.xul] [test_bug816340.xul] [test_bug914381.html] [test_bug990812.xul] [test_bug1063837.xul] [test_cpows.xul] skip-if = buildapp == 'mulet' [test_document_register.xul] +[test_registerElement_content.xul] +[test_registerElement_ep.xul] [test_domparsing.xul] [test_fileconstructor.xul] [test_fileconstructor_tempfile.xul] [test_nsITextInputProcessor.xul] [test_title.xul] diff --git a/dom/base/test/chrome/frame_registerElement_content.html b/dom/base/test/chrome/frame_registerElement_content.html new file mode 100644 --- /dev/null +++ b/dom/base/test/chrome/frame_registerElement_content.html @@ -0,0 +1,5 @@ + + + + + diff --git a/dom/base/test/chrome/registerElement_ep.js b/dom/base/test/chrome/registerElement_ep.js new file mode 100644 --- /dev/null +++ b/dom/base/test/chrome/registerElement_ep.js @@ -0,0 +1,8 @@ +var proto = Object.create(HTMLElement.prototype); +proto.magicNumber = 42; +proto.createdCallback = function() { + finishTest(this.magicNumber === 42); +}; +document.registerElement("x-foo", { prototype: proto }); + +document.createElement("x-foo"); diff --git a/dom/base/test/chrome/test_registerElement_content.xul b/dom/base/test/chrome/test_registerElement_content.xul new file mode 100644 --- /dev/null +++ b/dom/base/test/chrome/test_registerElement_content.xul @@ -0,0 +1,55 @@ + + + + + + + + + + Mozilla Bug 1130028 + + + + + + diff --git a/dom/base/test/chrome/test_registerElement_ep.xul b/dom/base/test/chrome/test_registerElement_ep.xul new file mode 100644 --- /dev/null +++ b/dom/base/test/chrome/test_registerElement_ep.xul @@ -0,0 +1,44 @@ + + + + + + + + + + Mozilla Bug 1130028 + + + + + +