зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1461742, add a mechanism so that custom elements can implement interfaces akin to XBL implements. This is accomplished by an additional chrome-only callback getCustomInterface that can be implemented by custom elements, r=bz
This commit is contained in:
Родитель
d3cf48d4ba
Коммит
0cc9fd046c
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/dom/DocGroup.h"
|
||||
#include "nsHTMLTags.h"
|
||||
#include "jsapi.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -92,6 +93,9 @@ CustomElementCallback::Call()
|
|||
static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
|
||||
mArgs.name, mArgs.oldValue, mArgs.newValue, mArgs.namespaceURI);
|
||||
break;
|
||||
case nsIDocument::eGetCustomInterface:
|
||||
MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -475,6 +479,10 @@ CustomElementRegistry::CreateCustomElementCallback(
|
|||
func = aDefinition->mCallbacks->mAttributeChangedCallback.Value();
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDocument::eGetCustomInterface:
|
||||
MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback");
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is no such callback, stop.
|
||||
|
@ -831,7 +839,8 @@ CustomElementRegistry::Define(JSContext* aCx,
|
|||
* 10.3. Let lifecycleCallbacks be a map with the four keys
|
||||
* "connectedCallback", "disconnectedCallback", "adoptedCallback", and
|
||||
* "attributeChangedCallback", each of which belongs to an entry whose
|
||||
* value is null.
|
||||
* value is null. The 'getCustomInterface' callback is also included
|
||||
* for chrome usage.
|
||||
* 10.4. For each of the four keys callbackName in lifecycleCallbacks:
|
||||
* 1. Let callbackValue be Get(prototype, callbackName). Rethrow any
|
||||
* exceptions.
|
||||
|
@ -1161,6 +1170,51 @@ CustomElementRegistry::Upgrade(Element* aElement,
|
|||
aElement->SetCustomElementDefinition(aDefinition);
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
CustomElementRegistry::CallGetCustomInterface(Element* aElement,
|
||||
const nsIID& aIID)
|
||||
{
|
||||
MOZ_ASSERT(aElement);
|
||||
|
||||
if (nsContentUtils::IsChromeDoc(aElement->OwnerDoc())) {
|
||||
CustomElementDefinition* definition = aElement->GetCustomElementDefinition();
|
||||
if (definition && definition->mCallbacks &&
|
||||
definition->mCallbacks->mGetCustomInterfaceCallback.WasPassed() &&
|
||||
definition->mLocalName == aElement->NodeInfo()->NameAtom()) {
|
||||
|
||||
LifecycleGetCustomInterfaceCallback* func =
|
||||
definition->mCallbacks->mGetCustomInterfaceCallback.Value();
|
||||
JS::Rooted<JSObject*> customInterface(RootingCx());
|
||||
|
||||
nsCOMPtr<nsIJSID> iid = nsJSID::NewID(aIID);
|
||||
func->Call(aElement, iid, &customInterface);
|
||||
if (customInterface) {
|
||||
RefPtr<nsXPCWrappedJS> wrappedJS;
|
||||
nsresult rv =
|
||||
nsXPCWrappedJS::GetNewOrUsed(customInterface,
|
||||
NS_GET_IID(nsISupports),
|
||||
getter_AddRefs(wrappedJS));
|
||||
if (NS_SUCCEEDED(rv) && wrappedJS) {
|
||||
// Check if the returned object implements the desired interface.
|
||||
nsCOMPtr<nsISupports> retval;
|
||||
if (NS_SUCCEEDED(wrappedJS->QueryInterface(aIID,
|
||||
getter_AddRefs(retval)))) {
|
||||
return retval.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, check if the element supports the interface directly, and just use that.
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
if (NS_SUCCEEDED(aElement->QueryInterface(aIID, getter_AddRefs(supports)))) {
|
||||
return supports.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
// CustomElementReactionsStack
|
||||
|
||||
|
@ -1372,6 +1426,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementDefinition)
|
|||
cb.NoteXPCOMChild(callbacks->mAdoptedCallback.Value());
|
||||
}
|
||||
|
||||
if (callbacks->mGetCustomInterfaceCallback.WasPassed()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mGetCustomInterfaceCallback");
|
||||
cb.NoteXPCOMChild(callbacks->mGetCustomInterfaceCallback.Value());
|
||||
}
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mConstructor");
|
||||
cb.NoteXPCOMChild(tmp->mConstructor);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
|
|
@ -416,6 +416,20 @@ public:
|
|||
*/
|
||||
static void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv);
|
||||
|
||||
/**
|
||||
* To allow native code to call methods of chrome-implemented custom elements,
|
||||
* a helper method may be defined in the custom element called
|
||||
* 'getCustomInterfaceCallback'. This method takes an IID and returns an
|
||||
* object which implements an XPCOM interface. If there is no
|
||||
* getCustomInterfaceCallback or the callback doesn't return an object,
|
||||
* QueryInterface is called on aElement to see if this interface is
|
||||
* implemented directly.
|
||||
*
|
||||
* This returns null if aElement is not from a chrome document.
|
||||
*/
|
||||
static already_AddRefed<nsISupports> CallGetCustomInterface(
|
||||
Element* aElement, const nsIID& aIID);
|
||||
|
||||
/**
|
||||
* Registers an unresolved custom element that is a candidate for
|
||||
* upgrade. |aTypeName| is the name of the custom element type, if it is not
|
||||
|
|
|
@ -4304,6 +4304,16 @@ Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32
|
|||
return updated;
|
||||
}
|
||||
|
||||
template<class T> void
|
||||
Element::GetCustomInterface(nsGetterAddRefs<T> aResult)
|
||||
{
|
||||
nsCOMPtr<nsISupports> iface =
|
||||
CustomElementRegistry::CallGetCustomInterface(this, NS_GET_TEMPLATE_IID(T));
|
||||
if (iface) {
|
||||
CallQueryInterface(iface, static_cast<T**>(aResult));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Element::ClearServoData(nsIDocument* aDoc) {
|
||||
MOZ_ASSERT(aDoc);
|
||||
|
|
|
@ -1987,6 +1987,14 @@ private:
|
|||
*/
|
||||
MOZ_CAN_RUN_SCRIPT nsRect GetClientAreaRect();
|
||||
|
||||
/**
|
||||
* GetCustomInterface is somewhat like a GetInterface, but it is expected
|
||||
* that the implementation is provided by a custom element or via the
|
||||
* the XBL implements keyword. To use this, create a public method that
|
||||
* wraps a call to GetCustomInterface.
|
||||
*/
|
||||
template<class T> void GetCustomInterface(nsGetterAddRefs<T> aResult);
|
||||
|
||||
// Prevent people from doing pointless checks/casts on Element instances.
|
||||
void IsElement() = delete;
|
||||
void AsElement() = delete;
|
||||
|
|
|
@ -3083,7 +3083,8 @@ public:
|
|||
eConnected,
|
||||
eDisconnected,
|
||||
eAdopted,
|
||||
eAttributeChanged
|
||||
eAttributeChanged,
|
||||
eGetCustomInterface
|
||||
};
|
||||
|
||||
nsIDocument* GetTopLevelContentDocument();
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
interface IID;
|
||||
|
||||
callback LifecycleConnectedCallback = void();
|
||||
callback LifecycleDisconnectedCallback = void();
|
||||
callback LifecycleAdoptedCallback = void(Document? oldDocument,
|
||||
|
@ -18,10 +20,12 @@ callback LifecycleAttributeChangedCallback = void(DOMString attrName,
|
|||
DOMString? oldValue,
|
||||
DOMString? newValue,
|
||||
DOMString? namespaceURI);
|
||||
callback LifecycleGetCustomInterfaceCallback = object?(IID iid);
|
||||
|
||||
dictionary LifecycleCallbacks {
|
||||
LifecycleConnectedCallback connectedCallback;
|
||||
LifecycleDisconnectedCallback disconnectedCallback;
|
||||
LifecycleAdoptedCallback adoptedCallback;
|
||||
LifecycleAttributeChangedCallback attributeChangedCallback;
|
||||
[ChromeOnly] LifecycleGetCustomInterfaceCallback getCustomInterfaceCallback;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче