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 "AccessCheck.h" michael@0: #include "base/basictypes.h" michael@0: #include "ipc/IPCMessageUtils.h" michael@0: #include "mozilla/dom/Event.h" michael@0: #include "mozilla/ContentEvents.h" michael@0: #include "mozilla/DOMEventTargetHelper.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "mozilla/InternalMutationEvent.h" michael@0: #include "mozilla/MiscEvents.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/TextEvents.h" michael@0: #include "mozilla/TouchEvents.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsDeviceContext.h" michael@0: #include "nsError.h" michael@0: #include "nsGlobalWindow.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsJSEnvironment.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsPIWindowRoot.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: namespace workers { michael@0: extern bool IsCurrentThreadRunningChromeWorker(); michael@0: } // namespace workers michael@0: michael@0: static char *sPopupAllowedEvents; michael@0: michael@0: Event::Event(EventTarget* aOwner, michael@0: nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent) michael@0: { michael@0: ConstructorInit(aOwner, aPresContext, aEvent); michael@0: } michael@0: michael@0: Event::Event(nsPIDOMWindow* aParent) michael@0: { michael@0: ConstructorInit(static_cast(aParent), nullptr, nullptr); michael@0: } michael@0: michael@0: void michael@0: Event::ConstructorInit(EventTarget* aOwner, michael@0: nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent) michael@0: { michael@0: SetIsDOMBinding(); michael@0: SetOwner(aOwner); michael@0: mIsMainThreadEvent = mOwner || NS_IsMainThread(); michael@0: if (mIsMainThreadEvent) { michael@0: nsJSContext::LikelyShortLivingObjectCreated(); michael@0: } michael@0: michael@0: mPrivateDataDuplicated = false; michael@0: michael@0: if (aEvent) { michael@0: mEvent = aEvent; michael@0: mEventIsInternal = false; michael@0: } michael@0: else { michael@0: mEventIsInternal = true; michael@0: /* michael@0: A derived class might want to allocate its own type of aEvent michael@0: (derived from WidgetEvent). To do this, it should take care to pass michael@0: a non-nullptr aEvent to this ctor, e.g.: michael@0: michael@0: FooEvent::FooEvent(..., WidgetEvent* aEvent) michael@0: : Event(..., aEvent ? aEvent : new WidgetEvent()) michael@0: michael@0: Then, to override the mEventIsInternal assignments done by the michael@0: base ctor, it should do this in its own ctor: michael@0: michael@0: FooEvent::FooEvent(..., WidgetEvent* aEvent) michael@0: ... michael@0: { michael@0: ... michael@0: if (aEvent) { michael@0: mEventIsInternal = false; michael@0: } michael@0: else { michael@0: mEventIsInternal = true; michael@0: } michael@0: ... michael@0: } michael@0: */ michael@0: mEvent = new WidgetEvent(false, 0); michael@0: mEvent->time = PR_Now(); michael@0: } michael@0: michael@0: InitPresContextData(aPresContext); michael@0: } michael@0: michael@0: void michael@0: Event::InitPresContextData(nsPresContext* aPresContext) michael@0: { michael@0: mPresContext = aPresContext; michael@0: // Get the explicit original target (if it's anonymous make it null) michael@0: { michael@0: nsCOMPtr content = GetTargetFromFrame(); michael@0: mExplicitOriginalTarget = content; michael@0: if (content && content->IsInAnonymousSubtree()) { michael@0: mExplicitOriginalTarget = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: Event::~Event() michael@0: { michael@0: NS_ASSERT_OWNINGTHREAD(Event); michael@0: michael@0: if (mEventIsInternal && mEvent) { michael@0: delete mEvent; michael@0: } michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Event) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMEvent) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(Event) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(Event) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(Event) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Event) 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_UNLINK_BEGIN(Event) michael@0: if (tmp->mEventIsInternal) { michael@0: tmp->mEvent->target = nullptr; michael@0: tmp->mEvent->currentTarget = nullptr; michael@0: tmp->mEvent->originalTarget = nullptr; michael@0: switch (tmp->mEvent->eventStructType) { michael@0: case NS_MOUSE_EVENT: michael@0: case NS_MOUSE_SCROLL_EVENT: michael@0: case NS_WHEEL_EVENT: michael@0: case NS_SIMPLE_GESTURE_EVENT: michael@0: case NS_POINTER_EVENT: michael@0: tmp->mEvent->AsMouseEventBase()->relatedTarget = nullptr; michael@0: break; michael@0: case NS_DRAG_EVENT: { michael@0: WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); michael@0: dragEvent->dataTransfer = nullptr; michael@0: dragEvent->relatedTarget = nullptr; michael@0: break; michael@0: } michael@0: case NS_CLIPBOARD_EVENT: michael@0: tmp->mEvent->AsClipboardEvent()->clipboardData = nullptr; michael@0: break; michael@0: case NS_MUTATION_EVENT: michael@0: tmp->mEvent->AsMutationEvent()->mRelatedNode = nullptr; michael@0: break; michael@0: case NS_FOCUS_EVENT: michael@0: tmp->mEvent->AsFocusEvent()->relatedTarget = nullptr; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mPresContext); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mExplicitOriginalTarget); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event) michael@0: if (tmp->mEventIsInternal) { michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->target) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->currentTarget) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->originalTarget) michael@0: switch (tmp->mEvent->eventStructType) { michael@0: case NS_MOUSE_EVENT: michael@0: case NS_MOUSE_SCROLL_EVENT: michael@0: case NS_WHEEL_EVENT: michael@0: case NS_SIMPLE_GESTURE_EVENT: michael@0: case NS_POINTER_EVENT: michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); michael@0: cb.NoteXPCOMChild(tmp->mEvent->AsMouseEventBase()->relatedTarget); michael@0: break; michael@0: case NS_DRAG_EVENT: { michael@0: WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent(); michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->dataTransfer"); michael@0: cb.NoteXPCOMChild(dragEvent->dataTransfer); michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); michael@0: cb.NoteXPCOMChild(dragEvent->relatedTarget); michael@0: break; michael@0: } michael@0: case NS_CLIPBOARD_EVENT: michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->clipboardData"); michael@0: cb.NoteXPCOMChild(tmp->mEvent->AsClipboardEvent()->clipboardData); michael@0: break; michael@0: case NS_MUTATION_EVENT: michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mRelatedNode"); michael@0: cb.NoteXPCOMChild(tmp->mEvent->AsMutationEvent()->mRelatedNode); michael@0: break; michael@0: case NS_FOCUS_EVENT: michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->relatedTarget"); michael@0: cb.NoteXPCOMChild(tmp->mEvent->AsFocusEvent()->relatedTarget); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: bool michael@0: Event::IsChrome(JSContext* aCx) const michael@0: { michael@0: return mIsMainThreadEvent ? michael@0: xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx)) : michael@0: mozilla::dom::workers::IsCurrentThreadRunningChromeWorker(); michael@0: } michael@0: michael@0: // nsIDOMEventInterface michael@0: NS_METHOD michael@0: Event::GetType(nsAString& aType) michael@0: { michael@0: if (!mIsMainThreadEvent || !mEvent->typeString.IsEmpty()) { michael@0: aType = mEvent->typeString; michael@0: return NS_OK; michael@0: } michael@0: const char* name = GetEventName(mEvent->message); michael@0: michael@0: if (name) { michael@0: CopyASCIItoUTF16(name, aType); michael@0: return NS_OK; michael@0: } else if (mEvent->message == NS_USER_DEFINED_EVENT && mEvent->userType) { michael@0: aType = Substring(nsDependentAtomString(mEvent->userType), 2); // Remove "on" michael@0: mEvent->typeString = aType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: aType.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static EventTarget* michael@0: GetDOMEventTarget(nsIDOMEventTarget* aTarget) michael@0: { michael@0: return aTarget ? aTarget->GetTargetForDOMEvent() : nullptr; michael@0: } michael@0: michael@0: EventTarget* michael@0: Event::GetTarget() const michael@0: { michael@0: return GetDOMEventTarget(mEvent->target); michael@0: } michael@0: michael@0: NS_METHOD michael@0: Event::GetTarget(nsIDOMEventTarget** aTarget) michael@0: { michael@0: NS_IF_ADDREF(*aTarget = GetTarget()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: EventTarget* michael@0: Event::GetCurrentTarget() const michael@0: { michael@0: return GetDOMEventTarget(mEvent->currentTarget); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetCurrentTarget(nsIDOMEventTarget** aCurrentTarget) michael@0: { michael@0: NS_IF_ADDREF(*aCurrentTarget = GetCurrentTarget()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // michael@0: // Get the actual event target node (may have been retargeted for mouse events) michael@0: // michael@0: already_AddRefed michael@0: Event::GetTargetFromFrame() michael@0: { michael@0: if (!mPresContext) { return nullptr; } michael@0: michael@0: // Get the target frame (have to get the ESM first) michael@0: nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); michael@0: if (!targetFrame) { return nullptr; } michael@0: michael@0: // get the real content michael@0: nsCOMPtr realEventContent; michael@0: targetFrame->GetContentForEvent(mEvent, getter_AddRefs(realEventContent)); michael@0: return realEventContent.forget(); michael@0: } michael@0: michael@0: EventTarget* michael@0: Event::GetExplicitOriginalTarget() const michael@0: { michael@0: if (mExplicitOriginalTarget) { michael@0: return mExplicitOriginalTarget; michael@0: } michael@0: return GetTarget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetExplicitOriginalTarget(nsIDOMEventTarget** aRealEventTarget) michael@0: { michael@0: NS_IF_ADDREF(*aRealEventTarget = GetExplicitOriginalTarget()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: EventTarget* michael@0: Event::GetOriginalTarget() const michael@0: { michael@0: if (mEvent->originalTarget) { michael@0: return GetDOMEventTarget(mEvent->originalTarget); michael@0: } michael@0: michael@0: return GetTarget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetOriginalTarget(nsIDOMEventTarget** aOriginalTarget) michael@0: { michael@0: NS_IF_ADDREF(*aOriginalTarget = GetOriginalTarget()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: Event::SetTrusted(bool aTrusted) michael@0: { michael@0: mEvent->mFlags.mIsTrusted = aTrusted; michael@0: } michael@0: michael@0: bool michael@0: Event::Init(mozilla::dom::EventTarget* aGlobal) michael@0: { michael@0: if (!mIsMainThreadEvent) { michael@0: return nsContentUtils::ThreadsafeIsCallerChrome(); michael@0: } michael@0: bool trusted = false; michael@0: nsCOMPtr w = do_QueryInterface(aGlobal); michael@0: if (w) { michael@0: nsCOMPtr d = w->GetExtantDoc(); michael@0: if (d) { michael@0: trusted = nsContentUtils::IsChromeDoc(d); michael@0: nsIPresShell* s = d->GetShell(); michael@0: if (s) { michael@0: InitPresContextData(s->GetPresContext()); michael@0: } michael@0: } michael@0: } michael@0: return trusted; michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: Event::Constructor(const GlobalObject& aGlobal, michael@0: const nsAString& aType, michael@0: const EventInit& aParam, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr t = do_QueryInterface(aGlobal.GetAsSupports()); michael@0: nsRefPtr e = new Event(t, nullptr, nullptr); michael@0: bool trusted = e->Init(t); michael@0: aRv = e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable); michael@0: e->SetTrusted(trusted); michael@0: return e.forget(); michael@0: } michael@0: michael@0: uint16_t michael@0: Event::EventPhase() const michael@0: { michael@0: // Note, remember to check that this works also michael@0: // if or when Bug 235441 is fixed. michael@0: if ((mEvent->currentTarget && michael@0: mEvent->currentTarget == mEvent->target) || michael@0: mEvent->mFlags.InTargetPhase()) { michael@0: return nsIDOMEvent::AT_TARGET; michael@0: } michael@0: if (mEvent->mFlags.mInCapturePhase) { michael@0: return nsIDOMEvent::CAPTURING_PHASE; michael@0: } michael@0: if (mEvent->mFlags.mInBubblingPhase) { michael@0: return nsIDOMEvent::BUBBLING_PHASE; michael@0: } michael@0: return nsIDOMEvent::NONE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetEventPhase(uint16_t* aEventPhase) michael@0: { michael@0: *aEventPhase = EventPhase(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetBubbles(bool* aBubbles) michael@0: { michael@0: *aBubbles = Bubbles(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetCancelable(bool* aCancelable) michael@0: { michael@0: *aCancelable = Cancelable(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetTimeStamp(uint64_t* aTimeStamp) michael@0: { michael@0: *aTimeStamp = TimeStamp(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::StopPropagation() michael@0: { michael@0: mEvent->mFlags.mPropagationStopped = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::StopImmediatePropagation() michael@0: { michael@0: mEvent->mFlags.mPropagationStopped = true; michael@0: mEvent->mFlags.mImmediatePropagationStopped = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetIsTrusted(bool* aIsTrusted) michael@0: { michael@0: *aIsTrusted = IsTrusted(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::PreventDefault() michael@0: { michael@0: // This method is called only from C++ code which must handle default action michael@0: // of this event. So, pass true always. michael@0: PreventDefaultInternal(true); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: Event::PreventDefault(JSContext* aCx) michael@0: { michael@0: MOZ_ASSERT(aCx, "JS context must be specified"); michael@0: michael@0: // Note that at handling default action, another event may be dispatched. michael@0: // Then, JS in content mey be call preventDefault() michael@0: // even in the event is in system event group. Therefore, don't refer michael@0: // mInSystemGroup here. michael@0: PreventDefaultInternal(IsChrome(aCx)); michael@0: } michael@0: michael@0: void michael@0: Event::PreventDefaultInternal(bool aCalledByDefaultHandler) michael@0: { michael@0: if (!mEvent->mFlags.mCancelable) { michael@0: return; michael@0: } michael@0: michael@0: mEvent->mFlags.mDefaultPrevented = true; michael@0: michael@0: // Note that even if preventDefault() has already been called by chrome, michael@0: // a call of preventDefault() by content needs to overwrite michael@0: // mDefaultPreventedByContent to true because in such case, defaultPrevented michael@0: // must be true when web apps check it after they call preventDefault(). michael@0: if (!aCalledByDefaultHandler) { michael@0: mEvent->mFlags.mDefaultPreventedByContent = true; michael@0: } michael@0: michael@0: if (!IsTrusted()) { michael@0: return; michael@0: } michael@0: michael@0: WidgetDragEvent* dragEvent = mEvent->AsDragEvent(); michael@0: if (!dragEvent) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr node = do_QueryInterface(mEvent->currentTarget); michael@0: if (!node) { michael@0: nsCOMPtr win = do_QueryInterface(mEvent->currentTarget); michael@0: if (!win) { michael@0: return; michael@0: } michael@0: node = win->GetExtantDoc(); michael@0: } michael@0: if (!nsContentUtils::IsChromeDoc(node->OwnerDoc())) { michael@0: dragEvent->mDefaultPreventedOnContent = true; michael@0: } michael@0: } michael@0: michael@0: void michael@0: Event::SetEventType(const nsAString& aEventTypeArg) michael@0: { michael@0: if (mIsMainThreadEvent) { michael@0: mEvent->userType = michael@0: nsContentUtils::GetEventIdAndAtom(aEventTypeArg, mEvent->eventStructType, michael@0: &(mEvent->message)); michael@0: } else { michael@0: mEvent->userType = nullptr; michael@0: mEvent->message = NS_USER_DEFINED_EVENT; michael@0: mEvent->typeString = aEventTypeArg; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::InitEvent(const nsAString& aEventTypeArg, michael@0: bool aCanBubbleArg, michael@0: bool aCancelableArg) michael@0: { michael@0: // Make sure this event isn't already being dispatched. michael@0: NS_ENSURE_TRUE(!mEvent->mFlags.mIsBeingDispatched, NS_OK); michael@0: michael@0: if (IsTrusted()) { michael@0: // Ensure the caller is permitted to dispatch trusted DOM events. michael@0: if (!nsContentUtils::ThreadsafeIsCallerChrome()) { michael@0: SetTrusted(false); michael@0: } michael@0: } michael@0: michael@0: SetEventType(aEventTypeArg); michael@0: michael@0: mEvent->mFlags.mBubbles = aCanBubbleArg; michael@0: mEvent->mFlags.mCancelable = aCancelableArg; michael@0: michael@0: mEvent->mFlags.mDefaultPrevented = false; michael@0: michael@0: // Clearing the old targets, so that the event is targeted correctly when michael@0: // re-dispatching it. michael@0: mEvent->target = nullptr; michael@0: mEvent->originalTarget = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::DuplicatePrivateData() michael@0: { michael@0: NS_ASSERTION(mEvent, "No WidgetEvent for Event duplication!"); michael@0: if (mEventIsInternal) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: mEvent = mEvent->Duplicate(); michael@0: mPresContext = nullptr; michael@0: mEventIsInternal = true; michael@0: mPrivateDataDuplicated = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::SetTarget(nsIDOMEventTarget* aTarget) michael@0: { michael@0: #ifdef DEBUG michael@0: { michael@0: nsCOMPtr win = do_QueryInterface(aTarget); michael@0: michael@0: NS_ASSERTION(!win || !win->IsInnerWindow(), michael@0: "Uh, inner window set as event target!"); michael@0: } michael@0: #endif michael@0: michael@0: mEvent->target = do_QueryInterface(aTarget); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: Event::IsDispatchStopped() michael@0: { michael@0: return mEvent->mFlags.mPropagationStopped; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(WidgetEvent*) michael@0: Event::GetInternalNSEvent() michael@0: { michael@0: return mEvent; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(Event*) michael@0: Event::InternalDOMEvent() michael@0: { michael@0: return this; michael@0: } michael@0: michael@0: // return true if eventName is contained within events, delimited by michael@0: // spaces michael@0: static bool michael@0: PopupAllowedForEvent(const char *eventName) michael@0: { michael@0: if (!sPopupAllowedEvents) { michael@0: Event::PopupAllowedEventsChanged(); michael@0: michael@0: if (!sPopupAllowedEvents) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: nsDependentCString events(sPopupAllowedEvents); michael@0: michael@0: nsAFlatCString::const_iterator start, end; michael@0: nsAFlatCString::const_iterator startiter(events.BeginReading(start)); michael@0: events.EndReading(end); michael@0: michael@0: while (startiter != end) { michael@0: nsAFlatCString::const_iterator enditer(end); michael@0: michael@0: if (!FindInReadable(nsDependentCString(eventName), startiter, enditer)) michael@0: return false; michael@0: michael@0: // the match is surrounded by spaces, or at a string boundary michael@0: if ((startiter == start || *--startiter == ' ') && michael@0: (enditer == end || *enditer == ' ')) { michael@0: return true; michael@0: } michael@0: michael@0: // Move on and see if there are other matches. (The delimitation michael@0: // requirement makes it pointless to begin the next search before michael@0: // the end of the invalid match just found.) michael@0: startiter = enditer; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // static michael@0: PopupControlState michael@0: Event::GetEventPopupControlState(WidgetEvent* aEvent) michael@0: { michael@0: // generally if an event handler is running, new windows are disallowed. michael@0: // check for exceptions: michael@0: PopupControlState abuse = openAbused; michael@0: michael@0: switch(aEvent->eventStructType) { michael@0: case NS_EVENT : michael@0: // For these following events only allow popups if they're michael@0: // triggered while handling user input. See michael@0: // nsPresShell::HandleEventInternal() for details. michael@0: if (EventStateManager::IsHandlingUserInput()) { michael@0: switch(aEvent->message) { michael@0: case NS_FORM_SELECTED : michael@0: if (PopupAllowedForEvent("select")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: case NS_FORM_CHANGE : michael@0: if (PopupAllowedForEvent("change")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: case NS_EDITOR_INPUT_EVENT : michael@0: // For this following event only allow popups if it's triggered michael@0: // while handling user input. See michael@0: // nsPresShell::HandleEventInternal() for details. michael@0: if (EventStateManager::IsHandlingUserInput()) { michael@0: switch(aEvent->message) { michael@0: case NS_EDITOR_INPUT: michael@0: if (PopupAllowedForEvent("input")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: case NS_INPUT_EVENT : michael@0: // For this following event only allow popups if it's triggered michael@0: // while handling user input. See michael@0: // nsPresShell::HandleEventInternal() for details. michael@0: if (EventStateManager::IsHandlingUserInput()) { michael@0: switch(aEvent->message) { michael@0: case NS_FORM_CHANGE : michael@0: if (PopupAllowedForEvent("change")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: case NS_XUL_COMMAND: michael@0: abuse = openControlled; michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: case NS_KEY_EVENT : michael@0: if (aEvent->mFlags.mIsTrusted) { michael@0: uint32_t key = aEvent->AsKeyboardEvent()->keyCode; michael@0: switch(aEvent->message) { michael@0: case NS_KEY_PRESS : michael@0: // return key on focused button. see note at NS_MOUSE_CLICK. michael@0: if (key == nsIDOMKeyEvent::DOM_VK_RETURN) { michael@0: abuse = openAllowed; michael@0: } else if (PopupAllowedForEvent("keypress")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: case NS_KEY_UP : michael@0: // space key on focused button. see note at NS_MOUSE_CLICK. michael@0: if (key == nsIDOMKeyEvent::DOM_VK_SPACE) { michael@0: abuse = openAllowed; michael@0: } else if (PopupAllowedForEvent("keyup")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: case NS_KEY_DOWN : michael@0: if (PopupAllowedForEvent("keydown")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: case NS_TOUCH_EVENT : michael@0: if (aEvent->mFlags.mIsTrusted) { michael@0: switch (aEvent->message) { michael@0: case NS_TOUCH_START : michael@0: if (PopupAllowedForEvent("touchstart")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: case NS_TOUCH_END : michael@0: if (PopupAllowedForEvent("touchend")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: case NS_MOUSE_EVENT : michael@0: if (aEvent->mFlags.mIsTrusted && michael@0: aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { michael@0: switch(aEvent->message) { michael@0: case NS_MOUSE_BUTTON_UP : michael@0: if (PopupAllowedForEvent("mouseup")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: case NS_MOUSE_BUTTON_DOWN : michael@0: if (PopupAllowedForEvent("mousedown")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: case NS_MOUSE_CLICK : michael@0: /* Click events get special treatment because of their michael@0: historical status as a more legitimate event handler. If michael@0: click popups are enabled in the prefs, clear the popup michael@0: status completely. */ michael@0: if (PopupAllowedForEvent("click")) { michael@0: abuse = openAllowed; michael@0: } michael@0: break; michael@0: case NS_MOUSE_DOUBLECLICK : michael@0: if (PopupAllowedForEvent("dblclick")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: case NS_FORM_EVENT : michael@0: // For these following events only allow popups if they're michael@0: // triggered while handling user input. See michael@0: // nsPresShell::HandleEventInternal() for details. michael@0: if (EventStateManager::IsHandlingUserInput()) { michael@0: switch(aEvent->message) { michael@0: case NS_FORM_SUBMIT : michael@0: if (PopupAllowedForEvent("submit")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: case NS_FORM_RESET : michael@0: if (PopupAllowedForEvent("reset")) { michael@0: abuse = openControlled; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: return abuse; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: Event::PopupAllowedEventsChanged() michael@0: { michael@0: if (sPopupAllowedEvents) { michael@0: nsMemory::Free(sPopupAllowedEvents); michael@0: } michael@0: michael@0: nsAdoptingCString str = Preferences::GetCString("dom.popup_allowed_events"); michael@0: michael@0: // We'll want to do this even if str is empty to avoid looking up michael@0: // this pref all the time if it's not set. michael@0: sPopupAllowedEvents = ToNewCString(str); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: Event::Shutdown() michael@0: { michael@0: if (sPopupAllowedEvents) { michael@0: nsMemory::Free(sPopupAllowedEvents); michael@0: } michael@0: } michael@0: michael@0: nsIntPoint michael@0: Event::GetScreenCoords(nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: LayoutDeviceIntPoint aPoint) michael@0: { michael@0: if (!nsContentUtils::IsCallerChrome()) { michael@0: // For non-chrome callers, return client coordinates instead. michael@0: // For some events, the result will be zero; specifically, for dragend michael@0: // events (there is no widget associated with dragend events, which michael@0: // causes GetClientX() to return zero). Since dragend is for the drag michael@0: // originator and not for the receiver, it is probably not widely used michael@0: // (receivers get a drop event). Therefore, returning 0 should not break michael@0: // many web pages. Also, a few years ago Firefox returned 0. michael@0: // See: https://bugzilla.mozilla.org/show_bug.cgi?id=466379 michael@0: CSSIntPoint clientCoords = GetClientCoords(aPresContext, aEvent, aPoint, CSSIntPoint(0, 0)); michael@0: return nsIntPoint(clientCoords.x, clientCoords.y); michael@0: } michael@0: michael@0: if (EventStateManager::sIsPointerLocked) { michael@0: return EventStateManager::sLastScreenPoint; michael@0: } michael@0: michael@0: if (!aEvent || michael@0: (aEvent->eventStructType != NS_MOUSE_EVENT && michael@0: aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && michael@0: aEvent->eventStructType != NS_WHEEL_EVENT && michael@0: aEvent->eventStructType != NS_POINTER_EVENT && michael@0: aEvent->eventStructType != NS_TOUCH_EVENT && michael@0: aEvent->eventStructType != NS_DRAG_EVENT && michael@0: aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT)) { michael@0: return nsIntPoint(0, 0); michael@0: } michael@0: michael@0: WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent(); michael@0: if (!guiEvent->widget) { michael@0: return LayoutDeviceIntPoint::ToUntyped(aPoint); michael@0: } michael@0: michael@0: LayoutDeviceIntPoint offset = aPoint + michael@0: LayoutDeviceIntPoint::FromUntyped(guiEvent->widget->WidgetToScreenOffset()); michael@0: nscoord factor = aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel(); michael@0: return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(offset.x * factor), michael@0: nsPresContext::AppUnitsToIntCSSPixels(offset.y * factor)); michael@0: } michael@0: michael@0: // static michael@0: CSSIntPoint michael@0: Event::GetPageCoords(nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: LayoutDeviceIntPoint aPoint, michael@0: CSSIntPoint aDefaultPoint) michael@0: { michael@0: CSSIntPoint pagePoint = michael@0: Event::GetClientCoords(aPresContext, aEvent, aPoint, aDefaultPoint); michael@0: michael@0: // If there is some scrolling, add scroll info to client point. michael@0: if (aPresContext && aPresContext->GetPresShell()) { michael@0: nsIPresShell* shell = aPresContext->GetPresShell(); michael@0: nsIScrollableFrame* scrollframe = shell->GetRootScrollFrameAsScrollable(); michael@0: if (scrollframe) { michael@0: pagePoint += CSSIntPoint::FromAppUnitsRounded(scrollframe->GetScrollPosition()); michael@0: } michael@0: } michael@0: michael@0: return pagePoint; michael@0: } michael@0: michael@0: // static michael@0: CSSIntPoint michael@0: Event::GetClientCoords(nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: LayoutDeviceIntPoint aPoint, michael@0: CSSIntPoint aDefaultPoint) michael@0: { michael@0: if (EventStateManager::sIsPointerLocked) { michael@0: return EventStateManager::sLastClientPoint; michael@0: } michael@0: michael@0: if (!aEvent || michael@0: (aEvent->eventStructType != NS_MOUSE_EVENT && michael@0: aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && michael@0: aEvent->eventStructType != NS_WHEEL_EVENT && michael@0: aEvent->eventStructType != NS_TOUCH_EVENT && michael@0: aEvent->eventStructType != NS_DRAG_EVENT && michael@0: aEvent->eventStructType != NS_POINTER_EVENT && michael@0: aEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) || michael@0: !aPresContext || michael@0: !aEvent->AsGUIEvent()->widget) { michael@0: return aDefaultPoint; michael@0: } michael@0: michael@0: nsIPresShell* shell = aPresContext->GetPresShell(); michael@0: if (!shell) { michael@0: return CSSIntPoint(0, 0); michael@0: } michael@0: michael@0: nsIFrame* rootFrame = shell->GetRootFrame(); michael@0: if (!rootFrame) { michael@0: return CSSIntPoint(0, 0); michael@0: } michael@0: nsPoint pt = michael@0: nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, michael@0: LayoutDeviceIntPoint::ToUntyped(aPoint), rootFrame); michael@0: michael@0: return CSSIntPoint::FromAppUnitsRounded(pt); michael@0: } michael@0: michael@0: // To be called ONLY by Event::GetType (which has the additional michael@0: // logic for handling user-defined events). michael@0: // static michael@0: const char* michael@0: Event::GetEventName(uint32_t aEventType) michael@0: { michael@0: switch(aEventType) { michael@0: #define ID_TO_EVENT(name_, _id, _type, _struct) \ michael@0: case _id: return #name_; michael@0: #include "mozilla/EventNameList.h" michael@0: #undef ID_TO_EVENT michael@0: default: michael@0: break; michael@0: } michael@0: // XXXldb We can hit this case for WidgetEvent objects that we didn't michael@0: // create and that are not user defined events since this function and michael@0: // SetEventType are incomplete. (But fixing that requires fixing the michael@0: // arrays in nsEventListenerManager too, since the events for which michael@0: // this is a problem generally *are* created by Event.) michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: Event::DefaultPrevented(JSContext* aCx) const michael@0: { michael@0: MOZ_ASSERT(aCx, "JS context must be specified"); michael@0: michael@0: NS_ENSURE_TRUE(mEvent, false); michael@0: michael@0: // If preventDefault() has never been called, just return false. michael@0: if (!mEvent->mFlags.mDefaultPrevented) { michael@0: return false; michael@0: } michael@0: michael@0: // If preventDefault() has been called by content, return true. Otherwise, michael@0: // i.e., preventDefault() has been called by chrome, return true only when michael@0: // this is called by chrome. michael@0: return mEvent->mFlags.mDefaultPreventedByContent || IsChrome(aCx); michael@0: } michael@0: michael@0: bool michael@0: Event::GetPreventDefault() const michael@0: { michael@0: if (mOwner) { michael@0: if (nsIDocument* doc = mOwner->GetExtantDoc()) { michael@0: doc->WarnOnceAbout(nsIDocument::eGetPreventDefault); michael@0: } michael@0: } michael@0: // GetPreventDefault() is legacy and Gecko specific method. Although, michael@0: // the result should be same as defaultPrevented, we don't need to break michael@0: // backward compatibility of legacy method. Let's behave traditionally. michael@0: return DefaultPrevented(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetPreventDefault(bool* aReturn) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aReturn); michael@0: *aReturn = GetPreventDefault(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Event::GetDefaultPrevented(bool* aReturn) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aReturn); michael@0: // This method must be called by only event handlers implemented by C++. michael@0: // Then, the handlers must handle default action. So, this method don't need michael@0: // to check if preventDefault() has been called by content or chrome. michael@0: *aReturn = DefaultPrevented(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: Event::Serialize(IPC::Message* aMsg, bool aSerializeInterfaceType) michael@0: { michael@0: if (aSerializeInterfaceType) { michael@0: IPC::WriteParam(aMsg, NS_LITERAL_STRING("event")); michael@0: } michael@0: michael@0: nsString type; michael@0: GetType(type); michael@0: IPC::WriteParam(aMsg, type); michael@0: michael@0: IPC::WriteParam(aMsg, Bubbles()); michael@0: IPC::WriteParam(aMsg, Cancelable()); michael@0: IPC::WriteParam(aMsg, IsTrusted()); michael@0: michael@0: // No timestamp serialization for now! michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: Event::Deserialize(const IPC::Message* aMsg, void** aIter) michael@0: { michael@0: nsString type; michael@0: NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &type), false); michael@0: michael@0: bool bubbles = false; michael@0: NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &bubbles), false); michael@0: michael@0: bool cancelable = false; michael@0: NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &cancelable), false); michael@0: michael@0: bool trusted = false; michael@0: NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &trusted), false); michael@0: michael@0: nsresult rv = InitEvent(type, bubbles, cancelable); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: SetTrusted(trusted); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: Event::SetOwner(mozilla::dom::EventTarget* aOwner) michael@0: { michael@0: mOwner = nullptr; michael@0: michael@0: if (!aOwner) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr n = do_QueryInterface(aOwner); michael@0: if (n) { michael@0: mOwner = do_QueryInterface(n->OwnerDoc()->GetScopeObject()); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr w = do_QueryInterface(aOwner); michael@0: if (w) { michael@0: if (w->IsOuterWindow()) { michael@0: mOwner = w->GetCurrentInnerWindow(); michael@0: } else { michael@0: mOwner.swap(w); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr eth = do_QueryInterface(aOwner); michael@0: if (eth) { michael@0: mOwner = eth->GetOwner(); michael@0: return; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: nsCOMPtr root = do_QueryInterface(aOwner); michael@0: MOZ_ASSERT(root, "Unexpected EventTarget!"); michael@0: #endif michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: nsresult michael@0: NS_NewDOMEvent(nsIDOMEvent** aInstancePtrResult, michael@0: EventTarget* aOwner, michael@0: nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent) michael@0: { michael@0: Event* it = new Event(aOwner, aPresContext, aEvent); michael@0: NS_ADDREF(it); michael@0: *aInstancePtrResult = static_cast(it); michael@0: return NS_OK; michael@0: }