michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_JSEventHandler_h_ michael@0: #define mozilla_JSEventHandler_h_ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/dom/EventHandlerBinding.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsIDOMKeyEvent.h" michael@0: #include "nsIDOMEventListener.h" michael@0: #include "nsIScriptContext.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: class TypedEventHandler michael@0: { michael@0: public: michael@0: enum HandlerType michael@0: { michael@0: eUnset = 0, michael@0: eNormal = 0x1, michael@0: eOnError = 0x2, michael@0: eOnBeforeUnload = 0x3, michael@0: eTypeBits = 0x3 michael@0: }; michael@0: michael@0: TypedEventHandler() michael@0: : mBits(0) michael@0: { michael@0: } michael@0: michael@0: TypedEventHandler(dom::EventHandlerNonNull* aHandler) michael@0: { michael@0: Assign(aHandler, eNormal); michael@0: } michael@0: michael@0: TypedEventHandler(dom::OnErrorEventHandlerNonNull* aHandler) michael@0: { michael@0: Assign(aHandler, eOnError); michael@0: } michael@0: michael@0: TypedEventHandler(dom::OnBeforeUnloadEventHandlerNonNull* aHandler) michael@0: { michael@0: Assign(aHandler, eOnBeforeUnload); michael@0: } michael@0: michael@0: TypedEventHandler(const TypedEventHandler& aOther) michael@0: { michael@0: if (aOther.HasEventHandler()) { michael@0: // Have to make sure we take our own ref michael@0: Assign(aOther.Ptr(), aOther.Type()); michael@0: } else { michael@0: mBits = 0; michael@0: } michael@0: } michael@0: michael@0: ~TypedEventHandler() michael@0: { michael@0: ReleaseHandler(); michael@0: } michael@0: michael@0: HandlerType Type() const michael@0: { michael@0: return HandlerType(mBits & eTypeBits); michael@0: } michael@0: michael@0: bool HasEventHandler() const michael@0: { michael@0: return !!Ptr(); michael@0: } michael@0: michael@0: void SetHandler(const TypedEventHandler& aHandler) michael@0: { michael@0: if (aHandler.HasEventHandler()) { michael@0: ReleaseHandler(); michael@0: Assign(aHandler.Ptr(), aHandler.Type()); michael@0: } else { michael@0: ForgetHandler(); michael@0: } michael@0: } michael@0: michael@0: dom::EventHandlerNonNull* NormalEventHandler() const michael@0: { michael@0: MOZ_ASSERT(Type() == eNormal && Ptr()); michael@0: return reinterpret_cast(Ptr()); michael@0: } michael@0: michael@0: void SetHandler(dom::EventHandlerNonNull* aHandler) michael@0: { michael@0: ReleaseHandler(); michael@0: Assign(aHandler, eNormal); michael@0: } michael@0: michael@0: dom::OnBeforeUnloadEventHandlerNonNull* OnBeforeUnloadEventHandler() const michael@0: { michael@0: MOZ_ASSERT(Type() == eOnBeforeUnload); michael@0: return reinterpret_cast(Ptr()); michael@0: } michael@0: michael@0: void SetHandler(dom::OnBeforeUnloadEventHandlerNonNull* aHandler) michael@0: { michael@0: ReleaseHandler(); michael@0: Assign(aHandler, eOnBeforeUnload); michael@0: } michael@0: michael@0: dom::OnErrorEventHandlerNonNull* OnErrorEventHandler() const michael@0: { michael@0: MOZ_ASSERT(Type() == eOnError); michael@0: return reinterpret_cast(Ptr()); michael@0: } michael@0: michael@0: void SetHandler(dom::OnErrorEventHandlerNonNull* aHandler) michael@0: { michael@0: ReleaseHandler(); michael@0: Assign(aHandler, eOnError); michael@0: } michael@0: michael@0: dom::CallbackFunction* Ptr() const michael@0: { michael@0: // Have to cast eTypeBits so we don't have to worry about michael@0: // promotion issues after the bitflip. michael@0: return reinterpret_cast(mBits & michael@0: ~uintptr_t(eTypeBits)); michael@0: } michael@0: michael@0: void ForgetHandler() michael@0: { michael@0: ReleaseHandler(); michael@0: mBits = 0; michael@0: } michael@0: michael@0: bool operator==(const TypedEventHandler& aOther) const michael@0: { michael@0: return michael@0: Ptr() && aOther.Ptr() && michael@0: Ptr()->CallbackPreserveColor() == aOther.Ptr()->CallbackPreserveColor(); michael@0: } michael@0: michael@0: private: michael@0: void operator=(const TypedEventHandler&) MOZ_DELETE; michael@0: michael@0: void ReleaseHandler() michael@0: { michael@0: nsISupports* ptr = Ptr(); michael@0: NS_IF_RELEASE(ptr); michael@0: } michael@0: michael@0: void Assign(nsISupports* aHandler, HandlerType aType) michael@0: { michael@0: MOZ_ASSERT(aHandler, "Must have handler"); michael@0: NS_ADDREF(aHandler); michael@0: mBits = uintptr_t(aHandler) | uintptr_t(aType); michael@0: } michael@0: michael@0: uintptr_t mBits; michael@0: }; michael@0: michael@0: /** michael@0: * Implemented by script event listeners. Used to retrieve the script object michael@0: * corresponding to the event target and the handler itself. michael@0: * michael@0: * Note, mTarget is a raw pointer and the owner of the JSEventHandler object michael@0: * is expected to call Disconnect()! michael@0: */ michael@0: michael@0: #define NS_JSEVENTHANDLER_IID \ michael@0: { 0x4f486881, 0x1956, 0x4079, \ michael@0: { 0x8c, 0xa0, 0xf3, 0xbd, 0x60, 0x5c, 0xc2, 0x79 } } michael@0: michael@0: class JSEventHandler : public nsIDOMEventListener michael@0: { michael@0: public: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_JSEVENTHANDLER_IID) michael@0: michael@0: JSEventHandler(nsISupports* aTarget, nsIAtom* aType, michael@0: const TypedEventHandler& aTypedHandler); michael@0: michael@0: virtual ~JSEventHandler(); michael@0: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: michael@0: // nsIDOMEventListener interface michael@0: NS_DECL_NSIDOMEVENTLISTENER michael@0: michael@0: nsISupports* GetEventTarget() const michael@0: { michael@0: return mTarget; michael@0: } michael@0: michael@0: void Disconnect() michael@0: { michael@0: mTarget = nullptr; michael@0: } michael@0: michael@0: const TypedEventHandler& GetTypedEventHandler() const michael@0: { michael@0: return mTypedHandler; michael@0: } michael@0: michael@0: void ForgetHandler() michael@0: { michael@0: mTypedHandler.ForgetHandler(); michael@0: } michael@0: michael@0: nsIAtom* EventName() const michael@0: { michael@0: return mEventName; michael@0: } michael@0: michael@0: // Set a handler for this event listener. The handler must already michael@0: // be bound to the right target. michael@0: void SetHandler(const TypedEventHandler& aTypedHandler) michael@0: { michael@0: mTypedHandler.SetHandler(aTypedHandler); michael@0: } michael@0: void SetHandler(dom::EventHandlerNonNull* aHandler) michael@0: { michael@0: mTypedHandler.SetHandler(aHandler); michael@0: } michael@0: void SetHandler(dom::OnBeforeUnloadEventHandlerNonNull* aHandler) michael@0: { michael@0: mTypedHandler.SetHandler(aHandler); michael@0: } michael@0: void SetHandler(dom::OnErrorEventHandlerNonNull* aHandler) michael@0: { michael@0: mTypedHandler.SetHandler(aHandler); michael@0: } michael@0: michael@0: size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return 0; michael@0: michael@0: // Measurement of the following members may be added later if DMD finds it michael@0: // is worthwhile: michael@0: // - mTarget michael@0: // michael@0: // The following members are not measured: michael@0: // - mTypedHandler: may be shared with others michael@0: // - mEventName: shared with others michael@0: } michael@0: michael@0: size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) michael@0: { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS(JSEventHandler) michael@0: michael@0: bool IsBlackForCC(); michael@0: michael@0: protected: michael@0: nsISupports* mTarget; michael@0: nsCOMPtr mEventName; michael@0: TypedEventHandler mTypedHandler; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(JSEventHandler, NS_JSEVENTHANDLER_IID) michael@0: michael@0: } // namespace mozilla michael@0: michael@0: /** michael@0: * Factory function. aHandler must already be bound to aTarget. michael@0: * aContext is allowed to be null if aHandler is already set up. michael@0: */ michael@0: nsresult NS_NewJSEventHandler(nsISupports* aTarget, michael@0: nsIAtom* aType, michael@0: const mozilla::TypedEventHandler& aTypedHandler, michael@0: mozilla::JSEventHandler** aReturn); michael@0: michael@0: #endif // mozilla_JSEventHandler_h_ michael@0: