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], ¤tKey); 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], ¤tValue); 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 }