michael@0: /* -*- Mode: C++; tab-width: 2; 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_DOMEventTargetHelper_h_ michael@0: #define mozilla_DOMEventTargetHelper_h_ michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "MainThreadUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/EventListenerManager.h" michael@0: #include "mozilla/dom/EventTarget.h" michael@0: michael@0: class JSCompartment; michael@0: michael@0: namespace mozilla { michael@0: michael@0: class ErrorResult; michael@0: michael@0: #define NS_DOMEVENTTARGETHELPER_IID \ michael@0: { 0xa28385c6, 0x9451, 0x4d7e, \ michael@0: { 0xa3, 0xdd, 0xf4, 0xb6, 0x87, 0x2f, 0xa4, 0x76 } } michael@0: michael@0: class DOMEventTargetHelper : public dom::EventTarget michael@0: { michael@0: public: michael@0: DOMEventTargetHelper() michael@0: : mParentObject(nullptr) michael@0: , mOwnerWindow(nullptr) michael@0: , mHasOrHasHadOwnerWindow(false) michael@0: { michael@0: } michael@0: DOMEventTargetHelper(nsPIDOMWindow* aWindow) michael@0: : mParentObject(nullptr) michael@0: , mOwnerWindow(nullptr) michael@0: , mHasOrHasHadOwnerWindow(false) michael@0: { michael@0: BindToOwner(aWindow); michael@0: // All objects coming through here are WebIDL objects michael@0: SetIsDOMBinding(); michael@0: } michael@0: DOMEventTargetHelper(DOMEventTargetHelper* aOther) michael@0: : mParentObject(nullptr) michael@0: , mOwnerWindow(nullptr) michael@0: , mHasOrHasHadOwnerWindow(false) michael@0: { michael@0: BindToOwner(aOther); michael@0: // All objects coming through here are WebIDL objects michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: virtual ~DOMEventTargetHelper(); michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(DOMEventTargetHelper) michael@0: michael@0: NS_DECL_NSIDOMEVENTTARGET michael@0: michael@0: virtual EventListenerManager* GetExistingListenerManager() const MOZ_OVERRIDE; michael@0: virtual EventListenerManager* GetOrCreateListenerManager() MOZ_OVERRIDE; michael@0: michael@0: using dom::EventTarget::RemoveEventListener; michael@0: virtual void AddEventListener(const nsAString& aType, michael@0: dom::EventListener* aListener, michael@0: bool aCapture, michael@0: const dom::Nullable& aWantsUntrusted, michael@0: ErrorResult& aRv) MOZ_OVERRIDE; michael@0: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOMEVENTTARGETHELPER_IID) michael@0: michael@0: void GetParentObject(nsIScriptGlobalObject **aParentObject) michael@0: { michael@0: if (mParentObject) { michael@0: CallQueryInterface(mParentObject, aParentObject); michael@0: } else { michael@0: *aParentObject = nullptr; michael@0: } michael@0: } michael@0: michael@0: static DOMEventTargetHelper* FromSupports(nsISupports* aSupports) michael@0: { michael@0: dom::EventTarget* target = static_cast(aSupports); michael@0: #ifdef DEBUG michael@0: { michael@0: nsCOMPtr target_qi = do_QueryInterface(aSupports); michael@0: michael@0: // If this assertion fires the QI implementation for the object in michael@0: // question doesn't use the EventTarget pointer as the michael@0: // nsISupports pointer. That must be fixed, or we'll crash... michael@0: NS_ASSERTION(target_qi == target, "Uh, fix QI!"); michael@0: } michael@0: #endif michael@0: michael@0: return static_cast(target); michael@0: } michael@0: michael@0: bool HasListenersFor(nsIAtom* aTypeWithOn) michael@0: { michael@0: return mListenerManager && mListenerManager->HasListenersFor(aTypeWithOn); michael@0: } michael@0: michael@0: nsresult SetEventHandler(nsIAtom* aType, michael@0: JSContext* aCx, michael@0: const JS::Value& aValue); michael@0: using dom::EventTarget::SetEventHandler; michael@0: void GetEventHandler(nsIAtom* aType, michael@0: JSContext* aCx, michael@0: JS::Value* aValue); michael@0: using dom::EventTarget::GetEventHandler; michael@0: virtual nsIDOMWindow* GetOwnerGlobal() MOZ_OVERRIDE michael@0: { michael@0: return nsPIDOMWindow::GetOuterFromCurrentInner(GetOwner()); michael@0: } michael@0: michael@0: nsresult CheckInnerWindowCorrectness() michael@0: { michael@0: NS_ENSURE_STATE(!mHasOrHasHadOwnerWindow || mOwnerWindow); michael@0: if (mOwnerWindow) { michael@0: NS_ASSERTION(mOwnerWindow->IsInnerWindow(), "Should have inner window here!\n"); michael@0: nsPIDOMWindow* outer = mOwnerWindow->GetOuterWindow(); michael@0: if (!outer || outer->GetCurrentInnerWindow() != mOwnerWindow) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsPIDOMWindow* GetOwner() const { return mOwnerWindow; } michael@0: void BindToOwner(nsIGlobalObject* aOwner); michael@0: void BindToOwner(nsPIDOMWindow* aOwner); michael@0: void BindToOwner(DOMEventTargetHelper* aOther); michael@0: virtual void DisconnectFromOwner(); michael@0: nsIGlobalObject* GetParentObject() const { return mParentObject; } michael@0: bool HasOrHasHadOwner() { return mHasOrHasHadOwnerWindow; } michael@0: michael@0: virtual void EventListenerAdded(nsIAtom* aType) MOZ_OVERRIDE; michael@0: virtual void EventListenerRemoved(nsIAtom* aType) MOZ_OVERRIDE; michael@0: virtual void EventListenerWasAdded(const nsAString& aType, michael@0: ErrorResult& aRv, michael@0: JSCompartment* aCompartment = nullptr) {} michael@0: virtual void EventListenerWasRemoved(const nsAString& aType, michael@0: ErrorResult& aRv, michael@0: JSCompartment* aCompartment = nullptr) {} michael@0: protected: michael@0: nsresult WantsUntrusted(bool* aRetVal); michael@0: michael@0: nsRefPtr mListenerManager; michael@0: // Dispatch a trusted, non-cancellable and non-bubbling event to |this|. michael@0: nsresult DispatchTrustedEvent(const nsAString& aEventName); michael@0: // Make |event| trusted and dispatch |aEvent| to |this|. michael@0: nsresult DispatchTrustedEvent(nsIDOMEvent* aEvent); michael@0: michael@0: virtual void LastRelease() {} michael@0: private: michael@0: // Inner window or sandbox. michael@0: nsIGlobalObject* mParentObject; michael@0: // mParentObject pre QI-ed and cached michael@0: // (it is needed for off main thread access) michael@0: nsPIDOMWindow* mOwnerWindow; michael@0: bool mHasOrHasHadOwnerWindow; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(DOMEventTargetHelper, michael@0: NS_DOMEVENTTARGETHELPER_IID) michael@0: michael@0: } // namespace mozilla michael@0: michael@0: // XPIDL event handlers michael@0: #define NS_IMPL_EVENT_HANDLER(_class, _event) \ michael@0: NS_IMETHODIMP _class::GetOn##_event(JSContext* aCx, \ michael@0: JS::MutableHandle aValue) \ michael@0: { \ michael@0: GetEventHandler(nsGkAtoms::on##_event, aCx, aValue.address()); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHODIMP _class::SetOn##_event(JSContext* aCx, \ michael@0: JS::Handle aValue) \ michael@0: { \ michael@0: return SetEventHandler(nsGkAtoms::on##_event, aCx, aValue); \ michael@0: } michael@0: michael@0: #define NS_IMPL_FORWARD_EVENT_HANDLER(_class, _event, _baseclass) \ michael@0: NS_IMETHODIMP _class::GetOn##_event(JSContext* aCx, \ michael@0: JS::MutableHandle aValue) \ michael@0: { \ michael@0: return _baseclass::GetOn##_event(aCx, aValue); \ michael@0: } \ michael@0: NS_IMETHODIMP _class::SetOn##_event(JSContext* aCx, \ michael@0: JS::Handle aValue) \ michael@0: { \ michael@0: return _baseclass::SetOn##_event(aCx, aValue); \ michael@0: } michael@0: michael@0: // WebIDL event handlers michael@0: #define IMPL_EVENT_HANDLER(_event) \ michael@0: inline mozilla::dom::EventHandlerNonNull* GetOn##_event() \ michael@0: { \ michael@0: if (NS_IsMainThread()) { \ michael@0: return GetEventHandler(nsGkAtoms::on##_event, EmptyString()); \ michael@0: } \ michael@0: return GetEventHandler(nullptr, NS_LITERAL_STRING(#_event)); \ michael@0: } \ michael@0: inline void SetOn##_event(mozilla::dom::EventHandlerNonNull* aCallback) \ michael@0: { \ michael@0: if (NS_IsMainThread()) { \ michael@0: SetEventHandler(nsGkAtoms::on##_event, EmptyString(), aCallback); \ michael@0: } else { \ michael@0: SetEventHandler(nullptr, NS_LITERAL_STRING(#_event), aCallback); \ michael@0: } \ michael@0: } michael@0: michael@0: /* Use this macro to declare functions that forward the behavior of this michael@0: * interface to another object. michael@0: * This macro doesn't forward PreHandleEvent because sometimes subclasses michael@0: * want to override it. michael@0: */ michael@0: #define NS_FORWARD_NSIDOMEVENTTARGET_NOPREHANDLEEVENT(_to) \ michael@0: NS_IMETHOD AddEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture, bool wantsUntrusted, uint8_t _argc) { \ michael@0: return _to AddEventListener(type, listener, useCapture, wantsUntrusted, _argc); \ michael@0: } \ michael@0: NS_IMETHOD AddSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture, bool aWantsUntrusted, uint8_t _argc) { \ michael@0: return _to AddSystemEventListener(type, listener, aUseCapture, aWantsUntrusted, _argc); \ michael@0: } \ michael@0: NS_IMETHOD RemoveEventListener(const nsAString & type, nsIDOMEventListener *listener, bool useCapture) { \ michael@0: return _to RemoveEventListener(type, listener, useCapture); \ michael@0: } \ michael@0: NS_IMETHOD RemoveSystemEventListener(const nsAString & type, nsIDOMEventListener *listener, bool aUseCapture) { \ michael@0: return _to RemoveSystemEventListener(type, listener, aUseCapture); \ michael@0: } \ michael@0: NS_IMETHOD DispatchEvent(nsIDOMEvent *evt, bool *_retval) { \ michael@0: return _to DispatchEvent(evt, _retval); \ michael@0: } \ michael@0: virtual mozilla::dom::EventTarget* GetTargetForDOMEvent() { \ michael@0: return _to GetTargetForDOMEvent(); \ michael@0: } \ michael@0: virtual mozilla::dom::EventTarget* GetTargetForEventTargetChain() { \ michael@0: return _to GetTargetForEventTargetChain(); \ michael@0: } \ michael@0: virtual nsresult WillHandleEvent( \ michael@0: mozilla::EventChainPostVisitor & aVisitor) { \ michael@0: return _to WillHandleEvent(aVisitor); \ michael@0: } \ michael@0: virtual nsresult PostHandleEvent( \ michael@0: mozilla::EventChainPostVisitor & aVisitor) { \ michael@0: return _to PostHandleEvent(aVisitor); \ michael@0: } \ michael@0: virtual nsresult DispatchDOMEvent(mozilla::WidgetEvent* aEvent, nsIDOMEvent* aDOMEvent, nsPresContext* aPresContext, nsEventStatus* aEventStatus) { \ michael@0: return _to DispatchDOMEvent(aEvent, aDOMEvent, aPresContext, aEventStatus); \ michael@0: } \ michael@0: virtual mozilla::EventListenerManager* GetOrCreateListenerManager() { \ michael@0: return _to GetOrCreateListenerManager(); \ michael@0: } \ michael@0: virtual mozilla::EventListenerManager* GetExistingListenerManager() const { \ michael@0: return _to GetExistingListenerManager(); \ michael@0: } \ michael@0: virtual nsIScriptContext * GetContextForEventHandlers(nsresult *aRv) { \ michael@0: return _to GetContextForEventHandlers(aRv); \ michael@0: } \ michael@0: virtual JSContext * GetJSContextForEventHandlers(void) { \ michael@0: return _to GetJSContextForEventHandlers(); \ michael@0: } michael@0: michael@0: #define NS_REALLY_FORWARD_NSIDOMEVENTTARGET(_class) \ michael@0: using _class::AddEventListener; \ michael@0: using _class::RemoveEventListener; \ michael@0: NS_FORWARD_NSIDOMEVENTTARGET(_class::) \ michael@0: virtual mozilla::EventListenerManager* \ michael@0: GetOrCreateListenerManager() { \ michael@0: return _class::GetOrCreateListenerManager(); \ michael@0: } \ michael@0: virtual mozilla::EventListenerManager* \ michael@0: GetExistingListenerManager() const { \ michael@0: return _class::GetExistingListenerManager(); \ michael@0: } michael@0: michael@0: #endif // mozilla_DOMEventTargetHelper_h_