mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-10 09:19:28 +00:00
Bug 783129 - Implementation of document.register without shadow DOM support. r=mrbkap, bent
This commit is contained in:
parent
35b1d04204
commit
de026650c9
@ -61,6 +61,7 @@
|
||||
#include "nsTreeWalker.h"
|
||||
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
|
||||
#include "nsContentCID.h"
|
||||
#include "nsError.h"
|
||||
@ -135,10 +136,12 @@
|
||||
#include "nsIPrompt.h"
|
||||
#include "nsIPropertyBag2.h"
|
||||
#include "nsIDOMPageTransitionEvent.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsFrameLoader.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsObjectLoadingContent.h"
|
||||
#include "nsHtml5TreeOpExecutor.h"
|
||||
#include "nsIDOMElementReplaceEvent.h"
|
||||
#ifdef MOZ_MEDIA
|
||||
#include "nsHTMLMediaElement.h"
|
||||
#endif // MOZ_MEDIA
|
||||
@ -168,6 +171,7 @@
|
||||
#include "mozilla/dom/Link.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsDOMTouchEvent.h"
|
||||
#include "DictionaryHelpers.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
@ -189,6 +193,8 @@ nsWeakPtr nsDocument::sFullScreenDoc = nullptr;
|
||||
// which requested DOM full-screen mode.
|
||||
nsWeakPtr nsDocument::sFullScreenRootDoc = nullptr;
|
||||
|
||||
nsIDOMElement* nsDocument::sCurrentUpgradeElement = nullptr;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
static PRLogModuleInfo* gDocumentLeakPRLog;
|
||||
static PRLogModuleInfo* gCspPRLog;
|
||||
@ -1369,6 +1375,11 @@ nsDocument::~nsDocument()
|
||||
mInDestructor = true;
|
||||
mInUnlinkOrDeletion = true;
|
||||
|
||||
nsISupports* supports;
|
||||
QueryInterface(NS_GET_IID(nsCycleCollectionISupports), reinterpret_cast<void**>(&supports));
|
||||
NS_ASSERTION(supports, "Failed to QI to nsCycleCollectionISupports?!");
|
||||
nsContentUtils::DropJSObjects(supports);
|
||||
|
||||
// Clear mObservers to keep it in sync with the mutationobserver list
|
||||
mObservers.Clear();
|
||||
|
||||
@ -1694,7 +1705,25 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
|
||||
struct CustomPrototypeTraceArgs {
|
||||
TraceCallback callback;
|
||||
void* closure;
|
||||
};
|
||||
|
||||
|
||||
static PLDHashOperator
|
||||
CustomPrototypeTrace(const nsAString& aName, JSObject* aObject, void *aArg)
|
||||
{
|
||||
CustomPrototypeTraceArgs* traceArgs = static_cast<CustomPrototypeTraceArgs*>(aArg);
|
||||
MOZ_ASSERT(aObject, "Protocol object value must not be null");
|
||||
traceArgs->callback(aObject, "mCustomPrototypes entry", traceArgs->closure);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
|
||||
CustomPrototypeTraceArgs customPrototypeArgs = { aCallback, aClosure };
|
||||
tmp->mCustomPrototypes.EnumerateRead(CustomPrototypeTrace, &customPrototypeArgs);
|
||||
nsINode::Trace(tmp, aCallback, aClosure);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
@ -1758,6 +1787,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
||||
|
||||
tmp->mIdentifierMap.Clear();
|
||||
|
||||
tmp->mCustomPrototypes.Clear();
|
||||
|
||||
if (tmp->mAnimationController) {
|
||||
tmp->mAnimationController->Unlink();
|
||||
}
|
||||
@ -1782,6 +1813,7 @@ nsDocument::Init()
|
||||
mIdentifierMap.Init();
|
||||
mStyledLinks.Init();
|
||||
mRadioGroups.Init();
|
||||
mCustomPrototypes.Init();
|
||||
|
||||
// Force initialization.
|
||||
nsINode::nsSlots* slots = Slots();
|
||||
@ -1818,6 +1850,15 @@ nsDocument::Init()
|
||||
mImageTracker.Init();
|
||||
mPlugins.Init();
|
||||
|
||||
nsXPCOMCycleCollectionParticipant* participant;
|
||||
CallQueryInterface(this, &participant);
|
||||
NS_ASSERTION(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
|
||||
|
||||
nsISupports* thisSupports;
|
||||
QueryInterface(NS_GET_IID(nsCycleCollectionISupports), reinterpret_cast<void**>(&thisSupports));
|
||||
NS_ASSERTION(thisSupports, "Failed to QI to nsCycleCollectionISupports!");
|
||||
nsContentUtils::HoldJSObjects(thisSupports, participant);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -4582,6 +4623,218 @@ nsDocument::GetElementsByTagName(const nsAString& aTagname,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
CustomElementConstructor(JSContext *aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
|
||||
|
||||
JSObject* global = JS_GetGlobalForObject(aCx, &args.callee());
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryWrapper(aCx, global);
|
||||
MOZ_ASSERT(window, "Should have a non-null window");
|
||||
|
||||
nsIDocument* document = window->GetDoc();
|
||||
|
||||
// Function name is the type of the custom element.
|
||||
JSString* jsFunName = JS_GetFunctionId(JS_ValueToFunction(aCx,
|
||||
args.calleev()));
|
||||
nsDependentJSString elemName;
|
||||
if (!elemName.init(aCx, jsFunName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> newElement;
|
||||
nsresult rv = document->CreateElem(elemName, nullptr, kNameSpaceID_XHTML,
|
||||
getter_AddRefs(newElement));
|
||||
JS::Value v;
|
||||
rv = nsContentUtils::WrapNative(aCx, global, newElement,
|
||||
(nsWrapperCache*) nullptr, &v);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
JS_SET_RVAL(aCx, aVp, v);
|
||||
return true;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetPrototypeFromClassInfoId(JSContext* aCx, JSObject* aScope,
|
||||
nsDOMClassInfoID aClassInfoId,
|
||||
JSObject** aPrototype)
|
||||
{
|
||||
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
||||
|
||||
nsIClassInfo* classInfo = NS_GetDOMClassInfoInstance(aClassInfoId);
|
||||
NS_ENSURE_TRUE(classInfo, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
nsresult rv = xpc->GetWrappedNativePrototype(aCx, aScope, classInfo,
|
||||
getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JSObject* interface;
|
||||
rv = holder->GetJSObject(&interface);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aPrototype = interface;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocument::Register(const nsAString& aName, const JS::Value& aOptions,
|
||||
JSContext* aCx, uint8_t aArgc,
|
||||
jsval* aConstructor /* out param */)
|
||||
{
|
||||
nsAutoString lcName;
|
||||
nsContentUtils::ASCIIToLower(aName, lcName);
|
||||
|
||||
NS_ENSURE_TRUE(StringBeginsWith(lcName, NS_LITERAL_STRING("x-")),
|
||||
NS_ERROR_DOM_INVALID_CHARACTER_ERR);
|
||||
|
||||
nsresult rv = nsContentUtils::CheckQName(lcName, false);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INVALID_CHARACTER_ERR);
|
||||
|
||||
DocumentRegisterOptions options;
|
||||
nsCOMPtr<nsILifecycleCallback> lifecycleCallback;
|
||||
|
||||
if (aArgc > 0) {
|
||||
rv = options.Init(aCx, &aOptions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (options.lifecycle) {
|
||||
JS::Value callbacksValue;
|
||||
rv = options.lifecycle->GetAsJSVal(&callbacksValue);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
LifecycleCallbacks callbacks;
|
||||
rv = callbacks.Init(aCx, &callbacksValue);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
lifecycleCallback = callbacks.created;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(wchen): Templates are not currently supported. Bug 818976.
|
||||
if (options.customTemplate) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsIScriptGlobalObject* sgo = GetScopeObject();
|
||||
NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
|
||||
JSObject* global = sgo->GetGlobalJSObject();
|
||||
|
||||
JSObject* protoObject;
|
||||
if (!options.prototype) {
|
||||
// If a prototype is not provided, we use the interface prototype for
|
||||
// HTMLSpanElement.
|
||||
rv = GetPrototypeFromClassInfoId(aCx, global,
|
||||
eDOMClassInfo_HTMLSpanElement_id,
|
||||
&protoObject);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
JS::Value customProto;
|
||||
rv = options.prototype->GetAsJSVal(&customProto);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_TRUE(!customProto.isPrimitive(), NS_ERROR_TYPE_ERR);
|
||||
|
||||
protoObject = &customProto.toObject();
|
||||
|
||||
// If a prototype is provided, we must check to ensure that it inherits
|
||||
// from HTMLElement.
|
||||
JSObject* htmlProto;
|
||||
rv = GetPrototypeFromClassInfoId(aCx, global,
|
||||
eDOMClassInfo_HTMLElement_id,
|
||||
&htmlProto);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Check the proto chain for HTMLElement prototype.
|
||||
JSObject* protoProto;
|
||||
NS_ENSURE_TRUE(JS_GetPrototype(aCx, protoObject, &protoProto),
|
||||
NS_ERROR_UNEXPECTED);
|
||||
while (protoProto) {
|
||||
if (protoProto == htmlProto) {
|
||||
break;
|
||||
}
|
||||
NS_ENSURE_TRUE(JS_GetPrototype(aCx, protoProto, &protoProto),
|
||||
NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(protoProto, NS_ERROR_DOM_TYPE_MISMATCH_ERR);
|
||||
}
|
||||
|
||||
// Associate the prototype with the custom element.
|
||||
mCustomPrototypes.Put(lcName, protoObject);
|
||||
|
||||
// Do element upgrade.
|
||||
nsRefPtr<nsContentList> list = GetElementsByTagName(lcName);
|
||||
for (int32_t i = 0; i < list->Length(false); i++) {
|
||||
nsINode* oldNode = list->Item(i, false);
|
||||
|
||||
// TODO(wchen): Perform upgrade on Shadow DOM when implemented.
|
||||
// Bug 806506.
|
||||
nsCOMPtr<nsINode> newNode;
|
||||
rv = nsNodeUtils::Clone(oldNode, true, getter_AddRefs(newNode));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsINode* parentNode = oldNode->GetParentNode();
|
||||
MOZ_ASSERT(parentNode, "Node obtained by GetElementsByTagName.");
|
||||
nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newNode);
|
||||
MOZ_ASSERT(newElement, "Cloned of node obtained by GetElementsByTagName.");
|
||||
|
||||
ErrorResult error;
|
||||
parentNode->ReplaceChild(*newNode, *oldNode, error);
|
||||
rv = error.ErrorCode();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Dispatch elementreplaced to replaced elements.
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
rv = CreateEvent(NS_LITERAL_STRING("elementreplace"), getter_AddRefs(event));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (lifecycleCallback) {
|
||||
// Update static member used for "this" translation
|
||||
// when calling callback.
|
||||
sCurrentUpgradeElement = newElement.get();
|
||||
lifecycleCallback->Created(nullptr);
|
||||
sCurrentUpgradeElement = nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMElementReplaceEvent> ptEvent = do_QueryInterface(event);
|
||||
MOZ_ASSERT(ptEvent);
|
||||
|
||||
rv = ptEvent->InitElementReplaceEvent(NS_LITERAL_STRING("elementreplace"),
|
||||
false, false, newElement);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
event->SetTrusted(true);
|
||||
event->SetTarget(oldNode);
|
||||
nsEventDispatcher::DispatchDOMEvent(oldNode, nullptr, event,
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
||||
nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
|
||||
NS_LITERAL_STRING("elementupgrade"),
|
||||
true, true);
|
||||
|
||||
// Create constructor to return. Store the name of the custom element as the
|
||||
// name of the function.
|
||||
JSFunction* constructor = JS_NewFunction(aCx, CustomElementConstructor, 0,
|
||||
JSFUN_CONSTRUCTOR, nullptr,
|
||||
NS_ConvertUTF16toUTF8(lcName).get());
|
||||
JSObject* constructorObject = JS_GetFunctionObject(constructor);
|
||||
|
||||
JS::Value protoVal = OBJECT_TO_JSVAL(protoObject);
|
||||
NS_ENSURE_TRUE(JS_SetProperty(aCx, constructorObject, "prototype",
|
||||
&protoVal), NS_ERROR_UNEXPECTED);
|
||||
|
||||
JS::Value constructorVal = OBJECT_TO_JSVAL(constructorObject);
|
||||
NS_ENSURE_TRUE(JS_SetProperty(aCx, protoObject, "constructor",
|
||||
&constructorVal), NS_ERROR_UNEXPECTED);
|
||||
|
||||
// Return the constructor.
|
||||
*aConstructor = OBJECT_TO_JSVAL(constructorObject);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsContentList>
|
||||
nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
|
||||
const nsAString& aLocalName)
|
||||
|
@ -1005,6 +1005,18 @@ public:
|
||||
// DocSizeOfIncludingThis is inherited from nsIDocument.
|
||||
|
||||
virtual nsIDOMNode* AsDOMNode() { return this; }
|
||||
|
||||
static nsIDOMElement* CurrentUpgradeElement()
|
||||
{
|
||||
return sCurrentUpgradeElement;
|
||||
}
|
||||
|
||||
JSObject* GetCustomPrototype(const nsAString& aElementName)
|
||||
{
|
||||
JSObject* prototype = nullptr;
|
||||
mCustomPrototypes.Get(aElementName, &prototype);
|
||||
return prototype;
|
||||
}
|
||||
protected:
|
||||
friend class nsNodeUtils;
|
||||
|
||||
@ -1162,6 +1174,14 @@ protected:
|
||||
// pop one off this stack, restoring the previous full-screen state
|
||||
nsTArray<nsWeakPtr> mFullScreenStack;
|
||||
|
||||
// Weak reference to the current upgraded element in document.register.
|
||||
// This member is used for function |this| translation when calling a
|
||||
// callback interface in document.register.
|
||||
static nsIDOMElement* sCurrentUpgradeElement;
|
||||
|
||||
// Hashtable for custom element prototypes in web components.
|
||||
nsDataHashtable<nsStringHashKey, JSObject*> mCustomPrototypes;
|
||||
|
||||
nsRefPtr<nsEventListenerManager> mListenerManager;
|
||||
nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
|
||||
nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
|
||||
|
@ -156,6 +156,16 @@ public:
|
||||
nullptr, aNodesWithProperties, nullptr, aResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones aNode, its attributes and, if aDeep is true, its descendant nodes
|
||||
*/
|
||||
static nsresult Clone(nsINode *aNode, bool aDeep, nsINode **aResult)
|
||||
{
|
||||
nsCOMArray<nsINode> dummyNodeWithProperties;
|
||||
return CloneAndAdopt(aNode, true, aDeep, nullptr, nullptr, nullptr,
|
||||
dummyNodeWithProperties, aNode->GetParent(), aResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks aNode, its attributes and descendant nodes. If aNewNodeInfoManager is
|
||||
* not null, it is used to create new nodeinfos for the nodes. Also reparents
|
||||
|
@ -816,6 +816,8 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext,
|
||||
if (aEventType.LowerCaseEqualsLiteral("commandevent") ||
|
||||
aEventType.LowerCaseEqualsLiteral("commandevents"))
|
||||
return NS_NewDOMCommandEvent(aDOMEvent, aPresContext, nullptr);
|
||||
if (aEventType.LowerCaseEqualsLiteral("elementreplace"))
|
||||
return NS_NewDOMElementReplaceEvent(aDOMEvent, aPresContext, nullptr);
|
||||
if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") ||
|
||||
aEventType.LowerCaseEqualsLiteral("datacontainerevents"))
|
||||
return NS_NewDOMDataContainerEvent(aDOMEvent, aPresContext, nullptr);
|
||||
|
@ -2360,6 +2360,9 @@ nsDOMClassInfo::Init()
|
||||
nsCOMPtr<nsIXPCFunctionThisTranslator> elt = new nsEventListenerThisTranslator();
|
||||
sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsIDOMEventListener), elt);
|
||||
|
||||
nsCOMPtr<nsIXPCFunctionThisTranslator> lct = new nsLifecycleCallbacksThisTranslator();
|
||||
sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsILifecycleCallback), lct);
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> sm =
|
||||
do_GetService("@mozilla.org/scriptsecuritymanager;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -7941,6 +7944,13 @@ nsElementSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
|
||||
}
|
||||
#endif
|
||||
|
||||
nsAutoString elementName;
|
||||
nsContentUtils::ASCIIToLower(element->NodeName(), elementName);
|
||||
if (StringBeginsWith(elementName, NS_LITERAL_STRING("x-"))) {
|
||||
// Don't allow slim wrappers for custom elements.
|
||||
return rv == NS_SUCCESS_ALLOW_SLIM_WRAPPERS ? NS_OK : rv;
|
||||
}
|
||||
|
||||
nsIDocument *doc = element->HasFlag(NODE_FORCE_XBL_BINDINGS) ?
|
||||
element->OwnerDoc() :
|
||||
element->GetCurrentDoc();
|
||||
@ -7986,6 +7996,17 @@ nsElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we have a registered x-tag then we fix the prototype.
|
||||
nsAutoString elementName;
|
||||
nsContentUtils::ASCIIToLower(element->NodeName(), elementName);
|
||||
if (StringBeginsWith(elementName, NS_LITERAL_STRING("x-"))) {
|
||||
nsDocument* document = static_cast<nsDocument*>(element->OwnerDoc());
|
||||
JSObject* prototype = document->GetCustomPrototype(elementName);
|
||||
if (prototype) {
|
||||
return JS_SetPrototype(cx, obj, prototype) ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
nsIDocument* doc;
|
||||
if (element->HasFlag(NODE_FORCE_XBL_BINDINGS)) {
|
||||
doc = element->OwnerDoc();
|
||||
@ -10197,6 +10218,24 @@ nsEventListenerThisTranslator::TranslateThis(nsISupports *aInitialThis,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsLifecycleCallbacksThisTranslator)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIXPCFunctionThisTranslator)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
NS_IMPL_ADDREF(nsLifecycleCallbacksThisTranslator)
|
||||
NS_IMPL_RELEASE(nsLifecycleCallbacksThisTranslator)
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLifecycleCallbacksThisTranslator::TranslateThis(nsISupports *aInitialThis,
|
||||
nsISupports **_retval)
|
||||
{
|
||||
NS_IF_ADDREF(*_retval = nsDocument::CurrentUpgradeElement());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMConstructorSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
|
||||
JSObject *globalObj, JSObject **parentObj)
|
||||
|
@ -1208,6 +1208,24 @@ public:
|
||||
NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR
|
||||
};
|
||||
|
||||
class nsLifecycleCallbacksThisTranslator : public nsIXPCFunctionThisTranslator
|
||||
{
|
||||
public:
|
||||
nsLifecycleCallbacksThisTranslator()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~nsLifecycleCallbacksThisTranslator()
|
||||
{
|
||||
}
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIXPCFunctionThisTranslator
|
||||
NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR
|
||||
};
|
||||
|
||||
class nsDOMConstructorSH : public nsDOMGenericSH
|
||||
{
|
||||
protected:
|
||||
|
@ -27,7 +27,7 @@ interface nsIDOMLocation;
|
||||
* http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html
|
||||
*/
|
||||
|
||||
[scriptable, uuid(31ce7ae7-15d5-4fc8-912b-ae0e23e93146)]
|
||||
[scriptable, uuid(6f3aac2e-ae11-487a-9eb0-0e12c66b3b21)]
|
||||
interface nsIDOMDocument : nsIDOMNode
|
||||
{
|
||||
readonly attribute nsIDOMDocumentType doctype;
|
||||
@ -47,6 +47,11 @@ interface nsIDOMDocument : nsIDOMNode
|
||||
raises(DOMException);
|
||||
nsIDOMNodeList getElementsByTagName(in DOMString tagname);
|
||||
|
||||
[optional_argc,
|
||||
implicit_jscontext] jsval register(in DOMString name,
|
||||
[optional] in jsval options)
|
||||
raises(DOMException);
|
||||
|
||||
// Introduced in DOM Level 2:
|
||||
[optional_argc] nsIDOMNode importNode(in nsIDOMNode importedNode,
|
||||
[optional] in boolean deep)
|
||||
@ -396,3 +401,24 @@ interface nsIDOMDocument : nsIDOMNode
|
||||
*/
|
||||
readonly attribute DOMString compatMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for lifecycle callbacks in document.register.
|
||||
*/
|
||||
|
||||
[scriptable, function, uuid(bfcd6299-081e-45d8-99f6-cb06fe2e70de)]
|
||||
interface nsILifecycleCallback : nsISupports
|
||||
{
|
||||
void created(in nsIDOMElement dummy);
|
||||
};
|
||||
|
||||
dictionary LifecycleCallbacks {
|
||||
nsILifecycleCallback created;
|
||||
};
|
||||
|
||||
dictionary DocumentRegisterOptions {
|
||||
nsIVariant prototype;
|
||||
nsIDOMDocumentFragment customTemplate;
|
||||
nsIVariant lifecycle;
|
||||
};
|
||||
|
||||
|
@ -47,6 +47,7 @@ XPIDLSRCS = \
|
||||
nsIDOMUserProximityEvent.idl \
|
||||
nsIDOMDeviceOrientationEvent.idl \
|
||||
nsIDOMDeviceMotionEvent.idl \
|
||||
nsIDOMElementReplaceEvent.idl \
|
||||
nsIDOMScrollAreaEvent.idl \
|
||||
nsIDOMTransitionEvent.idl \
|
||||
nsIDOMAnimationEvent.idl \
|
||||
|
24
dom/interfaces/events/nsIDOMElementReplaceEvent.idl
Normal file
24
dom/interfaces/events/nsIDOMElementReplaceEvent.idl
Normal file
@ -0,0 +1,24 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIDOMEvent.idl"
|
||||
|
||||
interface nsIDOMElement;
|
||||
|
||||
[scriptable, builtinclass, uuid(f57f7c46-d420-4f32-a61b-0eb585d30ee1)]
|
||||
interface nsIDOMElementReplaceEvent : nsIDOMEvent
|
||||
{
|
||||
readonly attribute nsIDOMElement upgrade;
|
||||
|
||||
void initElementReplaceEvent(in DOMString typeArg,
|
||||
in boolean canBubbleArg,
|
||||
in boolean canCancelArg,
|
||||
in nsIDOMElement upgrade);
|
||||
};
|
||||
|
||||
dictionary ElementReplaceEventInit : EventInit
|
||||
{
|
||||
nsIDOMElement upgrade;
|
||||
};
|
@ -27,6 +27,7 @@ DIRS += \
|
||||
storageevent \
|
||||
pointerlock \
|
||||
webapps \
|
||||
webcomponents \
|
||||
$(NULL)
|
||||
|
||||
#needs IPC support, also tests do not run successfully in Firefox for now
|
||||
|
@ -528,7 +528,8 @@ var interfaceNamesInGlobalScope =
|
||||
"RTCPeerConnection",
|
||||
"LocalMediaStream",
|
||||
"CSSConditionRule",
|
||||
"CSSGroupingRule"
|
||||
"CSSGroupingRule",
|
||||
"ElementReplaceEvent"
|
||||
]
|
||||
|
||||
for (var i in SpecialPowers.Components.interfaces) {
|
||||
|
18
dom/tests/mochitest/webcomponents/Makefile.in
Normal file
18
dom/tests/mochitest/webcomponents/Makefile.in
Normal file
@ -0,0 +1,18 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DEPTH = @DEPTH@
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = @relativesrcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MOCHITEST_FILES = \
|
||||
test_document_register.html \
|
||||
test_document_register_lifecycle.html \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
@ -0,0 +1,79 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=783129
|
||||
-->
|
||||
<head>
|
||||
<title>Test for document.register using custom prototype</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<script>
|
||||
var gElementUpgraded = false;
|
||||
var gElementReplaced = false;
|
||||
|
||||
function elementUpgrade() {
|
||||
gElementUpgraded = true;
|
||||
|
||||
// Check for prototype on upgraded element.
|
||||
var documentElement = document.getElementById("grabme");
|
||||
ok(documentElement.hello, "Upgraded element should inherit 'hello' method from prototype.");
|
||||
documentElement.hello();
|
||||
|
||||
var customChild = document.getElementById("kid");
|
||||
ok(customChild, "Upgrade should preserve children.");
|
||||
ok(customChild.parentNode == documentElement, "Parent should be updated to new custom element.");
|
||||
|
||||
// Try creating new element and checking for prototype.
|
||||
var constructedElement = document.createElement("x-hello");
|
||||
ok(constructedElement.hello, "Created element should inherit 'hello' method from prototype.");
|
||||
constructedElement.hello();
|
||||
}
|
||||
|
||||
function elementReplace(e) {
|
||||
gElementReplaced = true;
|
||||
|
||||
ok(e.upgrade != e.target, "Upgraded element should be different from the target.");
|
||||
ok(e.upgrade.firstElementChild.id == "kid", "Upgrade element should have a child.");
|
||||
ok(e.target.firstElementChild.id == "kid", "Replacement element should have a child.");
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
var HtmlProto = function() {};
|
||||
HtmlProto.prototype = HTMLElement.prototype;
|
||||
|
||||
// Create a prototype that inheits from HTMLElement.
|
||||
var customProto = new HtmlProto();
|
||||
customProto.hello = function() {
|
||||
ok(true, "Custom element should use provided prototype.");
|
||||
};
|
||||
|
||||
var oldElem = document.getElementById("grabme");
|
||||
oldElem.addEventListener("elementreplace", elementReplace);
|
||||
|
||||
document.addEventListener("elementupgrade", elementUpgrade);
|
||||
var elementConstructor = document.register("x-hello", { prototype: customProto });
|
||||
|
||||
// Try creating new element and checking for prototype.
|
||||
var constructedElement = new elementConstructor();
|
||||
ok(constructedElement.hello, "Created element should inherit 'hello' method from prototype.");
|
||||
constructedElement.hello();
|
||||
|
||||
ok(!oldElem.hello, "Element obtained prior to registration should not have inherited prototype.");
|
||||
|
||||
ok(gElementUpgraded && gElementReplaced, "Upgrade and replace events should have been fired.");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body onload="startTest()">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
|
||||
<x-hello id="grabme">
|
||||
<div id="kid"></div>
|
||||
</x-hello>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=783129
|
||||
-->
|
||||
<head>
|
||||
<title>Test for document.register lifecycle callback</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<script>
|
||||
|
||||
var gLifecycleCallbackCalled = false;
|
||||
|
||||
var lifecycleCallbacks = {
|
||||
created: function() {
|
||||
is(this.getAttribute("id"), "grabme", "|this| value should be the upgrade element");
|
||||
gLifecycleCallbackCalled = true;
|
||||
}
|
||||
};
|
||||
|
||||
function startTest() {
|
||||
var HtmlProto = function() {};
|
||||
HtmlProto.prototype = HTMLElement.prototype;
|
||||
|
||||
// Create a prototype that inheits from HTMLElement.
|
||||
var customProto = new HtmlProto();
|
||||
customProto.hello = function() {
|
||||
ok(true, "Custom element should use provided prototype.");
|
||||
};
|
||||
|
||||
var elementConstructor = document.register("x-hello", { prototype: customProto, lifecycle: lifecycleCallbacks });
|
||||
|
||||
ok(gLifecycleCallbackCalled, "Lifecycle callback should be called.");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body onload="startTest()">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
|
||||
<x-hello id="grabme">
|
||||
<div id="kid"></div>
|
||||
</x-hello>
|
||||
</body>
|
||||
</html>
|
@ -14,6 +14,8 @@ dictionaries = [
|
||||
[ 'DOMFileMetadataParameters', 'nsIDOMLockedFile.idl' ],
|
||||
[ 'XMLHttpRequestParameters', 'nsIXMLHttpRequest.idl' ],
|
||||
[ 'DeviceStorageEnumerationParameters', 'nsIDOMDeviceStorage.idl' ],
|
||||
[ 'DocumentRegisterOptions', 'nsIDOMDocument.idl' ],
|
||||
[ 'LifecycleCallbacks', 'nsIDOMDocument.idl' ],
|
||||
[ 'CameraSize', 'nsIDOMCameraManager.idl' ],
|
||||
[ 'CameraRegion', 'nsIDOMCameraManager.idl' ],
|
||||
[ 'CameraPosition', 'nsIDOMCameraManager.idl' ],
|
||||
@ -34,5 +36,6 @@ special_includes = [
|
||||
# name of the type to not include using #include "typename.h"
|
||||
exclude_automatic_type_include = [
|
||||
'nsISupports',
|
||||
'mozIDOMApplication'
|
||||
'mozIDOMApplication',
|
||||
'nsILifecycleCallback'
|
||||
]
|
||||
|
@ -32,6 +32,7 @@ simple_events = [
|
||||
'MozWifiConnectionInfoEvent',
|
||||
'MozCellBroadcastEvent',
|
||||
#endif
|
||||
'ElementReplaceEvent',
|
||||
'DeviceStorageChangeEvent',
|
||||
'PopupBlockedEvent'
|
||||
]
|
||||
|
Loading…
x
Reference in New Issue
Block a user