Bug 1867925 - Mark some storage-access-api tests as intermittent after wpt-sync....
[gecko.git] / accessible / xpcom / xpcAccessibleMacInterface.mm
blobe05937360b61fd661424e973fd5e862e2d0804fe
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/. */
9 #include "xpcAccessibleMacInterface.h"
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
21 #import "mozAccessible.h"
23 using namespace mozilla::a11y;
25 // xpcAccessibleMacNSObjectWrapper
27 NS_IMPL_ISUPPORTS(xpcAccessibleMacNSObjectWrapper,
28                   nsIAccessibleMacNSObjectWrapper)
30 xpcAccessibleMacNSObjectWrapper::xpcAccessibleMacNSObjectWrapper(id aNativeObj)
31     : mNativeObject(aNativeObj) {
32   [mNativeObject retain];
35 xpcAccessibleMacNSObjectWrapper::~xpcAccessibleMacNSObjectWrapper() {
36   [mNativeObject release];
39 id xpcAccessibleMacNSObjectWrapper::GetNativeObject() { return mNativeObject; }
41 // xpcAccessibleMacInterface
43 NS_IMPL_ISUPPORTS_INHERITED(xpcAccessibleMacInterface,
44                             xpcAccessibleMacNSObjectWrapper,
45                             nsIAccessibleMacInterface)
47 xpcAccessibleMacInterface::xpcAccessibleMacInterface(Accessible* aObj)
48     : xpcAccessibleMacNSObjectWrapper(GetNativeFromGeckoAccessible(aObj)) {}
50 NS_IMETHODIMP
51 xpcAccessibleMacInterface::GetAttributeNames(
52     nsTArray<nsString>& aAttributeNames) {
53   NS_OBJC_BEGIN_TRY_BLOCK_RETURN
55   if (!mNativeObject || [mNativeObject isExpired]) {
56     return NS_ERROR_NOT_AVAILABLE;
57   }
59   for (NSString* name in [mNativeObject accessibilityAttributeNames]) {
60     nsAutoString attribName;
61     nsCocoaUtils::GetStringForNSString(name, attribName);
62     aAttributeNames.AppendElement(attribName);
63   }
65   return NS_OK;
67   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
70 NS_IMETHODIMP
71 xpcAccessibleMacInterface::GetParameterizedAttributeNames(
72     nsTArray<nsString>& aAttributeNames) {
73   NS_OBJC_BEGIN_TRY_BLOCK_RETURN
75   if (!mNativeObject || [mNativeObject isExpired]) {
76     return NS_ERROR_NOT_AVAILABLE;
77   }
79   for (NSString* name in
80        [mNativeObject accessibilityParameterizedAttributeNames]) {
81     nsAutoString attribName;
82     nsCocoaUtils::GetStringForNSString(name, attribName);
83     aAttributeNames.AppendElement(attribName);
84   }
86   return NS_OK;
88   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
91 NS_IMETHODIMP
92 xpcAccessibleMacInterface::GetActionNames(nsTArray<nsString>& aActionNames) {
93   NS_OBJC_BEGIN_TRY_BLOCK_RETURN
95   if (!mNativeObject || [mNativeObject isExpired]) {
96     return NS_ERROR_NOT_AVAILABLE;
97   }
99   for (NSString* name in [mNativeObject accessibilityActionNames]) {
100     nsAutoString actionName;
101     nsCocoaUtils::GetStringForNSString(name, actionName);
102     aActionNames.AppendElement(actionName);
103   }
105   return NS_OK;
107   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
110 NS_IMETHODIMP
111 xpcAccessibleMacInterface::PerformAction(const nsAString& aActionName) {
112   NS_OBJC_BEGIN_TRY_BLOCK_RETURN
114   if (!mNativeObject || [mNativeObject isExpired]) {
115     return NS_ERROR_NOT_AVAILABLE;
116   }
118   NSString* actionName = nsCocoaUtils::ToNSString(aActionName);
119   [mNativeObject accessibilityPerformAction:actionName];
121   return NS_OK;
123   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
126 NS_IMETHODIMP
127 xpcAccessibleMacInterface::GetAttributeValue(const nsAString& aAttributeName,
128                                              JSContext* aCx,
129                                              JS::MutableHandleValue aResult) {
130   NS_OBJC_BEGIN_TRY_BLOCK_RETURN
132   if (!mNativeObject || [mNativeObject isExpired]) {
133     return NS_ERROR_NOT_AVAILABLE;
134   }
136   NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName);
137   return NSObjectToJsValue(
138       [mNativeObject accessibilityAttributeValue:attribName], aCx, aResult);
140   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE)
143 NS_IMETHODIMP
144 xpcAccessibleMacInterface::IsAttributeSettable(const nsAString& aAttributeName,
145                                                bool* aIsSettable) {
146   NS_ENSURE_ARG_POINTER(aIsSettable);
148   NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName);
149   if ([mNativeObject
150           respondsToSelector:@selector(accessibilityIsAttributeSettable:)]) {
151     *aIsSettable = [mNativeObject accessibilityIsAttributeSettable:attribName];
152     return NS_OK;
153   }
155   return NS_ERROR_NOT_IMPLEMENTED;
158 NS_IMETHODIMP
159 xpcAccessibleMacInterface::SetAttributeValue(const nsAString& aAttributeName,
160                                              JS::HandleValue aAttributeValue,
161                                              JSContext* aCx) {
162   nsresult rv = NS_OK;
163   id obj = JsValueToNSObject(aAttributeValue, aCx, &rv);
164   NS_ENSURE_SUCCESS(rv, rv);
166   NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName);
167   if ([mNativeObject respondsToSelector:@selector(accessibilitySetValue:
168                                                            forAttribute:)]) {
169     // The NSObject has an attribute setter, call that.
170     [mNativeObject accessibilitySetValue:obj forAttribute:attribName];
171     return NS_OK;
172   }
174   return NS_ERROR_NOT_IMPLEMENTED;
177 NS_IMETHODIMP
178 xpcAccessibleMacInterface::GetParameterizedAttributeValue(
179     const nsAString& aAttributeName, JS::HandleValue aParameter, JSContext* aCx,
180     JS::MutableHandleValue aResult) {
181   nsresult rv = NS_OK;
182   id paramObj = JsValueToNSObject(aParameter, aCx, &rv);
183   NS_ENSURE_SUCCESS(rv, rv);
185   NSString* attribName = nsCocoaUtils::ToNSString(aAttributeName);
186   return NSObjectToJsValue([mNativeObject accessibilityAttributeValue:attribName
187                                                          forParameter:paramObj],
188                            aCx, aResult);
191 bool xpcAccessibleMacInterface::SupportsSelector(SEL aSelector) {
192   // return true if we have this selector, and if isAccessibilitySelectorAllowed
193   // is implemented too whether it is "allowed".
194   return [mNativeObject respondsToSelector:aSelector] &&
195          (![mNativeObject respondsToSelector:@selector
196                           (isAccessibilitySelectorAllowed:selector:)] ||
197           [mNativeObject isAccessibilitySelectorAllowed:aSelector]);
200 nsresult xpcAccessibleMacInterface::NSObjectToJsValue(
201     id aObj, JSContext* aCx, JS::MutableHandleValue aResult) {
202   if (!aObj) {
203     aResult.set(JS::NullValue());
204   } else if ([aObj isKindOfClass:[NSString class]]) {
205     nsAutoString strVal;
206     nsCocoaUtils::GetStringForNSString((NSString*)aObj, strVal);
207     if (!mozilla::dom::ToJSValue(aCx, strVal, aResult)) {
208       return NS_ERROR_FAILURE;
209     }
210   } else if ([aObj isKindOfClass:[NSNumber class]]) {
211     // If the type being held by the NSNumber is a BOOL set js value
212     // to boolean. Otherwise use a double value.
213     if (strcmp([(NSNumber*)aObj objCType], @encode(BOOL)) == 0) {
214       if (!mozilla::dom::ToJSValue(aCx, [(NSNumber*)aObj boolValue], aResult)) {
215         return NS_ERROR_FAILURE;
216       }
217     } else {
218       if (!mozilla::dom::ToJSValue(aCx, [(NSNumber*)aObj doubleValue],
219                                    aResult)) {
220         return NS_ERROR_FAILURE;
221       }
222     }
223   } else if ([aObj isKindOfClass:[NSValue class]] &&
224              strcmp([(NSValue*)aObj objCType], @encode(NSPoint)) == 0) {
225     NSPoint point = [(NSValue*)aObj pointValue];
226     return NSObjectToJsValue(
227         @[
228           [NSNumber numberWithDouble:point.x],
229           [NSNumber numberWithDouble:point.y]
230         ],
231         aCx, aResult);
232   } else if ([aObj isKindOfClass:[NSValue class]] &&
233              strcmp([(NSValue*)aObj objCType], @encode(NSSize)) == 0) {
234     NSSize size = [(NSValue*)aObj sizeValue];
235     return NSObjectToJsValue(
236         @[
237           [NSNumber numberWithDouble:size.width],
238           [NSNumber numberWithDouble:size.height]
239         ],
240         aCx, aResult);
241   } else if ([aObj isKindOfClass:[NSValue class]] &&
242              strcmp([(NSValue*)aObj objCType], @encode(NSRange)) == 0) {
243     NSRange range = [(NSValue*)aObj rangeValue];
244     return NSObjectToJsValue(@[ @(range.location), @(range.length) ], aCx,
245                              aResult);
246   } else if ([aObj isKindOfClass:[NSValue class]] &&
247              strcmp([(NSValue*)aObj objCType], @encode(NSRect)) == 0) {
248     NSRect rect = [(NSValue*)aObj rectValue];
249     return NSObjectToJsValue(@{
250       @"origin" : [NSValue valueWithPoint:rect.origin],
251       @"size" : [NSValue valueWithSize:rect.size]
252     },
253                              aCx, aResult);
254   } else if ([aObj isKindOfClass:[NSArray class]]) {
255     NSArray* objArr = (NSArray*)aObj;
257     JS::RootedVector<JS::Value> v(aCx);
258     if (!v.resize([objArr count])) {
259       return NS_ERROR_FAILURE;
260     }
261     for (size_t i = 0; i < [objArr count]; ++i) {
262       nsresult rv = NSObjectToJsValue(objArr[i], aCx, v[i]);
263       NS_ENSURE_SUCCESS(rv, rv);
264     }
266     JSObject* arrayObj = JS::NewArrayObject(aCx, v);
267     if (!arrayObj) {
268       return NS_ERROR_FAILURE;
269     }
270     aResult.setObject(*arrayObj);
271   } else if ([aObj isKindOfClass:[NSDictionary class]]) {
272     JS::RootedObject obj(aCx, JS_NewPlainObject(aCx));
273     for (NSString* key in aObj) {
274       nsAutoString strKey;
275       nsCocoaUtils::GetStringForNSString(key, strKey);
276       JS::RootedValue value(aCx);
277       nsresult rv = NSObjectToJsValue(aObj[key], aCx, &value);
278       NS_ENSURE_SUCCESS(rv, rv);
279       JS_SetUCProperty(aCx, obj, strKey.get(), strKey.Length(), value);
280     }
281     aResult.setObject(*obj);
282   } else if ([aObj isKindOfClass:[NSAttributedString class]]) {
283     NSAttributedString* attrStr = (NSAttributedString*)aObj;
284     __block NSMutableArray* attrRunArray = [[NSMutableArray alloc] init];
286     [attrStr
287         enumerateAttributesInRange:NSMakeRange(0, [attrStr length])
288                            options:
289                                NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
290                         usingBlock:^(NSDictionary* attributes, NSRange range,
291                                      BOOL* stop) {
292                           NSString* str =
293                               [[attrStr string] substringWithRange:range];
294                           if (!str || !attributes) {
295                             return;
296                           }
298                           NSMutableDictionary* attrRun =
299                               [attributes mutableCopy];
300                           attrRun[@"string"] = str;
302                           [attrRunArray addObject:attrRun];
303                         }];
305     // The attributed string is represented in js as an array of objects.
306     // Each object represents a run of text where the "string" property is the
307     // string value and all the AX* properties are the attributes.
308     return NSObjectToJsValue(attrRunArray, aCx, aResult);
309   } else if (CFGetTypeID(aObj) == CGColorGetTypeID()) {
310     const CGFloat* components = CGColorGetComponents((CGColorRef)aObj);
311     NSString* hexString = [NSString
312         stringWithFormat:@"#%02x%02x%02x", (int)(components[0] * 0xff),
313                          (int)(components[1] * 0xff),
314                          (int)(components[2] * 0xff)];
315     return NSObjectToJsValue(hexString, aCx, aResult);
316   } else if ([aObj respondsToSelector:@selector(isAccessibilityElement)]) {
317     // We expect all of our accessibility objects to implement
318     // isAccessibilityElement at the very least. If it is implemented we will
319     // assume its an accessibility object.
320     nsCOMPtr<nsIAccessibleMacInterface> obj =
321         new xpcAccessibleMacInterface(aObj);
322     return nsContentUtils::WrapNative(
323         aCx, obj, &NS_GET_IID(nsIAccessibleMacInterface), aResult);
324   } else {
325     // If this is any other kind of NSObject, just wrap it and return it.
326     // It will be opaque and immutable on the JS side, but it can be
327     // brought back to us in an argument.
328     nsCOMPtr<nsIAccessibleMacNSObjectWrapper> obj =
329         new xpcAccessibleMacNSObjectWrapper(aObj);
330     return nsContentUtils::WrapNative(
331         aCx, obj, &NS_GET_IID(nsIAccessibleMacNSObjectWrapper), aResult);
332   }
334   return NS_OK;
337 id xpcAccessibleMacInterface::JsValueToNSObject(JS::HandleValue aValue,
338                                                 JSContext* aCx,
339                                                 nsresult* aResult) {
340   *aResult = NS_OK;
341   if (aValue.isInt32()) {
342     return [NSNumber numberWithInteger:aValue.toInt32()];
343   } else if (aValue.isBoolean()) {
344     return [NSNumber numberWithBool:aValue.toBoolean()];
345   } else if (aValue.isString()) {
346     nsAutoJSString temp;
347     if (!temp.init(aCx, aValue)) {
348       NS_WARNING("cannot init string with given value");
349       *aResult = NS_ERROR_FAILURE;
350       return nil;
351     }
352     return nsCocoaUtils::ToNSString(temp);
353   } else if (aValue.isObject()) {
354     JS::Rooted<JSObject*> obj(aCx, aValue.toObjectOrNull());
356     bool isArray;
357     JS::IsArrayObject(aCx, obj, &isArray);
358     if (isArray) {
359       // If this is an array, we construct an NSArray and insert the js
360       // array's elements by recursively calling this function.
361       uint32_t len;
362       JS::GetArrayLength(aCx, obj, &len);
363       NSMutableArray* array = [NSMutableArray arrayWithCapacity:len];
364       for (uint32_t i = 0; i < len; i++) {
365         JS::RootedValue v(aCx);
366         JS_GetElement(aCx, obj, i, &v);
367         [array addObject:JsValueToNSObject(v, aCx, aResult)];
368         NS_ENSURE_SUCCESS(*aResult, nil);
369       }
370       return array;
371     }
373     bool hasValueType;
374     bool hasValue;
375     JS_HasOwnProperty(aCx, obj, "valueType", &hasValueType);
376     JS_HasOwnProperty(aCx, obj, "value", &hasValue);
377     if (hasValueType && hasValue) {
378       // A js object representin an NSValue looks like this:
379       // { valueType: "NSRange", value: [1, 3] }
380       return JsValueToNSValue(obj, aCx, aResult);
381     }
383     bool hasObjectType;
384     bool hasObject;
385     JS_HasOwnProperty(aCx, obj, "objectType", &hasObjectType);
386     JS_HasOwnProperty(aCx, obj, "object", &hasObject);
387     if (hasObjectType && hasObject) {
388       // A js object representing an NSDictionary looks like this:
389       // { objectType: "NSDictionary", value: {k: v, k: v, ...} }
390       return JsValueToSpecifiedNSObject(obj, aCx, aResult);
391     }
393     // This may be another nsIAccessibleMacInterface instance.
394     // If so, return the wrapped NSObject.
395     nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
397     nsCOMPtr<nsIXPConnectWrappedNative> wrappedObj;
398     nsresult rv =
399         xpc->GetWrappedNativeOfJSObject(aCx, obj, getter_AddRefs(wrappedObj));
400     NS_ENSURE_SUCCESS(rv, nil);
401     nsCOMPtr<nsIAccessibleMacNSObjectWrapper> macObjIface =
402         do_QueryInterface(wrappedObj->Native());
403     return macObjIface->GetNativeObject();
404   }
406   *aResult = NS_ERROR_FAILURE;
407   return nil;
410 id xpcAccessibleMacInterface::JsValueToNSValue(JS::HandleObject aObject,
411                                                JSContext* aCx,
412                                                nsresult* aResult) {
413   *aResult = NS_ERROR_FAILURE;
414   JS::RootedValue valueTypeValue(aCx);
415   if (!JS_GetProperty(aCx, aObject, "valueType", &valueTypeValue)) {
416     NS_WARNING("Could not get valueType");
417     return nil;
418   }
420   JS::RootedValue valueValue(aCx);
421   if (!JS_GetProperty(aCx, aObject, "value", &valueValue)) {
422     NS_WARNING("Could not get value");
423     return nil;
424   }
426   nsAutoJSString valueType;
427   if (!valueTypeValue.isString() || !valueType.init(aCx, valueTypeValue)) {
428     NS_WARNING("valueType is not a string");
429     return nil;
430   }
432   bool isArray;
433   JS::IsArrayObject(aCx, valueValue, &isArray);
434   if (!isArray) {
435     NS_WARNING("value is not an array");
436     return nil;
437   }
439   JS::Rooted<JSObject*> value(aCx, valueValue.toObjectOrNull());
441   if (valueType.EqualsLiteral("NSRange")) {
442     uint32_t len;
443     JS::GetArrayLength(aCx, value, &len);
444     if (len != 2) {
445       NS_WARNING("Expected a 2 member array");
446       return nil;
447     }
449     JS::RootedValue locationValue(aCx);
450     JS_GetElement(aCx, value, 0, &locationValue);
451     JS::RootedValue lengthValue(aCx);
452     JS_GetElement(aCx, value, 1, &lengthValue);
453     if (!locationValue.isInt32() || !lengthValue.isInt32()) {
454       NS_WARNING("Expected an array of integers");
455       return nil;
456     }
458     *aResult = NS_OK;
459     return [NSValue valueWithRange:NSMakeRange(locationValue.toInt32(),
460                                                lengthValue.toInt32())];
461   }
463   return nil;
466 id xpcAccessibleMacInterface::JsValueToSpecifiedNSObject(
467     JS::HandleObject aObject, JSContext* aCx, nsresult* aResult) {
468   *aResult = NS_ERROR_FAILURE;
469   JS::RootedValue objectTypeValue(aCx);
470   if (!JS_GetProperty(aCx, aObject, "objectType", &objectTypeValue)) {
471     NS_WARNING("Could not get objectType");
472     return nil;
473   }
475   JS::RootedValue objectValue(aCx);
476   if (!JS_GetProperty(aCx, aObject, "object", &objectValue)) {
477     NS_WARNING("Could not get object");
478     return nil;
479   }
481   nsAutoJSString objectType;
482   if (!objectTypeValue.isString()) {
483     NS_WARNING("objectType is not a string");
484     return nil;
485   }
487   if (!objectType.init(aCx, objectTypeValue)) {
488     NS_WARNING("cannot init string with object type");
489     return nil;
490   }
492   bool isObject = objectValue.isObjectOrNull();
493   if (!isObject) {
494     NS_WARNING("object is not a JSON object");
495     return nil;
496   }
498   JS::Rooted<JSObject*> object(aCx, objectValue.toObjectOrNull());
500   if (objectType.EqualsLiteral("NSDictionary")) {
501     JS::Rooted<JS::IdVector> ids(aCx, JS::IdVector(aCx));
502     if (!JS_Enumerate(aCx, object, &ids)) {
503       NS_WARNING("Unable to get keys from dictionary object");
504       return nil;
505     }
507     NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
509     for (size_t i = 0, n = ids.length(); i < n; i++) {
510       nsresult rv = NS_OK;
511       // get current key
512       JS::RootedValue currentKey(aCx);
513       JS_IdToValue(aCx, ids[i], &currentKey);
514       id unwrappedKey = JsValueToNSObject(currentKey, aCx, &rv);
515       NS_ENSURE_SUCCESS(rv, nil);
516       MOZ_ASSERT([unwrappedKey isKindOfClass:[NSString class]]);
518       // get associated value for current key
519       JS::RootedValue currentValue(aCx);
520       JS_GetPropertyById(aCx, object, ids[i], &currentValue);
521       id unwrappedValue = JsValueToNSObject(currentValue, aCx, &rv);
522       NS_ENSURE_SUCCESS(rv, nil);
523       dict[unwrappedKey] = unwrappedValue;
524     }
526     *aResult = NS_OK;
527     return dict;
528   }
530   return nil;
533 NS_IMPL_ISUPPORTS(xpcAccessibleMacEvent, nsIAccessibleMacEvent)
535 xpcAccessibleMacEvent::xpcAccessibleMacEvent(id aNativeObj, id aData)
536     : mNativeObject(aNativeObj), mData(aData) {
537   [mNativeObject retain];
538   [mData retain];
541 xpcAccessibleMacEvent::~xpcAccessibleMacEvent() {
542   [mNativeObject release];
543   [mData release];
546 NS_IMETHODIMP
547 xpcAccessibleMacEvent::GetMacIface(nsIAccessibleMacInterface** aMacIface) {
548   RefPtr<xpcAccessibleMacInterface> macIface =
549       new xpcAccessibleMacInterface(mNativeObject);
550   macIface.forget(aMacIface);
551   return NS_OK;
554 NS_IMETHODIMP
555 xpcAccessibleMacEvent::GetData(JSContext* aCx, JS::MutableHandleValue aData) {
556   return xpcAccessibleMacInterface::NSObjectToJsValue(mData, aCx, aData);
559 void xpcAccessibleMacEvent::FireEvent(id aNativeObj, id aNotification,
560                                       id aUserInfo) {
561   if (nsCOMPtr<nsIObserverService> obsService =
562           services::GetObserverService()) {
563     nsCOMPtr<nsISimpleEnumerator> observers;
564     // Get all observers for the mac event topic.
565     obsService->EnumerateObservers(NS_ACCESSIBLE_MAC_EVENT_TOPIC,
566                                    getter_AddRefs(observers));
567     if (observers) {
568       bool hasObservers = false;
569       observers->HasMoreElements(&hasObservers);
570       // If we have observers, notify them.
571       if (hasObservers) {
572         nsCOMPtr<nsIAccessibleMacEvent> xpcIface =
573             new xpcAccessibleMacEvent(aNativeObj, aUserInfo);
574         nsAutoString notificationStr;
575         nsCocoaUtils::GetStringForNSString(aNotification, notificationStr);
576         obsService->NotifyObservers(xpcIface, NS_ACCESSIBLE_MAC_EVENT_TOPIC,
577                                     notificationStr.get());
578       }
579     }
580   }