From 479713d218180cc2bb53b994a43191d66c8acb20 Mon Sep 17 00:00:00 2001 From: "tdyas@zecador.org" Date: Thu, 23 Oct 2008 23:15:20 +0300 Subject: [PATCH] Bug 412486 - widget/event-detection support for multi-touch trackpad gestures, r=smaug,josh, sr=roc --- content/base/src/nsContentUtils.cpp | 11 +- content/base/src/nsGkAtomList.h | 10 + content/events/public/nsIPrivateDOMEvent.h | 2 + content/events/src/Makefile.in | 1 + content/events/src/nsDOMEvent.cpp | 55 ++++- content/events/src/nsDOMEvent.h | 9 +- .../events/src/nsDOMSimpleGestureEvent.cpp | 153 +++++++++++++ content/events/src/nsDOMSimpleGestureEvent.h | 60 +++++ content/events/src/nsEventDispatcher.cpp | 5 + dom/public/idl/base/nsIDOMWindowUtils.idl | 22 +- dom/public/idl/events/Makefile.in | 1 + .../idl/events/nsIDOMSimpleGestureEvent.idl | 141 ++++++++++++ dom/public/nsDOMClassInfoID.h | 2 + dom/src/base/nsDOMClassInfo.cpp | 11 + dom/src/base/nsDOMWindowUtils.cpp | 45 ++++ widget/public/nsEvent.h | 1 + widget/public/nsGUIEvent.h | 29 +++ widget/src/cocoa/nsChildView.h | 40 ++++ widget/src/cocoa/nsChildView.mm | 215 ++++++++++++++++++ 19 files changed, 809 insertions(+), 4 deletions(-) create mode 100644 content/events/src/nsDOMSimpleGestureEvent.cpp create mode 100644 content/events/src/nsDOMSimpleGestureEvent.h create mode 100644 dom/public/idl/events/nsIDOMSimpleGestureEvent.idl diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 8c6a42eb69d..ecd6b8c212d 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -479,7 +479,16 @@ nsContentUtils::InitializeEventTable() { { &nsGkAtoms::ondurationchange, { NS_DURATIONCHANGE, EventNameType_HTML }}, { &nsGkAtoms::onvolumechange, { NS_VOLUMECHANGE, EventNameType_HTML }}, #endif //MOZ_MEDIA - { &nsGkAtoms::onMozAfterPaint, { NS_AFTERPAINT, EventNameType_None }} + { &nsGkAtoms::onMozAfterPaint, { NS_AFTERPAINT, EventNameType_None }}, + + // Simple gesture events + { &nsGkAtoms::onMozSwipeGesture, { NS_SIMPLE_GESTURE_SWIPE, EventNameType_None } }, + { &nsGkAtoms::onMozMagnifyGestureStart, { NS_SIMPLE_GESTURE_MAGNIFY_START, EventNameType_None } }, + { &nsGkAtoms::onMozMagnifyGestureUpdate, { NS_SIMPLE_GESTURE_MAGNIFY_UPDATE, EventNameType_None } }, + { &nsGkAtoms::onMozMagnifyGesture, { NS_SIMPLE_GESTURE_MAGNIFY, EventNameType_None } }, + { &nsGkAtoms::onMozRotateGestureStart, { NS_SIMPLE_GESTURE_ROTATE_START, EventNameType_None } }, + { &nsGkAtoms::onMozRotateGestureUpdate, { NS_SIMPLE_GESTURE_ROTATE_UPDATE, EventNameType_None } }, + { &nsGkAtoms::onMozRotateGesture, { NS_SIMPLE_GESTURE_ROTATE, EventNameType_None } } }; sEventTable = new nsDataHashtable; diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 9fbc96d2cc5..d4a2e3bc12e 100755 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -1457,6 +1457,16 @@ GK_ATOM(seconds, "seconds") GK_ATOM(secondsFromDateTime, "seconds-from-dateTime") #endif +// Simple gestures support +GK_ATOM(onMozSwipeGesture, "onMozSwipeGesture") +GK_ATOM(onMozMagnifyGestureStart, "onMozMagnifyGestureStart") +GK_ATOM(onMozMagnifyGestureUpdate, "onMozMagnifyGestureUpdate") +GK_ATOM(onMozMagnifyGesture, "onMozMagnifyGesture") +GK_ATOM(onMozRotateGestureStart, "onMozRotateGestureStart") +GK_ATOM(onMozRotateGestureUpdate, "onMozRotateGestureUpdate") +GK_ATOM(onMozRotateGesture, "onMozRotateGesture") + + //--------------------------------------------------------------------------- // Special atoms //--------------------------------------------------------------------------- diff --git a/content/events/public/nsIPrivateDOMEvent.h b/content/events/public/nsIPrivateDOMEvent.h index ea99e24c667..87b7cf1b909 100644 --- a/content/events/public/nsIPrivateDOMEvent.h +++ b/content/events/public/nsIPrivateDOMEvent.h @@ -112,4 +112,6 @@ nsresult NS_NewDOMProgressEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsEvent* aEvent); nsresult NS_NewDOMNotifyPaintEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsNotifyPaintEvent* aEvent); +nsresult +NS_NewDOMSimpleGestureEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsSimpleGestureEvent* aEvent); #endif // nsIPrivateDOMEvent_h__ diff --git a/content/events/src/Makefile.in b/content/events/src/Makefile.in index 5e65d6af81d..63f494c60d3 100644 --- a/content/events/src/Makefile.in +++ b/content/events/src/Makefile.in @@ -95,6 +95,7 @@ CPPSRCS = \ nsDOMProgressEvent.cpp \ nsDOMDataTransfer.cpp \ nsDOMNotifyPaintEvent.cpp \ + nsDOMSimpleGestureEvent.cpp \ $(NULL) # we don't want the shared lib, but we want to force the creation of a static lib. diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index e2fccefb371..74c3a0f8468 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -83,7 +83,14 @@ static const char* const sEventNames[] = { "canshowcurrentframe", "canplay", "canplaythrough", "ratechange", "durationchange", "volumechange", #endif // MOZ_MEDIA - "MozAfterPaint" + "MozAfterPaint", + "MozSwipeGesture", + "MozMagnifyGestureStart", + "MozMagnifyGestureUpdate", + "MozMagnifyGesture", + "MozRotateGestureStart", + "MozRotateGestureUpdate", + "MozRotateGesture" }; static char *sPopupAllowedEvents; @@ -650,6 +657,22 @@ nsDOMEvent::SetEventType(const nsAString& aEventTypeArg) if (atom == nsGkAtoms::onMozAfterPaint) mEvent->message = NS_AFTERPAINT; } + else if (mEvent->eventStructType == NS_SIMPLE_GESTURE_EVENT) { + if (atom == nsGkAtoms::onMozSwipeGesture) + mEvent->message = NS_SIMPLE_GESTURE_SWIPE; + else if (atom == nsGkAtoms::onMozMagnifyGestureStart) + mEvent->message = NS_SIMPLE_GESTURE_MAGNIFY_START; + else if (atom == nsGkAtoms::onMozMagnifyGestureUpdate) + mEvent->message = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE; + else if (atom == nsGkAtoms::onMozMagnifyGesture) + mEvent->message = NS_SIMPLE_GESTURE_MAGNIFY; + else if (atom == nsGkAtoms::onMozRotateGestureStart) + mEvent->message = NS_SIMPLE_GESTURE_ROTATE_START; + else if (atom == nsGkAtoms::onMozRotateGestureUpdate) + mEvent->message = NS_SIMPLE_GESTURE_ROTATE_UPDATE; + else if (atom == nsGkAtoms::onMozRotateGesture) + mEvent->message = NS_SIMPLE_GESTURE_ROTATE; + } if (mEvent->message == NS_USER_DEFINED_EVENT) mEvent->userType = atom; @@ -972,6 +995,22 @@ NS_METHOD nsDOMEvent::DuplicatePrivateData() event->sameDocRegion, event->crossDocRegion); break; } + case NS_SIMPLE_GESTURE_EVENT: + { + nsSimpleGestureEvent* oldSimpleGestureEvent = static_cast(mEvent); + nsSimpleGestureEvent* simpleGestureEvent = + new nsSimpleGestureEvent(PR_FALSE, msg, nsnull, 0, 0.0); + NS_ENSURE_TRUE(simpleGestureEvent, NS_ERROR_OUT_OF_MEMORY); + isInputEvent = PR_TRUE; + simpleGestureEvent->direction = oldSimpleGestureEvent->direction; + simpleGestureEvent->delta = oldSimpleGestureEvent->delta; + simpleGestureEvent->isAlt = oldSimpleGestureEvent->isAlt; + simpleGestureEvent->isControl = oldSimpleGestureEvent->isControl; + simpleGestureEvent->isShift = oldSimpleGestureEvent->isShift; + simpleGestureEvent->isMeta = oldSimpleGestureEvent->isMeta; + newEvent = simpleGestureEvent; + break; + } default: { NS_WARNING("Unknown event type!!!"); @@ -1471,6 +1510,20 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType) #endif case NS_AFTERPAINT: return sEventNames[eDOMEvents_afterpaint]; + case NS_SIMPLE_GESTURE_SWIPE: + return sEventNames[eDOMEvents_MozSwipeGesture]; + case NS_SIMPLE_GESTURE_MAGNIFY_START: + return sEventNames[eDOMEvents_MozMagnifyGestureStart]; + case NS_SIMPLE_GESTURE_MAGNIFY_UPDATE: + return sEventNames[eDOMEvents_MozMagnifyGestureUpdate]; + case NS_SIMPLE_GESTURE_MAGNIFY: + return sEventNames[eDOMEvents_MozMagnifyGesture]; + case NS_SIMPLE_GESTURE_ROTATE_START: + return sEventNames[eDOMEvents_MozRotateGestureStart]; + case NS_SIMPLE_GESTURE_ROTATE_UPDATE: + return sEventNames[eDOMEvents_MozRotateGestureUpdate]; + case NS_SIMPLE_GESTURE_ROTATE: + return sEventNames[eDOMEvents_MozRotateGesture]; default: break; } diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index da280990133..b66c06ee056 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -161,7 +161,14 @@ public: eDOMEvents_durationchange, eDOMEvents_volumechange, #endif - eDOMEvents_afterpaint + eDOMEvents_afterpaint, + eDOMEvents_MozSwipeGesture, + eDOMEvents_MozMagnifyGestureStart, + eDOMEvents_MozMagnifyGestureUpdate, + eDOMEvents_MozMagnifyGesture, + eDOMEvents_MozRotateGestureStart, + eDOMEvents_MozRotateGestureUpdate, + eDOMEvents_MozRotateGesture }; nsDOMEvent(nsPresContext* aPresContext, nsEvent* aEvent); diff --git a/content/events/src/nsDOMSimpleGestureEvent.cpp b/content/events/src/nsDOMSimpleGestureEvent.cpp new file mode 100644 index 00000000000..755867e8017 --- /dev/null +++ b/content/events/src/nsDOMSimpleGestureEvent.cpp @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Thomas K. Dyas . + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsDOMSimpleGestureEvent.h" +#include "nsGUIEvent.h" +#include "nsContentUtils.h" + + +nsDOMSimpleGestureEvent::nsDOMSimpleGestureEvent(nsPresContext* aPresContext, nsSimpleGestureEvent* aEvent) + : nsDOMUIEvent(aPresContext, aEvent ? aEvent : new nsSimpleGestureEvent(PR_FALSE, 0, nsnull, 0, 0.0)) +{ + NS_ASSERTION(mEvent->eventStructType == NS_SIMPLE_GESTURE_EVENT, "event type mismatch"); + + if (aEvent) { + mEventIsInternal = PR_FALSE; + } else { + mEventIsInternal = PR_TRUE; + mEvent->time = PR_Now(); + } +} + +nsDOMSimpleGestureEvent::~nsDOMSimpleGestureEvent() +{ + if (mEventIsInternal) { + delete static_cast(mEvent); + mEvent = nsnull; + } +} + +NS_IMPL_ADDREF_INHERITED(nsDOMSimpleGestureEvent, nsDOMUIEvent) +NS_IMPL_RELEASE_INHERITED(nsDOMSimpleGestureEvent, nsDOMUIEvent) + +NS_INTERFACE_MAP_BEGIN(nsDOMSimpleGestureEvent) + NS_INTERFACE_MAP_ENTRY(nsIDOMSimpleGestureEvent) + NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(SimpleGestureEvent) +NS_INTERFACE_MAP_END_INHERITING(nsDOMUIEvent) + +/* readonly attribute unsigned long direction; */ +NS_IMETHODIMP +nsDOMSimpleGestureEvent::GetDirection(PRUint32 *aDirection) +{ + NS_ENSURE_ARG_POINTER(aDirection); + *aDirection = static_cast(mEvent)->direction; + return NS_OK; +} + +/* readonly attribute float delta; */ +NS_IMETHODIMP +nsDOMSimpleGestureEvent::GetDelta(PRFloat64 *aDelta) +{ + NS_ENSURE_ARG_POINTER(aDelta); + *aDelta = static_cast(mEvent)->delta; + return NS_OK; +} + +/* readonly attribute boolean altKey; */ +NS_IMETHODIMP +nsDOMSimpleGestureEvent::GetAltKey(PRBool* aIsDown) +{ + NS_ENSURE_ARG_POINTER(aIsDown); + *aIsDown = static_cast(mEvent)->isAlt; + return NS_OK; +} + +/* readonly attribute boolean ctrlKey; */ +NS_IMETHODIMP +nsDOMSimpleGestureEvent::GetCtrlKey(PRBool* aIsDown) +{ + NS_ENSURE_ARG_POINTER(aIsDown); + *aIsDown = static_cast(mEvent)->isControl; + return NS_OK; +} + +/* readonly attribute boolean shiftKey; */ +NS_IMETHODIMP +nsDOMSimpleGestureEvent::GetShiftKey(PRBool* aIsDown) +{ + NS_ENSURE_ARG_POINTER(aIsDown); + *aIsDown = static_cast(mEvent)->isShift; + return NS_OK; +} + +/* readonly attribute boolean metaKey; */ +NS_IMETHODIMP +nsDOMSimpleGestureEvent::GetMetaKey(PRBool* aIsDown) +{ + NS_ENSURE_ARG_POINTER(aIsDown); + *aIsDown = static_cast(mEvent)->isMeta; + return NS_OK; +} + + +NS_IMETHODIMP +nsDOMSimpleGestureEvent::InitSimpleGestureEvent(const nsAString & typeArg, PRBool canBubbleArg, PRBool cancelableArg, nsIDOMAbstractView *viewArg, PRInt32 detailArg, PRUint32 directionArg, PRFloat64 deltaArg, PRBool altKeyArg, PRBool ctrlKeyArg, PRBool shiftKeyArg, PRBool metaKeyArg) +{ + nsresult rv = nsDOMUIEvent::InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg); + NS_ENSURE_SUCCESS(rv, rv); + + nsSimpleGestureEvent* simpleGestureEvent = static_cast(mEvent); + simpleGestureEvent->direction = directionArg; + simpleGestureEvent->delta = deltaArg; + simpleGestureEvent->isAlt = altKeyArg; + simpleGestureEvent->isControl = ctrlKeyArg; + simpleGestureEvent->isShift = shiftKeyArg; + simpleGestureEvent->isMeta = metaKeyArg; + + return NS_OK; +} + +nsresult NS_NewDOMSimpleGestureEvent(nsIDOMEvent** aInstancePtrResult, + nsPresContext* aPresContext, + nsSimpleGestureEvent *aEvent) +{ + nsDOMSimpleGestureEvent *it = new nsDOMSimpleGestureEvent(aPresContext, aEvent); + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + return CallQueryInterface(it, aInstancePtrResult); +} diff --git a/content/events/src/nsDOMSimpleGestureEvent.h b/content/events/src/nsDOMSimpleGestureEvent.h new file mode 100644 index 00000000000..69099dedffa --- /dev/null +++ b/content/events/src/nsDOMSimpleGestureEvent.h @@ -0,0 +1,60 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Thomas K. Dyas . + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsDOMSimpleGestureEvent_h__ +#define nsDOMSimpleGestureEvent_h__ + +#include "nsIDOMSimpleGestureEvent.h" +#include "nsDOMUIEvent.h" + +class nsPresContext; + +class nsDOMSimpleGestureEvent : public nsIDOMSimpleGestureEvent, + public nsDOMUIEvent +{ +public: + nsDOMSimpleGestureEvent(nsPresContext*, nsSimpleGestureEvent*); + virtual ~nsDOMSimpleGestureEvent(); + + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_NSIDOMSIMPLEGESTUREEVENT + + // Forward to base class + NS_FORWARD_TO_NSDOMUIEVENT +}; + +#endif diff --git a/content/events/src/nsEventDispatcher.cpp b/content/events/src/nsEventDispatcher.cpp index 1539db8f36d..e6cb7d2f8c0 100644 --- a/content/events/src/nsEventDispatcher.cpp +++ b/content/events/src/nsEventDispatcher.cpp @@ -649,6 +649,9 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext, return NS_NewDOMNotifyPaintEvent(aDOMEvent, aPresContext, static_cast (aEvent)); + case NS_SIMPLE_GESTURE_EVENT: + return NS_NewDOMSimpleGestureEvent(aDOMEvent, aPresContext, + static_cast(aEvent)); } // For all other types of events, create a vanilla event object. @@ -707,6 +710,8 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext, return NS_NewDOMProgressEvent(aDOMEvent, aPresContext, nsnull); if (aEventType.LowerCaseEqualsLiteral("notifypaintevent")) return NS_NewDOMNotifyPaintEvent(aDOMEvent, aPresContext, nsnull); + if (aEventType.LowerCaseEqualsLiteral("simplegestureevent")) + return NS_NewDOMSimpleGestureEvent(aDOMEvent, aPresContext, nsnull); return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } diff --git a/dom/public/idl/base/nsIDOMWindowUtils.idl b/dom/public/idl/base/nsIDOMWindowUtils.idl index 2136e37fd80..bb7f9bf61fb 100644 --- a/dom/public/idl/base/nsIDOMWindowUtils.idl +++ b/dom/public/idl/base/nsIDOMWindowUtils.idl @@ -47,7 +47,7 @@ interface nsIDOMElement; -[scriptable, uuid(9a1ff4e0-5e31-46af-b393-2cbab8093442)] +[scriptable, uuid(3fe733aa-823b-443a-9945-b02f973ab439)] interface nsIDOMWindowUtils : nsISupports { /** @@ -240,4 +240,24 @@ interface nsIDOMWindowUtils : nsISupports { */ void processUpdates(); + + /** Synthesize a simple gesture event for a window. The event types + * supported are: MozSwipeGesture, MozMagnifyGestureStart, + * MozMagnifyGestureUpdate, MozMagnifyGesture, + * MozRotateGestureStart, MozRotateGestureUpdate, and + * MozRotateGesture. + * + * Cannot be accessed from unprivileged context (not + * content-accessible) Will throw a DOM security error if called + * without UniversalXPConnect privileges. + * + * @param aType event type + * @param aDirection direction, using constants defined in nsIDOMSimpleGestureEvent + * @param aDelta amount of magnification or rotation for magnify and rotation events + * @param aModifiers modifiers pressed, using constants defined in nsIDOMNSEvent + */ + void sendSimpleGestureEvent(in AString aType, + in unsigned long aDirection, + in double aDelta, + in long aModifiers); }; diff --git a/dom/public/idl/events/Makefile.in b/dom/public/idl/events/Makefile.in index 7976fea4daf..48fcec57fdf 100644 --- a/dom/public/idl/events/Makefile.in +++ b/dom/public/idl/events/Makefile.in @@ -76,6 +76,7 @@ XPIDLSRCS = \ nsIDOMCommandEvent.idl \ nsIDOMMessageEvent.idl \ nsIDOMNotifyPaintEvent.idl \ + nsIDOMSimpleGestureEvent.idl \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/dom/public/idl/events/nsIDOMSimpleGestureEvent.idl b/dom/public/idl/events/nsIDOMSimpleGestureEvent.idl new file mode 100644 index 00000000000..166b9240a99 --- /dev/null +++ b/dom/public/idl/events/nsIDOMSimpleGestureEvent.idl @@ -0,0 +1,141 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Thomas K. Dyas . + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#include "nsIDOMUIEvent.idl" + +/** + * The nsIDOMSimpleGestureEvent interface is the datatype for all + * Mozila-specific simple gesture events in the Document Object Model. + * + * The following events are generated: + * + * MozSwipeGesture - Generated when the user swipes their fingers + * across the input device. + * + * MozMagnifyGestureStart - Generated when the user begins the magnify + * ("pinch") gesture. The "delta" value represents the initial + * movement. + * + * MozMagnifyGestureUpdate - Generated periodically while the user is + * continuing the magnify ("pinch") gesture. The "delta" value + * represents the movement since the last MozMagnifyGestureStart or + * MozMagnifyGestureUpdate event. + * + * MozMagnifyGesture - Generated when the user has completed the + * magnify ("pinch") gesture. If you only want to receive a single + * event when the magnify gesture is complete, you only need to hook + * this event and can safely ignore the MozMagnifyGestureStart and the + * MozMagnifyGestureUpdate events. The "delta" value is the cumulative + * amount represented by the user's gesture. + * + * MozRotateGestureStart - Generated when the user begins the rotation + * gesture. The "delta" value represents the initial rotation. + * + * MozRotateGestureUpdate - Generated periodically while the user is + * continuing the rotation gesture. The "delta" value represents the + * rotation since the last MozRotateGestureStart or + * MozRotateGestureUpdate event. + * + * MozRotateGesture - Generated when the user has completed the + * rotation gesture. If you only want to receive a single event when + * the rotation gesture is complete, you only need to hook this event + * and can safely ignore the MozRotateGestureStart and the + * MozRotateGestureUpdate events. The "delta" value is the cumulative + * amount of rotation represented by the user's gesture. + */ + +[scriptable, uuid(20AB8E74-BF9B-4D1D-8A18-B5EE0F3C0A36)] +interface nsIDOMSimpleGestureEvent : nsIDOMUIEvent +{ + /* Direction constants */ + const unsigned long DIRECTION_UP = 1; + const unsigned long DIRECTION_DOWN = 2; + const unsigned long DIRECTION_LEFT = 4; + const unsigned long DIRECTION_RIGHT = 8; + + /* Direction of a gesture. Diagonals are indicated by OR'ing the + * applicable constants together. + * + * Swipes gestures may occur in any direction. + * + * Magnify gestures do not have a direction. + * + * Rotation gestures will be either DIRECTION_LEFT or + * DIRECTION_RIGHT (i.e., counter-clockwise and clockwise). + */ + readonly attribute unsigned long direction; + + /* Delta value for magnify and rotate gestures. + * + * For rotation, the value is in degrees and is positive for + * clockwise rotation and negative for counterclockwise + * rotation. The "direction" attribute also denotes the direction of + * the rotation: clockwise is DIRECTION_RIGHT and counterclockwise + * is DIRECTION_LEFT. + * + * For magnification, the value will be positive for a "zoom in" + * (i.e, increased magnification) and negative for a "zoom out" + * (i.e., decreased magnification). The particular units + * represented by the "delta" are currently implementation specific. + * + * XXX - The units for measuring magnification are currently + * unspecified because the units used by Mac OS X are currently + * undocumented. The values are typically in the range of 0.0 to + * 100.0, but it is only safe currently to rely on the delta being + * positive or negative. + */ + readonly attribute double delta; + + /* Modifier keys that may have been pressed during the gesture. */ + readonly attribute boolean altKey; + readonly attribute boolean ctrlKey; + readonly attribute boolean shiftKey; + readonly attribute boolean metaKey; + + void initSimpleGestureEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean cancelableArg, + in nsIDOMAbstractView viewArg, + in long detailArg, + in unsigned long directionArg, + in double deltaArg, + in boolean altKeyArg, + in boolean ctrlKeyArg, + in boolean shiftKeyArg, + in boolean metaKeyArg); +}; diff --git a/dom/public/nsDOMClassInfoID.h b/dom/public/nsDOMClassInfoID.h index 2116ac8b125..89e37611352 100644 --- a/dom/public/nsDOMClassInfoID.h +++ b/dom/public/nsDOMClassInfoID.h @@ -452,6 +452,8 @@ enum nsDOMClassInfoID { eDOMClassInfo_NotifyPaintEvent_id, + eDOMClassInfo_SimpleGestureEvent_id, + // This one better be the last one in this list eDOMClassInfoIDCount }; diff --git a/dom/src/base/nsDOMClassInfo.cpp b/dom/src/base/nsDOMClassInfo.cpp index a536583ae88..7fe26285e29 100644 --- a/dom/src/base/nsDOMClassInfo.cpp +++ b/dom/src/base/nsDOMClassInfo.cpp @@ -465,6 +465,9 @@ #include "nsDOMFile.h" #include "nsIDOMFileException.h" +// Simple gestures include +#include "nsIDOMSimpleGestureEvent.h" + static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); @@ -1293,6 +1296,9 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(NotifyPaintEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) + + NS_DEFINE_CLASSINFO_DATA(SimpleGestureEvent, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) }; // Objects that shuld be constructable through |new Name();| @@ -3531,6 +3537,11 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_EVENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(SimpleGestureEvent, nsIDOMSimpleGestureEvent) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMSimpleGestureEvent) + DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES + DOM_CLASSINFO_MAP_END + #ifdef NS_DEBUG { PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData); diff --git a/dom/src/base/nsDOMWindowUtils.cpp b/dom/src/base/nsDOMWindowUtils.cpp index 992c8864cd0..4505480e194 100644 --- a/dom/src/base/nsDOMWindowUtils.cpp +++ b/dom/src/base/nsDOMWindowUtils.cpp @@ -493,3 +493,48 @@ nsDOMWindowUtils::ProcessUpdates() batch.EndUpdateViewBatch(NS_VMREFRESH_IMMEDIATE); return NS_OK; } + +NS_IMETHODIMP +nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType, + PRUint32 aDirection, + PRFloat64 aDelta, + PRInt32 aModifiers) +{ + PRBool hasCap = PR_FALSE; + if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap)) + || !hasCap) + return NS_ERROR_DOM_SECURITY_ERR; + + // get the widget to send the event to + nsCOMPtr widget = GetWidget(); + if (!widget) + return NS_ERROR_FAILURE; + + PRInt32 msg; + if (aType.EqualsLiteral("MozSwipeGesture")) + msg = NS_SIMPLE_GESTURE_SWIPE; + else if (aType.EqualsLiteral("MozMagnifyGestureStart")) + msg = NS_SIMPLE_GESTURE_MAGNIFY_START; + else if (aType.EqualsLiteral("MozMagnifyGestureUpdate")) + msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE; + else if (aType.EqualsLiteral("MozMagnifyGesture")) + msg = NS_SIMPLE_GESTURE_MAGNIFY; + else if (aType.EqualsLiteral("MozRotateGestureStart")) + msg = NS_SIMPLE_GESTURE_ROTATE_START; + else if (aType.EqualsLiteral("MozRotateGestureUpdate")) + msg = NS_SIMPLE_GESTURE_ROTATE_UPDATE; + else if (aType.EqualsLiteral("MozRotateGesture")) + msg = NS_SIMPLE_GESTURE_ROTATE; + else + return NS_ERROR_FAILURE; + + nsSimpleGestureEvent event(PR_TRUE, msg, widget, aDirection, aDelta); + event.isShift = (aModifiers & nsIDOMNSEvent::SHIFT_MASK) ? PR_TRUE : PR_FALSE; + event.isControl = (aModifiers & nsIDOMNSEvent::CONTROL_MASK) ? PR_TRUE : PR_FALSE; + event.isAlt = (aModifiers & nsIDOMNSEvent::ALT_MASK) ? PR_TRUE : PR_FALSE; + event.isMeta = (aModifiers & nsIDOMNSEvent::META_MASK) ? PR_TRUE : PR_FALSE; + event.time = PR_IntervalNow(); + + nsEventStatus status; + return widget->DispatchEvent(&event, status); +} diff --git a/widget/public/nsEvent.h b/widget/public/nsEvent.h index d5e6b89bfea..96b19c73238 100644 --- a/widget/public/nsEvent.h +++ b/widget/public/nsEvent.h @@ -76,6 +76,7 @@ class nsMouseScrollEvent; class nsReconversionEvent; class nsTooltipEvent; class nsMenuEvent; +class nsSimpleGestureEvent; struct nsTextEventReply; diff --git a/widget/public/nsGUIEvent.h b/widget/public/nsGUIEvent.h index 651932cf5a5..b1ddc3042fc 100644 --- a/widget/public/nsGUIEvent.h +++ b/widget/public/nsGUIEvent.h @@ -22,6 +22,7 @@ * Contributor(s): * Makoto Kato * Dean Tessman + * Thomas K. Dyas (simple gestures support) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -103,6 +104,7 @@ class nsHashKey; #endif // MOZ_MEDIA #define NS_DRAG_EVENT 35 #define NS_NOTIFYPAINT_EVENT 36 +#define NS_SIMPLE_GESTURE_EVENT 37 // These flags are sort of a mess. They're sort of shared between event // listener flags and event flags, but only some of them. You've been @@ -382,6 +384,16 @@ class nsHashKey; #define NS_NOTIFYPAINT_START 3400 #define NS_AFTERPAINT (NS_NOTIFYPAINT_START) +// Simple gesture events +#define NS_SIMPLE_GESTURE_EVENT_START 3500 +#define NS_SIMPLE_GESTURE_SWIPE (NS_SIMPLE_GESTURE_EVENT_START) +#define NS_SIMPLE_GESTURE_MAGNIFY_START (NS_SIMPLE_GESTURE_EVENT_START+1) +#define NS_SIMPLE_GESTURE_MAGNIFY_UPDATE (NS_SIMPLE_GESTURE_EVENT_START+2) +#define NS_SIMPLE_GESTURE_MAGNIFY (NS_SIMPLE_GESTURE_EVENT_START+3) +#define NS_SIMPLE_GESTURE_ROTATE_START (NS_SIMPLE_GESTURE_EVENT_START+4) +#define NS_SIMPLE_GESTURE_ROTATE_UPDATE (NS_SIMPLE_GESTURE_EVENT_START+5) +#define NS_SIMPLE_GESTURE_ROTATE (NS_SIMPLE_GESTURE_EVENT_START+6) + /** * Return status for event processors, nsEventStatus, is defined in * nsEvent.h. @@ -1120,6 +1132,23 @@ public: nsCOMPtr sourceEvent; }; +/** + * Simple gesture event + */ +class nsSimpleGestureEvent : public nsInputEvent +{ +public: + nsSimpleGestureEvent(PRBool isTrusted, PRUint32 msg, nsIWidget* w, + PRUint32 directionArg, PRFloat64 deltaArg) + : nsInputEvent(isTrusted, msg, w, NS_SIMPLE_GESTURE_EVENT), + direction(directionArg), delta(deltaArg) + { + } + + PRUint32 direction; // See nsIDOMSimpleGestureEvent for values + PRFloat64 delta; // Delta for magnify and rotate events +}; + /** * Event status for D&D Event */ diff --git a/widget/src/cocoa/nsChildView.h b/widget/src/cocoa/nsChildView.h index 37375329c48..1c2a526deb8 100644 --- a/widget/src/cocoa/nsChildView.h +++ b/widget/src/cocoa/nsChildView.h @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Thomas K. Dyas (simple gestures support) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -182,6 +183,29 @@ enum { // Cocoa TSM documents (those created and managed by the NSTSMInputContext // class) -- for some reason TSMProcessRawKeyEvent() doesn't work with them. TSMDocumentID mPluginTSMDoc; + + // Simple gestures support + // + // mGestureState is used to detect when Cocoa has called both + // magnifyWithEvent and rotateWithEvent within the same + // beginGestureWithEvent and endGestureWithEvent sequence. We + // discard the spurious gesture event so as not to confuse Gecko. + // + // mCumulativeMagnification keeps track of the total amount of + // magnification peformed during a magnify gesture so that we can + // send that value with the final MozMagnifyGesture event. + // + // mCumulativeRotation keeps track of the total amount of rotation + // performed during a rotate gesture so we can send that value with + // the final MozRotateGesture event. + enum { + eGestureState_None, + eGestureState_StartGesture, + eGestureState_MagnifyGesture, + eGestureState_RotateGesture + } mGestureState; + float mCumulativeMagnification; + float mCumulativeRotation; } // these are sent to the first responder when the window key status changes @@ -196,6 +220,22 @@ enum { - (void)sendFocusEvent:(PRUint32)eventType; - (void) processPluginKeyEvent:(EventRef)aKeyEvent; + +// Simple gestures support +// +// XXX - The swipeWithEvent, beginGestureWithEvent, magnifyWithEvent, +// rotateWithEvent, and endGestureWithEvent methods are part of a +// PRIVATE interface exported by nsResponder and reverse-engineering +// was necessary to obtain the methods' prototypes. Thus, Apple may +// change the interface in the future without notice. +// +// The prototypes were obtained from the following link: +// http://cocoadex.com/2008/02/nsevent-modifications-swipe-ro.html +- (void)swipeWithEvent:(NSEvent *)anEvent; +- (void)beginGestureWithEvent:(NSEvent *)anEvent; +- (void)magnifyWithEvent:(NSEvent *)anEvent; +- (void)rotateWithEvent:(NSEvent *)anEvent; +- (void)endGestureWithEvent:(NSEvent *)anEvent; @end diff --git a/widget/src/cocoa/nsChildView.mm b/widget/src/cocoa/nsChildView.mm index 313705a0410..edb4a8951ae 100644 --- a/widget/src/cocoa/nsChildView.mm +++ b/widget/src/cocoa/nsChildView.mm @@ -25,6 +25,7 @@ * HÃ¥kan Waara * Stuart Morgan * Mats Palmgren + * Thomas K. Dyas (simple gestures support) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -72,6 +73,8 @@ #include "nsCocoaUtils.h" #include "nsMenuBarX.h" +#include "nsIDOMSimpleGestureEvent.h" + #include "gfxContext.h" #include "gfxQuartzSurface.h" @@ -2358,6 +2361,10 @@ NSEvent* gLastDragEvent = nil; mDragService = nsnull; mPluginTSMDoc = nil; + + mGestureState = eGestureState_None; + mCumulativeMagnification = 0.0; + mCumulativeRotation = 0.0; } // register for things we'll take from other applications @@ -3235,6 +3242,214 @@ static const PRInt32 sShadowInvalidationInterval = 100; } +/* + * XXX - The swipeWithEvent, beginGestureWithEvent, magnifyWithEvent, + * rotateWithEvent, and endGestureWithEvent methods are part of a + * PRIVATE interface exported by nsResponder and reverse-engineering + * was necessary to obtain the methods' prototypes. Thus, Apple may + * change the interface in the future without notice. + * + * The prototypes were obtained from the following link: + * http://cocoadex.com/2008/02/nsevent-modifications-swipe-ro.html + */ + +- (void)swipeWithEvent:(NSEvent *)anEvent +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + if (!anEvent || !mGeckoChild) + return; + + nsAutoRetainCocoaObject kungFuDeathGrip(self); + + float deltaX = [anEvent deltaX]; // left=1.0, right=-1.0 + float deltaY = [anEvent deltaY]; // up=1.0, down=-1.0 + + // Setup the "swipe" event. + nsSimpleGestureEvent geckoEvent(PR_TRUE, NS_SIMPLE_GESTURE_SWIPE, mGeckoChild, 0, 0.0); + [self convertGenericCocoaEvent:anEvent toGeckoEvent:&geckoEvent]; + + // Record the left/right direction. + if (deltaX > 0.0) + geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_LEFT; + else if (deltaX < 0.0) + geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_RIGHT; + + // Record the up/down direction. + if (deltaY > 0.0) + geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_UP; + else if (deltaY < 0.0) + geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_DOWN; + + // Send the event. + mGeckoChild->DispatchWindowEvent(geckoEvent); + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + + +- (void)beginGestureWithEvent:(NSEvent *)anEvent +{ + NS_ASSERTION(mGestureState == eGestureState_None, "mGestureState should be eGestureState_None"); + + if (!anEvent) + return; + + mGestureState = eGestureState_StartGesture; + mCumulativeMagnification = 0; + mCumulativeRotation = 0.0; +} + + +- (void)magnifyWithEvent:(NSEvent *)anEvent +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + if (!anEvent || !mGeckoChild) + return; + + nsAutoRetainCocoaObject kungFuDeathGrip(self); + + float deltaZ = [anEvent deltaZ]; + + PRUint32 msg; + switch (mGestureState) { + case eGestureState_StartGesture: + msg = NS_SIMPLE_GESTURE_MAGNIFY_START; + mGestureState = eGestureState_MagnifyGesture; + break; + + case eGestureState_MagnifyGesture: + msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE; + break; + + case eGestureState_None: + case eGestureState_RotateGesture: + default: + return; + } + + // Setup the event. + nsSimpleGestureEvent geckoEvent(PR_TRUE, msg, mGeckoChild, 0, deltaZ); + [self convertGenericCocoaEvent:anEvent toGeckoEvent:&geckoEvent]; + + // Send the event. + mGeckoChild->DispatchWindowEvent(geckoEvent); + + // Keep track of the cumulative magnification for the final "magnify" event. + mCumulativeMagnification += deltaZ; + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + + +- (void)rotateWithEvent:(NSEvent *)anEvent +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + if (!anEvent || !mGeckoChild) + return; + + nsAutoRetainCocoaObject kungFuDeathGrip(self); + + float rotation = [anEvent rotation]; + + PRUint32 msg; + switch (mGestureState) { + case eGestureState_StartGesture: + msg = NS_SIMPLE_GESTURE_ROTATE_START; + mGestureState = eGestureState_RotateGesture; + break; + + case eGestureState_RotateGesture: + msg = NS_SIMPLE_GESTURE_ROTATE_UPDATE; + break; + + case eGestureState_None: + case eGestureState_MagnifyGesture: + default: + return; + } + + // Setup the event. + nsSimpleGestureEvent geckoEvent(PR_TRUE, msg, mGeckoChild, 0, 0.0); + [self convertGenericCocoaEvent:anEvent toGeckoEvent:&geckoEvent]; + geckoEvent.delta = -rotation; + if (rotation > 0.0) { + geckoEvent.direction = nsIDOMSimpleGestureEvent::DIRECTION_LEFT; + } else { + geckoEvent.direction = nsIDOMSimpleGestureEvent::DIRECTION_RIGHT; + } + + // Send the event. + mGeckoChild->DispatchWindowEvent(geckoEvent); + + // Keep track of the cumulative rotation for the final "rotate" event. + mCumulativeRotation += rotation; + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + + +- (void)endGestureWithEvent:(NSEvent *)anEvent +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + if (!anEvent || !mGeckoChild) { + // Clear the gestures state if we cannot send an event. + mGestureState = eGestureState_None; + mCumulativeMagnification = 0.0; + mCumulativeRotation = 0.0; + return; + } + + nsAutoRetainCocoaObject kungFuDeathGrip(self); + + switch (mGestureState) { + case eGestureState_MagnifyGesture: + { + // Setup the "magnify" event. + nsSimpleGestureEvent geckoEvent(PR_TRUE, NS_SIMPLE_GESTURE_MAGNIFY, + mGeckoChild, 0, mCumulativeMagnification); + [self convertGenericCocoaEvent:anEvent toGeckoEvent:&geckoEvent]; + + // Send the event. + mGeckoChild->DispatchWindowEvent(geckoEvent); + } + break; + + case eGestureState_RotateGesture: + { + // Setup the "rotate" event. + nsSimpleGestureEvent geckoEvent(PR_TRUE, NS_SIMPLE_GESTURE_ROTATE, mGeckoChild, 0, 0.0); + [self convertGenericCocoaEvent:anEvent toGeckoEvent:&geckoEvent]; + geckoEvent.delta = -mCumulativeRotation; + if (mCumulativeRotation > 0.0) { + geckoEvent.direction = nsIDOMSimpleGestureEvent::DIRECTION_LEFT; + } else { + geckoEvent.direction = nsIDOMSimpleGestureEvent::DIRECTION_RIGHT; + } + + // Send the event. + mGeckoChild->DispatchWindowEvent(geckoEvent); + } + break; + + case eGestureState_None: + case eGestureState_StartGesture: + default: + break; + } + + // Clear the gestures state. + mGestureState = eGestureState_None; + mCumulativeMagnification = 0.0; + mCumulativeRotation = 0.0; + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + + - (void)mouseDown:(NSEvent*)theEvent { NS_OBJC_BEGIN_TRY_ABORT_BLOCK;