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:
Neil Deakin 2018-07-19 08:38:45 -04:00
Родитель d3cf48d4ba
Коммит 0cc9fd046c
6 изменённых файлов: 98 добавлений и 2 удалений

Просмотреть файл

@ -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;
};