tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

xpcAccessibleMacInterface.mm (22921B)


      1 /* clang-format off */
      2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      3 /* clang-format on */
      4 /* vim: set ts=2 et sw=2 tw=80: */
      5 /* This Source Code Form is subject to the terms of the Mozilla Public
      6 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      7 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      8 
      9 #include "xpcAccessibleMacInterface.h"
     10 
     11 #include "nsCocoaUtils.h"
     12 #include "nsContentUtils.h"
     13 #include "nsIObserverService.h"
     14 #include "nsISimpleEnumerator.h"
     15 #include "nsIXPConnect.h"
     16 #include "mozilla/dom/ToJSValue.h"
     17 #include "mozilla/Services.h"
     18 #include "nsString.h"
     19 #include "js/PropertyAndElement.h"  // JS_Enumerate, JS_GetElement, JS_GetProperty, JS_GetPropertyById, JS_HasOwnProperty, JS_SetUCProperty
     20 
     21 #import <Accessibility/Accessibility.h>
     22 
     23 #import "mozAccessible.h"
     24 
     25 using namespace mozilla::a11y;
     26 
     27 // xpcAccessibleMacNSObjectWrapper
     28 
     29 NS_IMPL_ISUPPORTS(xpcAccessibleMacNSObjectWrapper,
     30                  nsIAccessibleMacNSObjectWrapper)
     31 
     32 xpcAccessibleMacNSObjectWrapper::xpcAccessibleMacNSObjectWrapper(id aNativeObj)
     33    : mNativeObject(aNativeObj) {
     34  [mNativeObject retain];
     35 }
     36 
     37 xpcAccessibleMacNSObjectWrapper::~xpcAccessibleMacNSObjectWrapper() {
     38  [mNativeObject release];
     39 }
     40 
     41 id xpcAccessibleMacNSObjectWrapper::GetNativeObject() { return mNativeObject; }
     42 
     43 // xpcAccessibleMacInterface
     44 
     45 NS_IMPL_ISUPPORTS_INHERITED(xpcAccessibleMacInterface,
     46                            xpcAccessibleMacNSObjectWrapper,
     47                            nsIAccessibleMacInterface)
     48 
     49 xpcAccessibleMacInterface::xpcAccessibleMacInterface(Accessible* aObj)
     50    : xpcAccessibleMacNSObjectWrapper(GetNativeFromGeckoAccessible(aObj)) {}
     51 
     52 NS_IMETHODIMP
     53 xpcAccessibleMacInterface::GetAttributeNames(
     54    nsTArray<nsString>& aAttributeNames) {
     55  NS_OBJC_BEGIN_TRY_BLOCK_RETURN
     56 
     57  if (!mNativeObject || [mNativeObject isExpired]) {
     58    return NS_ERROR_NOT_AVAILABLE;
     59  }
     60 
     61  for (NSString* name in [mNativeObject accessibilityAttributeNames]) {
     62    nsAutoString attribName;
     63    nsCocoaUtils::GetStringForNSString(name, attribName);
     64    aAttributeNames.AppendElement(attribName);
     65  }
     66 
     67  return NS_OK;
     68 
     69  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
     70 }
     71 
     72 NS_IMETHODIMP
     73 xpcAccessibleMacInterface::GetParameterizedAttributeNames(
     74    nsTArray<nsString>& aAttributeNames) {
     75  NS_OBJC_BEGIN_TRY_BLOCK_RETURN
     76 
     77  if (!mNativeObject || [mNativeObject isExpired]) {
     78    return NS_ERROR_NOT_AVAILABLE;
     79  }
     80 
     81  for (NSString* name in
     82       [mNativeObject accessibilityParameterizedAttributeNames]) {
     83    nsAutoString attribName;
     84    nsCocoaUtils::GetStringForNSString(name, attribName);
     85    aAttributeNames.AppendElement(attribName);
     86  }
     87 
     88  return NS_OK;
     89 
     90  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
     91 }
     92 
     93 // Return a string that uniquely identifies a custom action.
     94 static NSString* GetCustomActionName(NSAccessibilityCustomAction* action) {
     95  return [NSString stringWithFormat:@"Name:%@ Target:%@ Selector:%@",
     96                                    [action name], [action target],
     97                                    NSStringFromSelector([action selector])];
     98 }
     99 
    100 NS_IMETHODIMP
    101 xpcAccessibleMacInterface::GetActionNames(nsTArray<nsString>& aActionNames) {
    102  NS_OBJC_BEGIN_TRY_BLOCK_RETURN
    103 
    104  if (!mNativeObject || [mNativeObject isExpired]) {
    105    return NS_ERROR_NOT_AVAILABLE;
    106  }
    107 
    108  for (NSString* name in [mNativeObject accessibilityActionNames]) {
    109    nsAutoString actionName;
    110    nsCocoaUtils::GetStringForNSString(name, actionName);
    111    aActionNames.AppendElement(actionName);
    112  }
    113 
    114  if (NSArray* customActions = [mNativeObject accessibilityCustomActions]) {
    115    for (id action in customActions) {
    116      nsAutoString actionName;
    117      NSString* actionNameStr = GetCustomActionName(action);
    118      nsCocoaUtils::GetStringForNSString(actionNameStr, actionName);
    119      aActionNames.AppendElement(actionName);
    120    }
    121  }
    122 
    123  return NS_OK;
    124 
    125  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
    126 }
    127 
    128 NS_IMETHODIMP
    129 xpcAccessibleMacInterface::GetActionDescription(const nsAString& aActionName,
    130                                                nsAString& aDescription) {
    131  aDescription.Truncate();
    132 
    133  if (!mNativeObject || [mNativeObject isExpired]) {
    134    return NS_ERROR_NOT_AVAILABLE;
    135  }
    136 
    137  NSString* actionName = nsCocoaUtils::ToNSString(aActionName);
    138 
    139  // First search custom actions, since `accessibilityActionDescription` will
    140  // just return the provided name if no description is found.
    141  if (NSArray* customActions = [mNativeObject accessibilityCustomActions]) {
    142    for (id action in customActions) {
    143      NSString* actionNameStr = GetCustomActionName(action);
    144      if ([actionNameStr isEqualToString:actionName]) {
    145        nsCocoaUtils::GetStringForNSString([action name], aDescription);
    146        return NS_OK;
    147      }
    148    }
    149  }
    150 
    151  NSString* description =
    152      [mNativeObject accessibilityActionDescription:actionName];
    153  nsCocoaUtils::GetStringForNSString(description, aDescription);
    154 
    155  return NS_OK;
    156 }
    157 
    158 NS_IMETHODIMP
    159 xpcAccessibleMacInterface::PerformAction(const nsAString& aActionName) {
    160  NS_OBJC_BEGIN_TRY_BLOCK_RETURN
    161 
    162  if (!mNativeObject || [mNativeObject isExpired]) {
    163    return NS_ERROR_NOT_AVAILABLE;
    164  }
    165 
    166  NSString* actionName = nsCocoaUtils::ToNSString(aActionName);
    167 
    168  // First search custom actions, since `accessibilityPerformAction` will
    169  // silently fail on unknown action names.
    170  if (NSArray* customActions = [mNativeObject accessibilityCustomActions]) {
    171    for (id action in customActions) {
    172      NSString* actionNameStr = GetCustomActionName(action);
    173      if ([actionNameStr isEqualToString:actionName]) {
    174        [[action target] performSelector:[action selector]];
    175        return NS_OK;
    176      }
    177    }
    178  }
    179 
    180  [mNativeObject accessibilityPerformAction:actionName];
    181 
    182  return NS_OK;
    183 
    184  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
    185 }
    186 
    187 NS_IMETHODIMP
    188 xpcAccessibleMacInterface::GetAttributeValue(const nsAString& aAttributeName,
    189                                             JSContext* aCx,
    190                                             JS::MutableHandleValue aResult) {
    191  NS_OBJC_BEGIN_TRY_BLOCK_RETURN
    192 
    193  if (!mNativeObject || [mNativeObject isExpired]) {
    194    return NS_ERROR_NOT_AVAILABLE;
    195  }
    196 
    197  NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName);
    198  return NSObjectToJsValue(
    199      [mNativeObject accessibilityAttributeValue:attribName], aCx, aResult);
    200 
    201  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
    202 }
    203 
    204 NS_IMETHODIMP
    205 xpcAccessibleMacInterface::IsAttributeSettable(const nsAString& aAttributeName,
    206                                               bool* aIsSettable) {
    207  NS_ENSURE_ARG_POINTER(aIsSettable);
    208 
    209  NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName);
    210  if ([mNativeObject
    211          respondsToSelector:@selector(accessibilityIsAttributeSettable:)]) {
    212    *aIsSettable = [mNativeObject accessibilityIsAttributeSettable:attribName];
    213    return NS_OK;
    214  }
    215 
    216  return NS_ERROR_NOT_IMPLEMENTED;
    217 }
    218 
    219 NS_IMETHODIMP
    220 xpcAccessibleMacInterface::SetAttributeValue(const nsAString& aAttributeName,
    221                                             JS::HandleValue aAttributeValue,
    222                                             JSContext* aCx) {
    223  nsresult rv = NS_OK;
    224  id obj = JsValueToNSObject(aAttributeValue, aCx, &rv);
    225  NS_ENSURE_SUCCESS(rv, rv);
    226 
    227  NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName);
    228  if ([mNativeObject respondsToSelector:@selector(accessibilitySetValue:
    229                                                           forAttribute:)]) {
    230    // The NSObject has an attribute setter, call that.
    231    [mNativeObject accessibilitySetValue:obj forAttribute:attribName];
    232    return NS_OK;
    233  }
    234 
    235  return NS_ERROR_NOT_IMPLEMENTED;
    236 }
    237 
    238 NS_IMETHODIMP
    239 xpcAccessibleMacInterface::GetParameterizedAttributeValue(
    240    const nsAString& aAttributeName, JS::HandleValue aParameter, JSContext* aCx,
    241    JS::MutableHandleValue aResult) {
    242  nsresult rv = NS_OK;
    243  id paramObj = JsValueToNSObject(aParameter, aCx, &rv);
    244  NS_ENSURE_SUCCESS(rv, rv);
    245 
    246  NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName);
    247  return NSObjectToJsValue([mNativeObject accessibilityAttributeValue:attribName
    248                                                         forParameter:paramObj],
    249                           aCx, aResult);
    250 }
    251 
    252 bool xpcAccessibleMacInterface::SupportsSelector(SEL aSelector) {
    253  // return true if we have this selector, and if isAccessibilitySelectorAllowed
    254  // is implemented too whether it is "allowed".
    255  return [mNativeObject respondsToSelector:aSelector] &&
    256         (![mNativeObject respondsToSelector:@selector
    257                          (isAccessibilitySelectorAllowed:selector:)] ||
    258          [mNativeObject isAccessibilitySelectorAllowed:aSelector]);
    259 }
    260 
    261 nsresult xpcAccessibleMacInterface::NSObjectToJsValue(
    262    id aObj, JSContext* aCx, JS::MutableHandleValue aResult) {
    263  if (!aObj) {
    264    aResult.set(JS::NullValue());
    265  } else if ([aObj isKindOfClass:[NSString class]]) {
    266    nsAutoString strVal;
    267    nsCocoaUtils::GetStringForNSString((NSString*)aObj, strVal);
    268    if (!mozilla::dom::ToJSValue(aCx, strVal, aResult)) {
    269      return NS_ERROR_FAILURE;
    270    }
    271  } else if ([aObj isKindOfClass:[NSNumber class]]) {
    272    // If the type being held by the NSNumber is a BOOL set js value
    273    // to boolean. Otherwise use a double value.
    274    if (strcmp([(NSNumber*)aObj objCType], @encode(BOOL)) == 0) {
    275      if (!mozilla::dom::ToJSValue(aCx, [(NSNumber*)aObj boolValue], aResult)) {
    276        return NS_ERROR_FAILURE;
    277      }
    278    } else {
    279      if (!mozilla::dom::ToJSValue(aCx, [(NSNumber*)aObj doubleValue],
    280                                   aResult)) {
    281        return NS_ERROR_FAILURE;
    282      }
    283    }
    284  } else if ([aObj isKindOfClass:[NSValue class]] &&
    285             strcmp([(NSValue*)aObj objCType], @encode(NSPoint)) == 0) {
    286    NSPoint point = [(NSValue*)aObj pointValue];
    287    return NSObjectToJsValue(
    288        @[
    289          [NSNumber numberWithDouble:point.x],
    290          [NSNumber numberWithDouble:point.y]
    291        ],
    292        aCx, aResult);
    293  } else if ([aObj isKindOfClass:[NSValue class]] &&
    294             strcmp([(NSValue*)aObj objCType], @encode(NSSize)) == 0) {
    295    NSSize size = [(NSValue*)aObj sizeValue];
    296    return NSObjectToJsValue(
    297        @[
    298          [NSNumber numberWithDouble:size.width],
    299          [NSNumber numberWithDouble:size.height]
    300        ],
    301        aCx, aResult);
    302  } else if ([aObj isKindOfClass:[NSValue class]] &&
    303             strcmp([(NSValue*)aObj objCType], @encode(NSRange)) == 0) {
    304    NSRange range = [(NSValue*)aObj rangeValue];
    305    return NSObjectToJsValue(@[ @(range.location), @(range.length) ], aCx,
    306                             aResult);
    307  } else if ([aObj isKindOfClass:[NSValue class]] &&
    308             strcmp([(NSValue*)aObj objCType], @encode(NSRect)) == 0) {
    309    NSRect rect = [(NSValue*)aObj rectValue];
    310    return NSObjectToJsValue(@{
    311      @"origin" : [NSValue valueWithPoint:rect.origin],
    312      @"size" : [NSValue valueWithSize:rect.size]
    313    },
    314                             aCx, aResult);
    315  } else if ([aObj isKindOfClass:[NSArray class]]) {
    316    NSArray* objArr = (NSArray*)aObj;
    317 
    318    JS::RootedVector<JS::Value> v(aCx);
    319    if (!v.resize([objArr count])) {
    320      return NS_ERROR_FAILURE;
    321    }
    322    for (size_t i = 0; i < [objArr count]; ++i) {
    323      nsresult rv = NSObjectToJsValue(objArr[i], aCx, v[i]);
    324      NS_ENSURE_SUCCESS(rv, rv);
    325    }
    326 
    327    JSObject* arrayObj = JS::NewArrayObject(aCx, v);
    328    if (!arrayObj) {
    329      return NS_ERROR_FAILURE;
    330    }
    331    aResult.setObject(*arrayObj);
    332  } else if ([aObj isKindOfClass:[NSDictionary class]]) {
    333    JS::RootedObject obj(aCx, JS_NewPlainObject(aCx));
    334    for (NSString* key in aObj) {
    335      nsAutoString strKey;
    336      nsCocoaUtils::GetStringForNSString(key, strKey);
    337      JS::RootedValue value(aCx);
    338      nsresult rv = NSObjectToJsValue(aObj[key], aCx, &value);
    339      NS_ENSURE_SUCCESS(rv, rv);
    340      JS_SetUCProperty(aCx, obj, strKey.get(), strKey.Length(), value);
    341    }
    342    aResult.setObject(*obj);
    343  } else if ([aObj isKindOfClass:[NSAttributedString class]]) {
    344    NSAttributedString* attrStr = (NSAttributedString*)aObj;
    345    __block NSMutableArray* attrRunArray = [[NSMutableArray alloc] init];
    346 
    347    [attrStr
    348        enumerateAttributesInRange:NSMakeRange(0, [attrStr length])
    349                           options:
    350                               NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
    351                        usingBlock:^(NSDictionary* attributes, NSRange range,
    352                                     BOOL* stop) {
    353                          NSString* str =
    354                              [[attrStr string] substringWithRange:range];
    355                          if (!str || !attributes) {
    356                            return;
    357                          }
    358 
    359                          NSMutableDictionary* attrRun =
    360                              [attributes mutableCopy];
    361                          attrRun[@"string"] = str;
    362 
    363                          [attrRunArray addObject:attrRun];
    364                        }];
    365 
    366    // The attributed string is represented in js as an array of objects.
    367    // Each object represents a run of text where the "string" property is the
    368    // string value and all the AX* properties are the attributes.
    369    return NSObjectToJsValue(attrRunArray, aCx, aResult);
    370  } else if (CFGetTypeID(aObj) == CGColorGetTypeID()) {
    371    const CGFloat* components = CGColorGetComponents((CGColorRef)aObj);
    372    NSString* hexString = [NSString
    373        stringWithFormat:@"#%02x%02x%02x", (int)(components[0] * 0xff),
    374                         (int)(components[1] * 0xff),
    375                         (int)(components[2] * 0xff)];
    376    return NSObjectToJsValue(hexString, aCx, aResult);
    377  } else if ([aObj respondsToSelector:@selector(isAccessibilityElement)]) {
    378    // We expect all of our accessibility objects to implement
    379    // isAccessibilityElement at the very least. If it is implemented we will
    380    // assume its an accessibility object.
    381    nsCOMPtr<nsIAccessibleMacInterface> obj =
    382        new xpcAccessibleMacInterface(aObj);
    383    return nsContentUtils::WrapNative(
    384        aCx, obj, &NS_GET_IID(nsIAccessibleMacInterface), aResult);
    385  } else {
    386    if (@available(macOS 11.0, *)) {
    387      if ([aObj isKindOfClass:[AXCustomContent class]]) {
    388        // This is an AXCustomContent. Convert it to a single item dictionary.
    389        AXCustomContent* customContent = (AXCustomContent*)aObj;
    390        return NSObjectToJsValue(
    391            @{[customContent label] : [customContent value]}, aCx, aResult);
    392      }
    393    }
    394    // If this is any other kind of NSObject, just wrap it and return it.
    395    // It will be opaque and immutable on the JS side, but it can be
    396    // brought back to us in an argument.
    397    nsCOMPtr<nsIAccessibleMacNSObjectWrapper> obj =
    398        new xpcAccessibleMacNSObjectWrapper(aObj);
    399    return nsContentUtils::WrapNative(
    400        aCx, obj, &NS_GET_IID(nsIAccessibleMacNSObjectWrapper), aResult);
    401  }
    402 
    403  return NS_OK;
    404 }
    405 
    406 id xpcAccessibleMacInterface::JsValueToNSObject(JS::HandleValue aValue,
    407                                                JSContext* aCx,
    408                                                nsresult* aResult) {
    409  *aResult = NS_OK;
    410  if (aValue.isInt32()) {
    411    return [NSNumber numberWithInteger:aValue.toInt32()];
    412  } else if (aValue.isBoolean()) {
    413    return [NSNumber numberWithBool:aValue.toBoolean()];
    414  } else if (aValue.isString()) {
    415    nsAutoJSString temp;
    416    if (!temp.init(aCx, aValue)) {
    417      NS_WARNING("cannot init string with given value");
    418      *aResult = NS_ERROR_FAILURE;
    419      return nil;
    420    }
    421    return nsCocoaUtils::ToNSString(temp);
    422  } else if (aValue.isObject()) {
    423    JS::Rooted<JSObject*> obj(aCx, aValue.toObjectOrNull());
    424 
    425    bool isArray;
    426    JS::IsArrayObject(aCx, obj, &isArray);
    427    if (isArray) {
    428      // If this is an array, we construct an NSArray and insert the js
    429      // array's elements by recursively calling this function.
    430      uint32_t len;
    431      JS::GetArrayLength(aCx, obj, &len);
    432      NSMutableArray* array = [NSMutableArray arrayWithCapacity:len];
    433      for (uint32_t i = 0; i < len; i++) {
    434        JS::RootedValue v(aCx);
    435        JS_GetElement(aCx, obj, i, &v);
    436        [array addObject:JsValueToNSObject(v, aCx, aResult)];
    437        NS_ENSURE_SUCCESS(*aResult, nil);
    438      }
    439      return array;
    440    }
    441 
    442    bool hasValueType;
    443    bool hasValue;
    444    JS_HasOwnProperty(aCx, obj, "valueType", &hasValueType);
    445    JS_HasOwnProperty(aCx, obj, "value", &hasValue);
    446    if (hasValueType && hasValue) {
    447      // A js object representin an NSValue looks like this:
    448      // { valueType: "NSRange", value: [1, 3] }
    449      return JsValueToNSValue(obj, aCx, aResult);
    450    }
    451 
    452    bool hasObjectType;
    453    bool hasObject;
    454    JS_HasOwnProperty(aCx, obj, "objectType", &hasObjectType);
    455    JS_HasOwnProperty(aCx, obj, "object", &hasObject);
    456    if (hasObjectType && hasObject) {
    457      // A js object representing an NSDictionary looks like this:
    458      // { objectType: "NSDictionary", value: {k: v, k: v, ...} }
    459      return JsValueToSpecifiedNSObject(obj, aCx, aResult);
    460    }
    461 
    462    // This may be another nsIAccessibleMacInterface instance.
    463    // If so, return the wrapped NSObject.
    464    nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
    465 
    466    nsCOMPtr<nsIXPConnectWrappedNative> wrappedObj;
    467    nsresult rv =
    468        xpc->GetWrappedNativeOfJSObject(aCx, obj, getter_AddRefs(wrappedObj));
    469    NS_ENSURE_SUCCESS(rv, nil);
    470    nsCOMPtr<nsIAccessibleMacNSObjectWrapper> macObjIface =
    471        do_QueryInterface(wrappedObj->Native());
    472    return macObjIface->GetNativeObject();
    473  }
    474 
    475  *aResult = NS_ERROR_FAILURE;
    476  return nil;
    477 }
    478 
    479 id xpcAccessibleMacInterface::JsValueToNSValue(JS::HandleObject aObject,
    480                                               JSContext* aCx,
    481                                               nsresult* aResult) {
    482  *aResult = NS_ERROR_FAILURE;
    483  JS::RootedValue valueTypeValue(aCx);
    484  if (!JS_GetProperty(aCx, aObject, "valueType", &valueTypeValue)) {
    485    NS_WARNING("Could not get valueType");
    486    return nil;
    487  }
    488 
    489  JS::RootedValue valueValue(aCx);
    490  if (!JS_GetProperty(aCx, aObject, "value", &valueValue)) {
    491    NS_WARNING("Could not get value");
    492    return nil;
    493  }
    494 
    495  nsAutoJSString valueType;
    496  if (!valueTypeValue.isString() || !valueType.init(aCx, valueTypeValue)) {
    497    NS_WARNING("valueType is not a string");
    498    return nil;
    499  }
    500 
    501  bool isArray;
    502  JS::IsArrayObject(aCx, valueValue, &isArray);
    503  if (!isArray) {
    504    NS_WARNING("value is not an array");
    505    return nil;
    506  }
    507 
    508  JS::Rooted<JSObject*> value(aCx, valueValue.toObjectOrNull());
    509 
    510  if (valueType.EqualsLiteral("NSRange")) {
    511    uint32_t len;
    512    JS::GetArrayLength(aCx, value, &len);
    513    if (len != 2) {
    514      NS_WARNING("Expected a 2 member array");
    515      return nil;
    516    }
    517 
    518    JS::RootedValue locationValue(aCx);
    519    JS_GetElement(aCx, value, 0, &locationValue);
    520    JS::RootedValue lengthValue(aCx);
    521    JS_GetElement(aCx, value, 1, &lengthValue);
    522    if (!locationValue.isInt32() || !lengthValue.isInt32()) {
    523      NS_WARNING("Expected an array of integers");
    524      return nil;
    525    }
    526 
    527    *aResult = NS_OK;
    528    return [NSValue valueWithRange:NSMakeRange(locationValue.toInt32(),
    529                                               lengthValue.toInt32())];
    530  }
    531 
    532  return nil;
    533 }
    534 
    535 id xpcAccessibleMacInterface::JsValueToSpecifiedNSObject(
    536    JS::HandleObject aObject, JSContext* aCx, nsresult* aResult) {
    537  *aResult = NS_ERROR_FAILURE;
    538  JS::RootedValue objectTypeValue(aCx);
    539  if (!JS_GetProperty(aCx, aObject, "objectType", &objectTypeValue)) {
    540    NS_WARNING("Could not get objectType");
    541    return nil;
    542  }
    543 
    544  JS::RootedValue objectValue(aCx);
    545  if (!JS_GetProperty(aCx, aObject, "object", &objectValue)) {
    546    NS_WARNING("Could not get object");
    547    return nil;
    548  }
    549 
    550  nsAutoJSString objectType;
    551  if (!objectTypeValue.isString()) {
    552    NS_WARNING("objectType is not a string");
    553    return nil;
    554  }
    555 
    556  if (!objectType.init(aCx, objectTypeValue)) {
    557    NS_WARNING("cannot init string with object type");
    558    return nil;
    559  }
    560 
    561  bool isObject = objectValue.isObjectOrNull();
    562  if (!isObject) {
    563    NS_WARNING("object is not a JSON object");
    564    return nil;
    565  }
    566 
    567  JS::Rooted<JSObject*> object(aCx, objectValue.toObjectOrNull());
    568 
    569  if (objectType.EqualsLiteral("NSDictionary")) {
    570    JS::Rooted<JS::IdVector> ids(aCx, JS::IdVector(aCx));
    571    if (!JS_Enumerate(aCx, object, &ids)) {
    572      NS_WARNING("Unable to get keys from dictionary object");
    573      return nil;
    574    }
    575 
    576    NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
    577 
    578    for (size_t i = 0, n = ids.length(); i < n; i++) {
    579      nsresult rv = NS_OK;
    580      // get current key
    581      JS::RootedValue currentKey(aCx);
    582      JS_IdToValue(aCx, ids[i], &currentKey);
    583      id unwrappedKey = JsValueToNSObject(currentKey, aCx, &rv);
    584      NS_ENSURE_SUCCESS(rv, nil);
    585      MOZ_ASSERT([unwrappedKey isKindOfClass:[NSString class]]);
    586 
    587      // get associated value for current key
    588      JS::RootedValue currentValue(aCx);
    589      JS_GetPropertyById(aCx, object, ids[i], &currentValue);
    590      id unwrappedValue = JsValueToNSObject(currentValue, aCx, &rv);
    591      NS_ENSURE_SUCCESS(rv, nil);
    592      dict[unwrappedKey] = unwrappedValue;
    593    }
    594 
    595    *aResult = NS_OK;
    596    return dict;
    597  }
    598 
    599  return nil;
    600 }
    601 
    602 NS_IMPL_ISUPPORTS(xpcAccessibleMacEvent, nsIAccessibleMacEvent)
    603 
    604 xpcAccessibleMacEvent::xpcAccessibleMacEvent(id aNativeObj, id aData)
    605    : mNativeObject(aNativeObj), mData(aData) {
    606  [mNativeObject retain];
    607  [mData retain];
    608 }
    609 
    610 xpcAccessibleMacEvent::~xpcAccessibleMacEvent() {
    611  [mNativeObject release];
    612  [mData release];
    613 }
    614 
    615 NS_IMETHODIMP
    616 xpcAccessibleMacEvent::GetMacIface(nsIAccessibleMacInterface** aMacIface) {
    617  RefPtr<xpcAccessibleMacInterface> macIface =
    618      new xpcAccessibleMacInterface(mNativeObject);
    619  macIface.forget(aMacIface);
    620  return NS_OK;
    621 }
    622 
    623 NS_IMETHODIMP
    624 xpcAccessibleMacEvent::GetData(JSContext* aCx, JS::MutableHandleValue aData) {
    625  return xpcAccessibleMacInterface::NSObjectToJsValue(mData, aCx, aData);
    626 }
    627 
    628 void xpcAccessibleMacEvent::FireEvent(id aNativeObj, id aNotification,
    629                                      id aUserInfo) {
    630  if (nsCOMPtr<nsIObserverService> obsService =
    631          services::GetObserverService()) {
    632    nsCOMPtr<nsISimpleEnumerator> observers;
    633    // Get all observers for the mac event topic.
    634    obsService->EnumerateObservers(NS_ACCESSIBLE_MAC_EVENT_TOPIC,
    635                                   getter_AddRefs(observers));
    636    if (observers) {
    637      bool hasObservers = false;
    638      observers->HasMoreElements(&hasObservers);
    639      // If we have observers, notify them.
    640      if (hasObservers) {
    641        nsCOMPtr<nsIAccessibleMacEvent> xpcIface =
    642            new xpcAccessibleMacEvent(aNativeObj, aUserInfo);
    643        nsAutoString notificationStr;
    644        nsCocoaUtils::GetStringForNSString(aNotification, notificationStr);
    645        obsService->NotifyObservers(xpcIface, NS_ACCESSIBLE_MAC_EVENT_TOPIC,
    646                                    notificationStr.get());
    647      }
    648    }
    649  }
    650 }