Bug 1653421 - Part 5: Add xpc mac event interface so we can include data. r=morgan
authorEitan Isaacson <eitan@monotonous.org>
Tue, 21 Jul 2020 23:03:02 +0000
changeset 541543 cec6fc78de0ec7349530f49faa08c3fa1cfa6d9f
parent 541542 f8de624ccd8a858132ffac2d66d80082acc3ae6a
child 541544 88708e164b1add2cbd653456d3c2bbb1b5f64ea5
push id122322
push usereisaacson@mozilla.com
push dateTue, 21 Jul 2020 23:11:09 +0000
treeherderautoland@88708e164b1a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmorgan
bugs1653421
milestone80.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1653421 - Part 5: Add xpc mac event interface so we can include data. r=morgan Differential Revision: https://phabricator.services.mozilla.com/D84057
accessible/interfaces/nsIAccessibleMacInterface.idl
accessible/mac/MOXAccessibleBase.mm
accessible/tests/browser/mac/head.js
accessible/xpcom/xpcAccessibleMacInterface.h
accessible/xpcom/xpcAccessibleMacInterface.mm
--- a/accessible/interfaces/nsIAccessibleMacInterface.idl
+++ b/accessible/interfaces/nsIAccessibleMacInterface.idl
@@ -71,8 +71,17 @@ interface nsIAccessibleMacInterface : ns
 
   /**
    * Sets the given attribute with the given value on the object.
    * Emulates `AXUIElementSetAttributeValue`.
    **/
   [implicit_jscontext]
   void setAttributeValue(in AString attributeName, in jsval attributeValue);
 };
