Bug 1449821 - 2. Add finalizer support to EventDispatcher; r?snorp
Add a new interface, nsIAndroidEventFinalizer, that is called whenever a
callback object is finalized, i.e. there can be no more callbacks for a
particular dispatch. Add the finalizer as a new parameter to dispatch().
Existing callers that don't need a finalizer are not affected.
This functionality is required in order to unregister our message
manager listener in a child process, after dispatching an event with
callback from a child process.
MozReview-Commit-ID: 8n89c63UGNm
--- a/widget/android/EventDispatcher.cpp
+++ b/widget/android/EventDispatcher.cpp
@@ -556,17 +556,17 @@ UnboxData(jni::String::Param aEvent, JSC
} else {
JS_ReportErrorUTF8(aCx, "Invalid event data for %s", event.get());
}
return NS_ERROR_INVALID_ARG;
}
class JavaCallbackDelegate final : public nsIAndroidEventCallback
{
- java::EventCallback::GlobalRef mCallback;
+ const java::EventCallback::GlobalRef mCallback;
virtual ~JavaCallbackDelegate() {}
NS_IMETHOD Call(JS::HandleValue aData,
void (java::EventCallback::*aCall)(
jni::Object::Param) const)
{
MOZ_ASSERT(NS_IsMainThread());
@@ -605,16 +605,17 @@ NS_IMPL_ISUPPORTS(JavaCallbackDelegate,
class NativeCallbackDelegateSupport final :
public java::EventDispatcher::NativeCallbackDelegate
::Natives<NativeCallbackDelegateSupport>
{
using CallbackDelegate = java::EventDispatcher::NativeCallbackDelegate;
using Base = CallbackDelegate::Natives<NativeCallbackDelegateSupport>;
const nsCOMPtr<nsIAndroidEventCallback> mCallback;
+ const nsCOMPtr<nsIAndroidEventFinalizer> mFinalizer;
const nsCOMPtr<nsPIDOMWindowOuter> mWindow;
void Call(jni::Object::Param aData,
nsresult (nsIAndroidEventCallback::*aCall)(JS::HandleValue))
{
MOZ_ASSERT(NS_IsMainThread());
// Use the same compartment as the wrapped JS object if possible,
@@ -654,32 +655,66 @@ public:
}
static void Finalize(const CallbackDelegate::LocalRef& aInstance)
{
DisposeNative(aInstance);
}
NativeCallbackDelegateSupport(nsIAndroidEventCallback* callback,
+ nsIAndroidEventFinalizer* finalizer,
nsPIDOMWindowOuter* domWindow)
: mCallback(callback)
+ , mFinalizer(finalizer)
, mWindow(domWindow)
{}
+ ~NativeCallbackDelegateSupport()
+ {
+ if (mFinalizer) {
+ mFinalizer->OnFinalize();
+ }
+ }
+
void SendSuccess(jni::Object::Param aData)
{
Call(aData, &nsIAndroidEventCallback::OnSuccess);
}
void SendError(jni::Object::Param aData)
{
Call(aData, &nsIAndroidEventCallback::OnError);
}
};
+class FinalizingCallbackDelegate final : public nsIAndroidEventCallback
+{
+ const nsCOMPtr<nsIAndroidEventCallback> mCallback;
+ const nsCOMPtr<nsIAndroidEventFinalizer> mFinalizer;
+
+ virtual ~FinalizingCallbackDelegate()
+ {
+ if (mFinalizer) {
+ mFinalizer->OnFinalize();
+ }
+ }
+
+public:
+ FinalizingCallbackDelegate(nsIAndroidEventCallback* aCallback,
+ nsIAndroidEventFinalizer* aFinalizer)
+ : mCallback(aCallback)
+ , mFinalizer(aFinalizer)
+ {}
+
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_NSIANDROIDEVENTCALLBACK(mCallback->);
+};
+
+NS_IMPL_ISUPPORTS(FinalizingCallbackDelegate, nsIAndroidEventCallback)
+
} // namespace detail
using namespace detail;
NS_IMPL_ISUPPORTS(EventDispatcher, nsIAndroidEventDispatcher)
nsresult
EventDispatcher::DispatchOnGecko(ListenersList* list, const nsAString& aEvent,
@@ -715,34 +750,37 @@ EventDispatcher::DispatchOnGecko(Listene
const nsresult rv = list->listeners[i]->OnEvent(
aEvent, aData, aCallback);
Unused << NS_WARN_IF(NS_FAILED(rv));
}
return NS_OK;
}
java::EventDispatcher::NativeCallbackDelegate::LocalRef
-EventDispatcher::WrapCallback(nsIAndroidEventCallback* aCallback)
+EventDispatcher::WrapCallback(nsIAndroidEventCallback* aCallback,
+ nsIAndroidEventFinalizer* aFinalizer)
{
- java::EventDispatcher::NativeCallbackDelegate::LocalRef
- callback(jni::GetGeckoThreadEnv());
+ if (!aCallback) {
+ return java::EventDispatcher::NativeCallbackDelegate::LocalRef(
+ jni::GetGeckoThreadEnv());
+ }
- if (aCallback) {
- callback = java::EventDispatcher::NativeCallbackDelegate::New();
- NativeCallbackDelegateSupport::AttachNative(
- callback,
- MakeUnique<NativeCallbackDelegateSupport>(
- aCallback, mDOMWindow));
- }
+ java::EventDispatcher::NativeCallbackDelegate::LocalRef
+ callback = java::EventDispatcher::NativeCallbackDelegate::New();
+ NativeCallbackDelegateSupport::AttachNative(
+ callback,
+ MakeUnique<NativeCallbackDelegateSupport>(
+ aCallback, aFinalizer, mDOMWindow));
return callback;
}
NS_IMETHODIMP
EventDispatcher::Dispatch(JS::HandleValue aEvent, JS::HandleValue aData,
- nsIAndroidEventCallback* aCallback, JSContext* aCx)
+ nsIAndroidEventCallback* aCallback,
+ nsIAndroidEventFinalizer* aFinalizer, JSContext* aCx)
{
MOZ_ASSERT(NS_IsMainThread());
if (!aEvent.isString()) {
NS_WARNING("Invalid event name");
return NS_ERROR_INVALID_ARG;
}
@@ -750,29 +788,35 @@ EventDispatcher::Dispatch(JS::HandleValu
NS_ENSURE_TRUE(CheckJS(aCx, event.init(aCx, aEvent.toString())),
NS_ERROR_OUT_OF_MEMORY);
// Don't need to lock here because we're on the main thread, and we can't
// race against Register/UnregisterListener.
ListenersList* list = mListenersMap.Get(event);
if (list) {
- return DispatchOnGecko(list, event, aData, aCallback);
+ if (!aCallback || !aFinalizer) {
+ return DispatchOnGecko(list, event, aData, aCallback);
+ }
+ nsCOMPtr<nsIAndroidEventCallback> callback(
+ new FinalizingCallbackDelegate(aCallback, aFinalizer));
+ return DispatchOnGecko(list, event, aData, callback);
}
if (!mDispatcher) {
return NS_OK;
}
jni::Object::LocalRef data(jni::GetGeckoThreadEnv());
nsresult rv = BoxData(event, aCx, aData, data, /* ObjectOnly */ true);
NS_ENSURE_SUCCESS(rv, JS_IsExceptionPending(aCx) ? NS_OK : rv);
dom::AutoNoJSAPI nojsapi;
- mDispatcher->DispatchToThreads(event, data, WrapCallback(aCallback));
+ mDispatcher->DispatchToThreads(event, data,
+ WrapCallback(aCallback, aFinalizer));
return NS_OK;
}
nsresult
EventDispatcher::Dispatch(const char16_t* aEvent,
java::GeckoBundle::Param aData,
nsIAndroidEventCallback* aCallback)
{
--- a/widget/android/EventDispatcher.h
+++ b/widget/android/EventDispatcher.h
@@ -85,15 +85,16 @@ private:
nsresult RegisterEventLocked(const nsAString&, nsIAndroidEventListener*);
nsresult UnregisterEventLocked(const nsAString&, nsIAndroidEventListener*);
nsresult DispatchOnGecko(ListenersList* list, const nsAString& aEvent,
JS::HandleValue aData,
nsIAndroidEventCallback* aCallback);
java::EventDispatcher::NativeCallbackDelegate::LocalRef
- WrapCallback(nsIAndroidEventCallback* aCallback);
+ WrapCallback(nsIAndroidEventCallback* aCallback,
+ nsIAndroidEventFinalizer* aFinalizer = nullptr);
};
} // namespace widget
} // namespace mozilla
#endif // mozilla_widget_EventDispatcher_h
--- a/widget/android/nsIAndroidBridge.idl
+++ b/widget/android/nsIAndroidBridge.idl
@@ -33,31 +33,38 @@ interface nsIAndroidBrowserApp : nsISupp
[scriptable, uuid(e64c39b8-b8ec-477d-aef5-89d517ff9219)]
interface nsIAndroidEventCallback : nsISupports
{
void onSuccess([optional] in jsval data);
void onError([optional] in jsval data);
};
+[scriptable, function, uuid(819ee2db-d3b8-46dd-a476-40f89c49133c)]
+interface nsIAndroidEventFinalizer : nsISupports
+{
+ void onFinalize();
+};
+
[scriptable, function, uuid(73569a75-78eb-4c7f-82b9-2d4f5ccf44c3)]
interface nsIAndroidEventListener : nsISupports
{
void onEvent(in AString event,
[optional] in jsval data,
[optional] in nsIAndroidEventCallback callback);
};
[scriptable, uuid(e98bf792-4145-411e-b298-8219d9b03817)]
interface nsIAndroidEventDispatcher : nsISupports
{
[implicit_jscontext]
void dispatch(in jsval event,
[optional] in jsval data,
- [optional] in nsIAndroidEventCallback callback);
+ [optional] in nsIAndroidEventCallback callback,
+ [optional] in nsIAndroidEventFinalizer finalizer);
[implicit_jscontext]
void registerListener(in nsIAndroidEventListener listener,
in jsval events);
[implicit_jscontext]
void unregisterListener(in nsIAndroidEventListener listener,
in jsval events);
};