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: #include "nsContentUtils.h" michael@0: #include "nsIDocument.h" michael@0: #include "prprf.h" michael@0: #include "nsGlobalWindow.h" michael@0: #include "ScriptSettings.h" michael@0: #include "mozilla/DOMEventTargetHelper.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/EventListenerManager.h" michael@0: #include "mozilla/Likely.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: using namespace dom; michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(DOMEventTargetHelper) michael@0: if (MOZ_UNLIKELY(cb.WantDebugInfo())) { michael@0: char name[512]; michael@0: nsAutoString uri; michael@0: if (tmp->mOwnerWindow && tmp->mOwnerWindow->GetExtantDoc()) { michael@0: tmp->mOwnerWindow->GetExtantDoc()->GetDocumentURI(uri); michael@0: } michael@0: PR_snprintf(name, sizeof(name), "DOMEventTargetHelper %s", michael@0: NS_ConvertUTF16toUTF8(uri).get()); michael@0: cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); michael@0: } else { michael@0: NS_IMPL_CYCLE_COLLECTION_DESCRIBE(DOMEventTargetHelper, tmp->mRefCnt.get()) michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(DOMEventTargetHelper) michael@0: if (tmp->IsBlack()) { michael@0: if (tmp->mListenerManager) { michael@0: tmp->mListenerManager->MarkForCC(); michael@0: } michael@0: return true; michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(DOMEventTargetHelper) michael@0: return tmp->IsBlackAndDoesNotNeedTracing(tmp); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(DOMEventTargetHelper) michael@0: return tmp->IsBlack(); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMEventTargetHelper) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) michael@0: NS_INTERFACE_MAP_ENTRY(dom::EventTarget) michael@0: NS_INTERFACE_MAP_ENTRY(DOMEventTargetHelper) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(DOMEventTargetHelper, michael@0: LastRelease()) michael@0: michael@0: NS_IMPL_DOMTARGET_DEFAULTS(DOMEventTargetHelper) michael@0: michael@0: DOMEventTargetHelper::~DOMEventTargetHelper() michael@0: { michael@0: if (nsPIDOMWindow* owner = GetOwner()) { michael@0: static_cast(owner)->RemoveEventTargetObject(this); michael@0: } michael@0: if (mListenerManager) { michael@0: mListenerManager->Disconnect(); michael@0: } michael@0: ReleaseWrapper(this); michael@0: } michael@0: michael@0: void michael@0: DOMEventTargetHelper::BindToOwner(nsPIDOMWindow* aOwner) michael@0: { michael@0: MOZ_ASSERT(!aOwner || aOwner->IsInnerWindow()); michael@0: nsCOMPtr glob = do_QueryInterface(aOwner); michael@0: BindToOwner(glob); michael@0: } michael@0: michael@0: void michael@0: DOMEventTargetHelper::BindToOwner(nsIGlobalObject* aOwner) michael@0: { michael@0: if (mParentObject) { michael@0: if (mOwnerWindow) { michael@0: static_cast(mOwnerWindow)->RemoveEventTargetObject(this); michael@0: mOwnerWindow = nullptr; michael@0: } michael@0: mParentObject = nullptr; michael@0: mHasOrHasHadOwnerWindow = false; michael@0: } michael@0: if (aOwner) { michael@0: mParentObject = aOwner; michael@0: // Let's cache the result of this QI for fast access and off main thread usage michael@0: mOwnerWindow = nsCOMPtr(do_QueryInterface(aOwner)).get(); michael@0: if (mOwnerWindow) { michael@0: mHasOrHasHadOwnerWindow = true; michael@0: static_cast(mOwnerWindow)->AddEventTargetObject(this); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: DOMEventTargetHelper::BindToOwner(DOMEventTargetHelper* aOther) michael@0: { michael@0: if (mOwnerWindow) { michael@0: static_cast(mOwnerWindow)->RemoveEventTargetObject(this); michael@0: mOwnerWindow = nullptr; michael@0: mParentObject = nullptr; michael@0: mHasOrHasHadOwnerWindow = false; michael@0: } michael@0: if (aOther) { michael@0: mHasOrHasHadOwnerWindow = aOther->HasOrHasHadOwner(); michael@0: if (aOther->GetParentObject()) { michael@0: mParentObject = aOther->GetParentObject(); michael@0: // Let's cache the result of this QI for fast access and off main thread usage michael@0: mOwnerWindow = nsCOMPtr(do_QueryInterface(mParentObject)).get(); michael@0: if (mOwnerWindow) { michael@0: mHasOrHasHadOwnerWindow = true; michael@0: static_cast(mOwnerWindow)->AddEventTargetObject(this); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: DOMEventTargetHelper::DisconnectFromOwner() michael@0: { michael@0: mOwnerWindow = nullptr; michael@0: mParentObject = nullptr; michael@0: // Event listeners can't be handled anymore, so we can release them here. michael@0: if (mListenerManager) { michael@0: mListenerManager->Disconnect(); michael@0: mListenerManager = nullptr; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMEventTargetHelper::RemoveEventListener(const nsAString& aType, michael@0: nsIDOMEventListener* aListener, michael@0: bool aUseCapture) michael@0: { michael@0: EventListenerManager* elm = GetExistingListenerManager(); michael@0: if (elm) { michael@0: elm->RemoveEventListener(aType, aListener, aUseCapture); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(DOMEventTargetHelper) michael@0: michael@0: NS_IMETHODIMP michael@0: DOMEventTargetHelper::AddEventListener(const nsAString& aType, michael@0: nsIDOMEventListener* aListener, michael@0: bool aUseCapture, michael@0: bool aWantsUntrusted, michael@0: uint8_t aOptionalArgc) michael@0: { michael@0: NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1, michael@0: "Won't check if this is chrome, you want to set " michael@0: "aWantsUntrusted to false or make the aWantsUntrusted " michael@0: "explicit by making aOptionalArgc non-zero."); michael@0: michael@0: if (aOptionalArgc < 2) { michael@0: nsresult rv = WantsUntrusted(&aWantsUntrusted); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: EventListenerManager* elm = GetOrCreateListenerManager(); michael@0: NS_ENSURE_STATE(elm); michael@0: elm->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: DOMEventTargetHelper::AddEventListener(const nsAString& aType, michael@0: EventListener* aListener, michael@0: bool aUseCapture, michael@0: const Nullable& aWantsUntrusted, michael@0: ErrorResult& aRv) michael@0: { michael@0: bool wantsUntrusted; michael@0: if (aWantsUntrusted.IsNull()) { michael@0: nsresult rv = WantsUntrusted(&wantsUntrusted); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: } else { michael@0: wantsUntrusted = aWantsUntrusted.Value(); michael@0: } michael@0: michael@0: EventListenerManager* elm = GetOrCreateListenerManager(); michael@0: if (!elm) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return; michael@0: } michael@0: elm->AddEventListener(aType, aListener, aUseCapture, wantsUntrusted); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMEventTargetHelper::AddSystemEventListener(const nsAString& aType, michael@0: nsIDOMEventListener* aListener, michael@0: bool aUseCapture, michael@0: bool aWantsUntrusted, michael@0: uint8_t aOptionalArgc) michael@0: { michael@0: NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1, michael@0: "Won't check if this is chrome, you want to set " michael@0: "aWantsUntrusted to false or make the aWantsUntrusted " michael@0: "explicit by making aOptionalArgc non-zero."); michael@0: michael@0: if (aOptionalArgc < 2) { michael@0: nsresult rv = WantsUntrusted(&aWantsUntrusted); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_AddSystemEventListener(this, aType, aListener, aUseCapture, michael@0: aWantsUntrusted); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMEventTargetHelper::DispatchEvent(nsIDOMEvent* aEvent, bool* aRetVal) michael@0: { michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: nsresult rv = michael@0: EventDispatcher::DispatchDOMEvent(this, nullptr, aEvent, nullptr, &status); michael@0: michael@0: *aRetVal = (status != nsEventStatus_eConsumeNoDefault); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: DOMEventTargetHelper::DispatchTrustedEvent(const nsAString& aEventName) michael@0: { michael@0: nsCOMPtr event; michael@0: NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); michael@0: nsresult rv = event->InitEvent(aEventName, false, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return DispatchTrustedEvent(event); michael@0: } michael@0: michael@0: nsresult michael@0: DOMEventTargetHelper::DispatchTrustedEvent(nsIDOMEvent* event) michael@0: { michael@0: event->SetTrusted(true); michael@0: michael@0: bool dummy; michael@0: return DispatchEvent(event, &dummy); michael@0: } michael@0: michael@0: nsresult michael@0: DOMEventTargetHelper::SetEventHandler(nsIAtom* aType, michael@0: JSContext* aCx, michael@0: const JS::Value& aValue) michael@0: { michael@0: nsRefPtr handler; michael@0: JS::Rooted callable(aCx); michael@0: if (aValue.isObject() && michael@0: JS_ObjectIsCallable(aCx, callable = &aValue.toObject())) { michael@0: handler = new EventHandlerNonNull(callable, dom::GetIncumbentGlobal()); michael@0: } michael@0: SetEventHandler(aType, EmptyString(), handler); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: DOMEventTargetHelper::GetEventHandler(nsIAtom* aType, michael@0: JSContext* aCx, michael@0: JS::Value* aValue) michael@0: { michael@0: EventHandlerNonNull* handler = GetEventHandler(aType, EmptyString()); michael@0: if (handler) { michael@0: *aValue = JS::ObjectValue(*handler->Callable()); michael@0: } else { michael@0: *aValue = JS::NullValue(); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: DOMEventTargetHelper::PreHandleEvent(EventChainPreVisitor& aVisitor) michael@0: { michael@0: aVisitor.mCanHandle = true; michael@0: aVisitor.mParentTarget = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: DOMEventTargetHelper::PostHandleEvent(EventChainPostVisitor& aVisitor) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: DOMEventTargetHelper::DispatchDOMEvent(WidgetEvent* aEvent, michael@0: nsIDOMEvent* aDOMEvent, michael@0: nsPresContext* aPresContext, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: return EventDispatcher::DispatchDOMEvent(this, aEvent, aDOMEvent, michael@0: aPresContext, aEventStatus); michael@0: } michael@0: michael@0: EventListenerManager* michael@0: DOMEventTargetHelper::GetOrCreateListenerManager() michael@0: { michael@0: if (!mListenerManager) { michael@0: mListenerManager = new EventListenerManager(this); michael@0: } michael@0: michael@0: return mListenerManager; michael@0: } michael@0: michael@0: EventListenerManager* michael@0: DOMEventTargetHelper::GetExistingListenerManager() const michael@0: { michael@0: return mListenerManager; michael@0: } michael@0: michael@0: nsIScriptContext* michael@0: DOMEventTargetHelper::GetContextForEventHandlers(nsresult* aRv) michael@0: { michael@0: *aRv = CheckInnerWindowCorrectness(); michael@0: if (NS_FAILED(*aRv)) { michael@0: return nullptr; michael@0: } michael@0: nsPIDOMWindow* owner = GetOwner(); michael@0: return owner ? static_cast(owner)->GetContextInternal() michael@0: : nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: DOMEventTargetHelper::WantsUntrusted(bool* aRetVal) michael@0: { michael@0: nsresult rv; michael@0: nsIScriptContext* context = GetContextForEventHandlers(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsCOMPtr doc = michael@0: nsContentUtils::GetDocumentFromScriptContext(context); michael@0: // We can let listeners on workers to always handle all the events. michael@0: *aRetVal = (doc && !nsContentUtils::IsChromeDoc(doc)) || !NS_IsMainThread(); michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: DOMEventTargetHelper::EventListenerAdded(nsIAtom* aType) michael@0: { michael@0: ErrorResult rv; michael@0: EventListenerWasAdded(Substring(nsDependentAtomString(aType), 2), rv); michael@0: } michael@0: michael@0: void michael@0: DOMEventTargetHelper::EventListenerRemoved(nsIAtom* aType) michael@0: { michael@0: ErrorResult rv; michael@0: EventListenerWasRemoved(Substring(nsDependentAtomString(aType), 2), rv); michael@0: } michael@0: michael@0: } // namespace mozilla