+
+[scriptable, builtinclass, uuid(6153f07b-2260-495b-9899-9699d9fe323e)]
+interface nsIAccessibleMacEvent : nsISupports
+{
+  readonly attribute nsIAccessibleMacInterface macIface;
+
+  [implicit_jscontext]
+  readonly attribute jsval data;
+};
--- a/accessible/mac/MOXAccessibleBase.mm
+++ b/accessible/mac/MOXAccessibleBase.mm
@@ -343,17 +343,17 @@ using namespace mozilla::a11y;
 }
 
 - (void)moxPostNotification:(NSString*)notification {
   [self moxPostNotification:notification withUserInfo:nil];
 }
 
 - (void)moxPostNotification:(NSString*)notification withUserInfo:(NSDictionary*)userInfo {
   // This sends events via nsIObserverService to be consumed by our mochitests.
-  xpcAccessibleMacInterface::FireEvent(self, notification);
+  xpcAccessibleMacEvent::FireEvent(self, notification, userInfo);
 
   if (gfxPlatform::IsHeadless()) {
     // Using a headless toolkit for tests and whatnot, posting accessibility
     // notification won't work.
     return;
   }
 
   if (![self isAccessibilityElement]) {
--- a/accessible/tests/browser/mac/head.js
+++ b/accessible/tests/browser/mac/head.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-/* exported getNativeInterface, waitForMacEvent, NSRange */
+/* exported getNativeInterface, waitForMacEventWithInfo, waitForMacEvent, NSRange */
 
 // Load the shared-head file first.
 /* import-globals-from ../shared-head.js */
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
   this
 );
 
@@ -21,29 +21,38 @@ loadScripts(
 );
 
 function getNativeInterface(accDoc, id) {
   return findAccessibleChildByID(accDoc, id).nativeInterface.QueryInterface(
     Ci.nsIAccessibleMacInterface
   );
 }
 
-function waitForMacEvent(notificationType, filter) {
+function waitForMacEventWithInfo(notificationType, filter) {
   return new Promise(resolve => {
     let eventObserver = {
       observe(subject, topic, data) {
-        let macIface = subject.QueryInterface(Ci.nsIAccessibleMacInterface);
-        if (data === notificationType && (!filter || filter(macIface))) {
+        let macEvent = subject.QueryInterface(Ci.nsIAccessibleMacEvent);
+        if (
+          data === notificationType &&
+          (!filter || filter(macEvent.macIface, macEvent.data))
+        ) {
           Services.obs.removeObserver(this, "accessible-mac-event");
-          resolve(macIface);
+          resolve(macEvent);
         }
       },
     };
     Services.obs.addObserver(eventObserver, "accessible-mac-event");
   });
 }
 
+function waitForMacEvent(notificationType, filter) {
+  return waitForMacEventWithInfo(notificationType, filter).then(
+    e => e.macIface
+  );
+}
+
 function NSRange(location, length) {
   return {
     valueType: "NSRange",
     value: [location, length],
   };
 }
--- a/accessible/xpcom/xpcAccessibleMacInterface.h
+++ b/accessible/xpcom/xpcAccessibleMacInterface.h
@@ -42,34 +42,29 @@ class xpcAccessibleMacInterface : public
 
   // Construct an xpcAccessibleMacInterface using the native object
   // associated with this accessible or proxy.
   explicit xpcAccessibleMacInterface(AccessibleOrProxy aObj);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLEMACINTERFACE
 
-  // This sends notifications via nsIObserverService to be consumed by our
-  // mochitests. aNativeObj is a NSAccessibility protocol object,
-  // and aNotification is a NSString.
-  static void FireEvent(id aNativeObj, id aNotification);
+  // Convert an NSObject (which can be anything, string, number, array, etc.)
+  // into a properly typed js value populated in the aResult handle.
+  static nsresult NSObjectToJsValue(id aObj, JSContext* aCx,
+                                    JS::MutableHandleValue aResult);
 
  protected:
   virtual ~xpcAccessibleMacInterface() {}
 
   // Return true if our native object responds to this selector and
   // if it implements isAccessibilitySelectorAllowed check that it returns true
   // too.
   bool SupportsSelector(SEL aSelector);
 
-  // Convert an NSObject (which can be anything, string, number, array, etc.)
-  // into a properly typed js value populated in the aResult handle.
-  nsresult NSObjectToJsValue(id aObj, JSContext* aCx,
-                             JS::MutableHandleValue aResult);
-
   // Convert a js value to an NSObject. This is called recursively for arrays.
   // If the conversion fails, aResult is set to an error and nil is returned.
   id JsValueToNSObject(JS::HandleValue aValue, JSContext* aCx,
                        nsresult* aResult);
 
   // Convert a js value to an NSValue NSObject. This is called
   // by JsValueToNSObject when encountering a JS object with
   // a "value" and "valueType" property.
@@ -77,12 +72,31 @@ class xpcAccessibleMacInterface : public
                       nsresult* aResult);
 
  private:
   xpcAccessibleMacInterface(const xpcAccessibleMacInterface&) = delete;
   xpcAccessibleMacInterface& operator=(const xpcAccessibleMacInterface&) =
       delete;
 };
 
+class xpcAccessibleMacEvent : public nsIAccessibleMacEvent {
+ public:
+  explicit xpcAccessibleMacEvent(id aNativeObj, id aData);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIACCESSIBLEMACEVENT;
+
+  // This sends notifications via nsIObserverService to be consumed by our
+  // mochitests. aNativeObj is a NSAccessibility protocol object,
+  // and aNotification is a NSString.
+  static void FireEvent(id aNativeObj, id aNotification, id aUserInfo);
+
+ protected:
+  virtual ~xpcAccessibleMacEvent();
+
+  id mNativeObject;
+  id mData;
+};
+
 }  // namespace a11y
 }  // namespace mozilla
 
 #endif
--- a/accessible/xpcom/xpcAccessibleMacInterface.mm
+++ b/accessible/xpcom/xpcAccessibleMacInterface.mm
@@ -228,16 +228,27 @@ nsresult xpcAccessibleMacInterface::NSOb
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     JSObject* arrayObj = JS::NewArrayObject(aCx, v);
     if (!arrayObj) {
       return NS_ERROR_FAILURE;
     }
     aResult.setObject(*arrayObj);
