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 "base/basictypes.h" michael@0: #include "ipc/IPCMessageUtils.h" michael@0: #include "mozilla/dom/UIEvent.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/ContentEvents.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "mozilla/TextEvents.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIFrame.h" michael@0: #include "prtime.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: UIEvent::UIEvent(EventTarget* aOwner, michael@0: nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent) michael@0: : Event(aOwner, aPresContext, michael@0: aEvent ? aEvent : new InternalUIEvent(false, 0)) michael@0: , mClientPoint(0, 0) michael@0: , mLayerPoint(0, 0) michael@0: , mPagePoint(0, 0) michael@0: , mMovementPoint(0, 0) michael@0: , mIsPointerLocked(EventStateManager::sIsPointerLocked) michael@0: , mLastClientPoint(EventStateManager::sLastClientPoint) michael@0: { michael@0: if (aEvent) { michael@0: mEventIsInternal = false; michael@0: } michael@0: else { michael@0: mEventIsInternal = true; michael@0: mEvent->time = PR_Now(); michael@0: } michael@0: michael@0: // Fill mDetail and mView according to the mEvent (widget-generated michael@0: // event) we've got michael@0: switch(mEvent->eventStructType) michael@0: { michael@0: case NS_UI_EVENT: michael@0: { michael@0: mDetail = mEvent->AsUIEvent()->detail; michael@0: break; michael@0: } michael@0: michael@0: case NS_SCROLLPORT_EVENT: michael@0: { michael@0: InternalScrollPortEvent* scrollEvent = mEvent->AsScrollPortEvent(); michael@0: mDetail = (int32_t)scrollEvent->orient; michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: mDetail = 0; michael@0: break; michael@0: } michael@0: michael@0: mView = nullptr; michael@0: if (mPresContext) michael@0: { michael@0: nsISupports* container = mPresContext->GetContainerWeak(); michael@0: if (container) michael@0: { michael@0: nsCOMPtr window = do_GetInterface(container); michael@0: if (window) michael@0: mView = do_QueryInterface(window); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: UIEvent::Constructor(const GlobalObject& aGlobal, michael@0: const nsAString& aType, michael@0: const UIEventInit& aParam, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr t = do_QueryInterface(aGlobal.GetAsSupports()); michael@0: nsRefPtr e = new UIEvent(t, nullptr, nullptr); michael@0: bool trusted = e->Init(t); michael@0: aRv = e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, michael@0: aParam.mDetail); michael@0: e->SetTrusted(trusted); michael@0: return e.forget(); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent, Event, michael@0: mView) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(UIEvent, Event) michael@0: NS_IMPL_RELEASE_INHERITED(UIEvent, Event) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(UIEvent) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMUIEvent) michael@0: NS_INTERFACE_MAP_END_INHERITING(Event) michael@0: michael@0: static nsIntPoint michael@0: DevPixelsToCSSPixels(const LayoutDeviceIntPoint& aPoint, michael@0: nsPresContext* aContext) michael@0: { michael@0: return nsIntPoint(aContext->DevPixelsToIntCSSPixels(aPoint.x), michael@0: aContext->DevPixelsToIntCSSPixels(aPoint.y)); michael@0: } michael@0: michael@0: nsIntPoint michael@0: UIEvent::GetMovementPoint() michael@0: { michael@0: if (mPrivateDataDuplicated) { michael@0: return mMovementPoint; michael@0: } michael@0: michael@0: if (!mEvent || michael@0: (mEvent->eventStructType != NS_MOUSE_EVENT && michael@0: mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && michael@0: mEvent->eventStructType != NS_WHEEL_EVENT && michael@0: mEvent->eventStructType != NS_DRAG_EVENT && michael@0: mEvent->eventStructType != NS_POINTER_EVENT && michael@0: mEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) || michael@0: !mEvent->AsGUIEvent()->widget) { michael@0: return nsIntPoint(0, 0); michael@0: } michael@0: michael@0: // Calculate the delta between the last screen point and the current one. michael@0: nsIntPoint current = DevPixelsToCSSPixels(mEvent->refPoint, mPresContext); michael@0: nsIntPoint last = DevPixelsToCSSPixels(mEvent->lastRefPoint, mPresContext); michael@0: return current - last; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetView(nsIDOMWindow** aView) michael@0: { michael@0: *aView = mView; michael@0: NS_IF_ADDREF(*aView); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetDetail(int32_t* aDetail) michael@0: { michael@0: *aDetail = mDetail; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::InitUIEvent(const nsAString& typeArg, michael@0: bool canBubbleArg, michael@0: bool cancelableArg, michael@0: nsIDOMWindow* viewArg, michael@0: int32_t detailArg) michael@0: { michael@0: if (viewArg) { michael@0: nsCOMPtr view = do_QueryInterface(viewArg); michael@0: NS_ENSURE_TRUE(view, NS_ERROR_INVALID_ARG); michael@0: } michael@0: nsresult rv = Event::InitEvent(typeArg, canBubbleArg, cancelableArg); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mDetail = detailArg; michael@0: mView = viewArg; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetPageX(int32_t* aPageX) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPageX); michael@0: *aPageX = PageX(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t michael@0: UIEvent::PageX() const michael@0: { michael@0: if (mPrivateDataDuplicated) { michael@0: return mPagePoint.x; michael@0: } michael@0: michael@0: return Event::GetPageCoords(mPresContext, mEvent, mEvent->refPoint, michael@0: mClientPoint).x; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetPageY(int32_t* aPageY) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPageY); michael@0: *aPageY = PageY(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t michael@0: UIEvent::PageY() const michael@0: { michael@0: if (mPrivateDataDuplicated) { michael@0: return mPagePoint.y; michael@0: } michael@0: michael@0: return Event::GetPageCoords(mPresContext, mEvent, mEvent->refPoint, michael@0: mClientPoint).y; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetWhich(uint32_t* aWhich) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aWhich); michael@0: *aWhich = Which(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: UIEvent::GetRangeParent() michael@0: { michael@0: nsIFrame* targetFrame = nullptr; michael@0: michael@0: if (mPresContext) { michael@0: targetFrame = mPresContext->EventStateManager()->GetEventTarget(); michael@0: } michael@0: michael@0: if (targetFrame) { michael@0: nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, michael@0: targetFrame); michael@0: nsCOMPtr parent = targetFrame->GetContentOffsetsFromPoint(pt).content; michael@0: if (parent) { michael@0: if (parent->ChromeOnlyAccess() && michael@0: !nsContentUtils::CanAccessNativeAnon()) { michael@0: return nullptr; michael@0: } michael@0: return parent.forget(); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetRangeParent(nsIDOMNode** aRangeParent) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRangeParent); michael@0: *aRangeParent = nullptr; michael@0: nsCOMPtr n = GetRangeParent(); michael@0: if (n) { michael@0: CallQueryInterface(n, aRangeParent); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetRangeOffset(int32_t* aRangeOffset) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRangeOffset); michael@0: *aRangeOffset = RangeOffset(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t michael@0: UIEvent::RangeOffset() const michael@0: { michael@0: if (!mPresContext) { michael@0: return 0; michael@0: } michael@0: michael@0: nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); michael@0: if (!targetFrame) { michael@0: return 0; michael@0: } michael@0: michael@0: nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, michael@0: targetFrame); michael@0: return targetFrame->GetContentOffsetsFromPoint(pt).offset; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetCancelBubble(bool* aCancelBubble) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCancelBubble); michael@0: *aCancelBubble = CancelBubble(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::SetCancelBubble(bool aCancelBubble) michael@0: { michael@0: mEvent->mFlags.mPropagationStopped = aCancelBubble; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIntPoint michael@0: UIEvent::GetLayerPoint() const michael@0: { michael@0: if (!mEvent || michael@0: (mEvent->eventStructType != NS_MOUSE_EVENT && michael@0: mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT && michael@0: mEvent->eventStructType != NS_WHEEL_EVENT && michael@0: mEvent->eventStructType != NS_POINTER_EVENT && michael@0: mEvent->eventStructType != NS_TOUCH_EVENT && michael@0: mEvent->eventStructType != NS_DRAG_EVENT && michael@0: mEvent->eventStructType != NS_SIMPLE_GESTURE_EVENT) || michael@0: !mPresContext || michael@0: mEventIsInternal) { michael@0: return mLayerPoint; michael@0: } michael@0: // XXX I'm not really sure this is correct; it's my best shot, though michael@0: nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); michael@0: if (!targetFrame) michael@0: return mLayerPoint; michael@0: nsIFrame* layer = nsLayoutUtils::GetClosestLayer(targetFrame); michael@0: nsPoint pt(nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, layer)); michael@0: return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x), michael@0: nsPresContext::AppUnitsToIntCSSPixels(pt.y)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetLayerX(int32_t* aLayerX) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aLayerX); michael@0: *aLayerX = GetLayerPoint().x; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetLayerY(int32_t* aLayerY) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aLayerY); michael@0: *aLayerY = GetLayerPoint().y; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::GetIsChar(bool* aIsChar) michael@0: { michael@0: *aIsChar = IsChar(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: UIEvent::IsChar() const michael@0: { michael@0: WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent(); michael@0: if (keyEvent) { michael@0: return keyEvent->isChar; michael@0: } michael@0: WidgetTextEvent* textEvent = mEvent->AsTextEvent(); michael@0: return textEvent ? textEvent->isChar : false; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: UIEvent::DuplicatePrivateData() michael@0: { michael@0: mClientPoint = michael@0: Event::GetClientCoords(mPresContext, mEvent, mEvent->refPoint, michael@0: mClientPoint); michael@0: mMovementPoint = GetMovementPoint(); michael@0: mLayerPoint = GetLayerPoint(); michael@0: mPagePoint = michael@0: Event::GetPageCoords(mPresContext, mEvent, mEvent->refPoint, mClientPoint); michael@0: // GetScreenPoint converts mEvent->refPoint to right coordinates. michael@0: nsIntPoint screenPoint = michael@0: Event::GetScreenCoords(mPresContext, mEvent, mEvent->refPoint); michael@0: nsresult rv = Event::DuplicatePrivateData(); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(screenPoint); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: UIEvent::Serialize(IPC::Message* aMsg, bool aSerializeInterfaceType) michael@0: { michael@0: if (aSerializeInterfaceType) { michael@0: IPC::WriteParam(aMsg, NS_LITERAL_STRING("uievent")); michael@0: } michael@0: michael@0: Event::Serialize(aMsg, false); michael@0: michael@0: int32_t detail = 0; michael@0: GetDetail(&detail); michael@0: IPC::WriteParam(aMsg, detail); michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: UIEvent::Deserialize(const IPC::Message* aMsg, void** aIter) michael@0: { michael@0: NS_ENSURE_TRUE(Event::Deserialize(aMsg, aIter), false); michael@0: NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &mDetail), false); michael@0: return true; michael@0: } michael@0: michael@0: // XXX Following struct and array are used only in michael@0: // UIEvent::ComputeModifierState(), but if we define them in it, michael@0: // we fail to build on Mac at calling mozilla::ArrayLength(). michael@0: struct ModifierPair michael@0: { michael@0: Modifier modifier; michael@0: const char* name; michael@0: }; michael@0: static const ModifierPair kPairs[] = { michael@0: { MODIFIER_ALT, NS_DOM_KEYNAME_ALT }, michael@0: { MODIFIER_ALTGRAPH, NS_DOM_KEYNAME_ALTGRAPH }, michael@0: { MODIFIER_CAPSLOCK, NS_DOM_KEYNAME_CAPSLOCK }, michael@0: { MODIFIER_CONTROL, NS_DOM_KEYNAME_CONTROL }, michael@0: { MODIFIER_FN, NS_DOM_KEYNAME_FN }, michael@0: { MODIFIER_META, NS_DOM_KEYNAME_META }, michael@0: { MODIFIER_NUMLOCK, NS_DOM_KEYNAME_NUMLOCK }, michael@0: { MODIFIER_SCROLLLOCK, NS_DOM_KEYNAME_SCROLLLOCK }, michael@0: { MODIFIER_SHIFT, NS_DOM_KEYNAME_SHIFT }, michael@0: { MODIFIER_SYMBOLLOCK, NS_DOM_KEYNAME_SYMBOLLOCK }, michael@0: { MODIFIER_OS, NS_DOM_KEYNAME_OS } michael@0: }; michael@0: michael@0: // static michael@0: Modifiers michael@0: UIEvent::ComputeModifierState(const nsAString& aModifiersList) michael@0: { michael@0: if (aModifiersList.IsEmpty()) { michael@0: return 0; michael@0: } michael@0: michael@0: // Be careful about the performance. If aModifiersList is too long, michael@0: // parsing it needs too long time. michael@0: // XXX Should we abort if aModifiersList is too long? michael@0: michael@0: Modifiers modifiers = 0; michael@0: michael@0: nsAString::const_iterator listStart, listEnd; michael@0: aModifiersList.BeginReading(listStart); michael@0: aModifiersList.EndReading(listEnd); michael@0: michael@0: for (uint32_t i = 0; i < ArrayLength(kPairs); i++) { michael@0: nsAString::const_iterator start(listStart), end(listEnd); michael@0: if (!FindInReadable(NS_ConvertASCIItoUTF16(kPairs[i].name), start, end)) { michael@0: continue; michael@0: } michael@0: michael@0: if ((start != listStart && !NS_IsAsciiWhitespace(*(--start))) || michael@0: (end != listEnd && !NS_IsAsciiWhitespace(*(end)))) { michael@0: continue; michael@0: } michael@0: modifiers |= kPairs[i].modifier; michael@0: } michael@0: michael@0: return modifiers; michael@0: } michael@0: michael@0: bool michael@0: UIEvent::GetModifierStateInternal(const nsAString& aKey) michael@0: { michael@0: WidgetInputEvent* inputEvent = mEvent->AsInputEvent(); michael@0: MOZ_ASSERT(inputEvent, "mEvent must be WidgetInputEvent or derived class"); michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SHIFT)) { michael@0: return inputEvent->IsShift(); michael@0: } michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_CONTROL)) { michael@0: return inputEvent->IsControl(); michael@0: } michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_META)) { michael@0: return inputEvent->IsMeta(); michael@0: } michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_ALT)) { michael@0: return inputEvent->IsAlt(); michael@0: } michael@0: michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_ALTGRAPH)) { michael@0: return inputEvent->IsAltGraph(); michael@0: } michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_OS)) { michael@0: return inputEvent->IsOS(); michael@0: } michael@0: michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_CAPSLOCK)) { michael@0: return inputEvent->IsCapsLocked(); michael@0: } michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_NUMLOCK)) { michael@0: return inputEvent->IsNumLocked(); michael@0: } michael@0: michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_FN)) { michael@0: return inputEvent->IsFn(); michael@0: } michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SCROLLLOCK)) { michael@0: return inputEvent->IsScrollLocked(); michael@0: } michael@0: if (aKey.EqualsLiteral(NS_DOM_KEYNAME_SYMBOLLOCK)) { michael@0: return inputEvent->IsSymbolLocked(); michael@0: } michael@0: return false; 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_NewDOMUIEvent(nsIDOMEvent** aInstancePtrResult, michael@0: EventTarget* aOwner, michael@0: nsPresContext* aPresContext, michael@0: WidgetGUIEvent* aEvent) michael@0: { michael@0: UIEvent* it = new UIEvent(aOwner, aPresContext, aEvent); michael@0: NS_ADDREF(it); michael@0: *aInstancePtrResult = static_cast(it); michael@0: return NS_OK; michael@0: }