+  } else if ([aObj isKindOfClass:[NSDictionary class]]) {
+    JS::RootedObject obj(aCx, JS_NewPlainObject(aCx));
+    for (NSString* key in aObj) {
+      nsAutoString strKey;
+      nsCocoaUtils::GetStringForNSString(key, strKey);
+      JS::RootedValue value(aCx);
+      nsresult rv = NSObjectToJsValue(aObj[key], aCx, &value);
+      NS_ENSURE_SUCCESS(rv, rv);
+      JS_SetUCProperty(aCx, obj, strKey.get(), strKey.Length(), value);
+    }
+    aResult.setObject(*obj);
   } else if ([aObj respondsToSelector:@selector(isAccessibilityElement)]) {
     // We expect all of our accessibility objects to implement isAccessibilityElement
     // at the very least. If it is implemented we will assume its an accessibility object.
     nsCOMPtr<nsIAccessibleMacInterface> obj = new xpcAccessibleMacInterface(aObj);
     return nsContentUtils::WrapNative(aCx, obj, &NS_GET_IID(nsIAccessibleMacInterface), aResult);
   } else {
     // If this is any other kind of NSObject, just wrap it and return it.
     // It will be opaque and immutable on the JS side, but it can be
@@ -351,26 +362,51 @@ id xpcAccessibleMacInterface::JsValueToN
 
     *aResult = NS_OK;
     return [NSValue valueWithRange:NSMakeRange(locationValue.toInt32(), lengthValue.toInt32())];
   }
 
   return nil;
 }
 
-void xpcAccessibleMacInterface::FireEvent(id aNativeObj, id aNotification) {
+NS_IMPL_ISUPPORTS(xpcAccessibleMacEvent, nsIAccessibleMacEvent)
+
+xpcAccessibleMacEvent::xpcAccessibleMacEvent(id aNativeObj, id aData)
+    : mNativeObject(aNativeObj), mData(aData) {
+  [mNativeObject retain];
+  [mData retain];
+}
+
+xpcAccessibleMacEvent::~xpcAccessibleMacEvent() {
+  [mNativeObject release];
+  [mData release];
+}
+
+NS_IMETHODIMP
+xpcAccessibleMacEvent::GetMacIface(nsIAccessibleMacInterface** aMacIface) {
+  RefPtr<xpcAccessibleMacInterface> macIface = new xpcAccessibleMacInterface(mNativeObject);
+  macIface.forget(aMacIface);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+xpcAccessibleMacEvent::GetData(JSContext* aCx, JS::MutableHandleValue aData) {
+  return xpcAccessibleMacInterface::NSObjectToJsValue(mData, aCx, aData);
+}
+
+void xpcAccessibleMacEvent::FireEvent(id aNativeObj, id aNotification, id aUserInfo) {
   if (nsCOMPtr<nsIObserverService> obsService = services::GetObserverService()) {
     nsCOMPtr<nsISimpleEnumerator> observers;
     // Get all observers for the mac event topic.
     obsService->EnumerateObservers(NS_ACCESSIBLE_MAC_EVENT_TOPIC, getter_AddRefs(observers));
     if (observers) {
       bool hasObservers = false;
       observers->HasMoreElements(&hasObservers);
       // If we have observers, notify them.
       if (hasObservers) {
-        nsCOMPtr<nsIAccessibleMacInterface> xpcIface = new xpcAccessibleMacInterface(aNativeObj);
+        nsCOMPtr<nsIAccessibleMacEvent> xpcIface = new xpcAccessibleMacEvent(aNativeObj, aUserInfo);
         nsAutoString notificationStr;
         nsCocoaUtils::GetStringForNSString(aNotification, notificationStr);
         obsService->NotifyObservers(xpcIface, NS_ACCESSIBLE_MAC_EVENT_TOPIC, notificationStr.get());
       }
     }
   }
 }