michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "AccessibleWrap.h" michael@0: michael@0: #include "Accessible2_i.c" michael@0: #include "Accessible2_2_i.c" michael@0: #include "AccessibleRole.h" michael@0: #include "AccessibleStates.h" michael@0: michael@0: #include "Compatibility.h" michael@0: #include "ia2AccessibleRelation.h" michael@0: #include "IUnknownImpl.h" michael@0: #include "nsCoreUtils.h" michael@0: #include "nsIAccessibleTypes.h" michael@0: #include "Relation.h" michael@0: michael@0: #include "nsIPersistentProperties2.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::a11y; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // ia2Accessible michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::QueryInterface(REFIID iid, void** ppv) michael@0: { michael@0: if (!ppv) michael@0: return E_INVALIDARG; michael@0: michael@0: *ppv = nullptr; michael@0: michael@0: if (IID_IAccessible2_2 == iid) michael@0: *ppv = static_cast(this); michael@0: else if (IID_IAccessible2 == iid && !Compatibility::IsIA2Off()) michael@0: *ppv = static_cast(this); michael@0: michael@0: if (*ppv) { michael@0: (reinterpret_cast(*ppv))->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: return E_NOINTERFACE; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // IAccessible2 michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_nRelations(long* aNRelations) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aNRelations) michael@0: return E_INVALIDARG; michael@0: *aNRelations = 0; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { michael@0: if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) michael@0: continue; michael@0: michael@0: Relation rel = acc->RelationByType(sRelationTypePairs[idx].first); michael@0: if (rel.Next()) michael@0: (*aNRelations)++; michael@0: } michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_relation(long aRelationIndex, michael@0: IAccessibleRelation** aRelation) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aRelation) michael@0: return E_INVALIDARG; michael@0: *aRelation = nullptr; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: long relIdx = 0; michael@0: for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { michael@0: if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) michael@0: continue; michael@0: michael@0: RelationType relationType = sRelationTypePairs[idx].first; michael@0: Relation rel = acc->RelationByType(relationType); michael@0: nsRefPtr ia2Relation = michael@0: new ia2AccessibleRelation(relationType, &rel); michael@0: if (ia2Relation->HasTargets()) { michael@0: if (relIdx == aRelationIndex) { michael@0: ia2Relation.forget(aRelation); michael@0: return S_OK; michael@0: } michael@0: michael@0: relIdx++; michael@0: } michael@0: } michael@0: michael@0: return E_INVALIDARG; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_relations(long aMaxRelations, michael@0: IAccessibleRelation** aRelation, michael@0: long *aNRelations) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aRelation || !aNRelations) michael@0: return E_INVALIDARG; michael@0: *aNRelations = 0; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) && michael@0: *aNRelations < aMaxRelations; idx++) { michael@0: if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) michael@0: continue; michael@0: michael@0: RelationType relationType = sRelationTypePairs[idx].first; michael@0: Relation rel = acc->RelationByType(relationType); michael@0: nsRefPtr ia2Rel = michael@0: new ia2AccessibleRelation(relationType, &rel); michael@0: if (ia2Rel->HasTargets()) { michael@0: ia2Rel.forget(aRelation + (*aNRelations)); michael@0: (*aNRelations)++; michael@0: } michael@0: } michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::role(long* aRole) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aRole) michael@0: return E_INVALIDARG; michael@0: *aRole = 0; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: #define ROLE(_geckoRole, stringRole, atkRole, macRole, \ michael@0: msaaRole, ia2Role, nameRule) \ michael@0: case roles::_geckoRole: \ michael@0: *aRole = ia2Role; \ michael@0: break; michael@0: michael@0: a11y::role geckoRole = acc->Role(); michael@0: switch (geckoRole) { michael@0: #include "RoleMap.h" michael@0: default: michael@0: MOZ_CRASH("Unknown role."); michael@0: }; michael@0: michael@0: #undef ROLE michael@0: michael@0: // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call michael@0: // the IA2 role a ROLE_OUTLINEITEM. michael@0: if (geckoRole == roles::ROW) { michael@0: Accessible* xpParent = acc->Parent(); michael@0: if (xpParent && xpParent->Role() == roles::TREE_TABLE) michael@0: *aRole = ROLE_SYSTEM_OUTLINEITEM; michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::scrollTo(enum IA2ScrollType aScrollType) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsCoreUtils::ScrollTo(acc->Document()->PresShell(), michael@0: acc->GetContent(), aScrollType); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::scrollToPoint(enum IA2CoordinateType aCoordType, michael@0: long aX, long aY) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ? michael@0: nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE : michael@0: nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; michael@0: michael@0: nsresult rv = acc->ScrollToPoint(geckoCoordType, aX, aY); michael@0: return GetHRESULT(rv); michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_groupPosition(long* aGroupLevel, michael@0: long* aSimilarItemsInGroup, michael@0: long* aPositionInGroup) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aGroupLevel || !aSimilarItemsInGroup || !aPositionInGroup) michael@0: return E_INVALIDARG; michael@0: michael@0: *aGroupLevel = 0; michael@0: *aSimilarItemsInGroup = 0; michael@0: *aPositionInGroup = 0; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: GroupPos groupPos = acc->GroupPosition(); michael@0: michael@0: // Group information for accessibles having level only (like html headings michael@0: // elements) isn't exposed by this method. AT should look for 'level' object michael@0: // attribute. michael@0: if (!groupPos.setSize && !groupPos.posInSet) michael@0: return S_FALSE; michael@0: michael@0: *aGroupLevel = groupPos.level; michael@0: *aSimilarItemsInGroup = groupPos.setSize; michael@0: *aPositionInGroup = groupPos.posInSet; michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_states(AccessibleStates* aStates) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aStates) michael@0: return E_INVALIDARG; michael@0: *aStates = 0; michael@0: michael@0: // XXX: bug 344674 should come with better approach that we have here. michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: uint64_t state = acc->State(); michael@0: michael@0: if (state & states::INVALID) michael@0: *aStates |= IA2_STATE_INVALID_ENTRY; michael@0: if (state & states::REQUIRED) michael@0: *aStates |= IA2_STATE_REQUIRED; michael@0: michael@0: // The following IA2 states are not supported by Gecko michael@0: // IA2_STATE_ARMED michael@0: // IA2_STATE_MANAGES_DESCENDANTS michael@0: // IA2_STATE_ICONIFIED michael@0: // IA2_STATE_INVALID // This is not a state, it is the absence of a state michael@0: michael@0: if (state & states::ACTIVE) michael@0: *aStates |= IA2_STATE_ACTIVE; michael@0: if (state & states::DEFUNCT) michael@0: *aStates |= IA2_STATE_DEFUNCT; michael@0: if (state & states::EDITABLE) michael@0: *aStates |= IA2_STATE_EDITABLE; michael@0: if (state & states::HORIZONTAL) michael@0: *aStates |= IA2_STATE_HORIZONTAL; michael@0: if (state & states::MODAL) michael@0: *aStates |= IA2_STATE_MODAL; michael@0: if (state & states::MULTI_LINE) michael@0: *aStates |= IA2_STATE_MULTI_LINE; michael@0: if (state & states::OPAQUE1) michael@0: *aStates |= IA2_STATE_OPAQUE; michael@0: if (state & states::SELECTABLE_TEXT) michael@0: *aStates |= IA2_STATE_SELECTABLE_TEXT; michael@0: if (state & states::SINGLE_LINE) michael@0: *aStates |= IA2_STATE_SINGLE_LINE; michael@0: if (state & states::STALE) michael@0: *aStates |= IA2_STATE_STALE; michael@0: if (state & states::SUPPORTS_AUTOCOMPLETION) michael@0: *aStates |= IA2_STATE_SUPPORTS_AUTOCOMPLETION; michael@0: if (state & states::TRANSIENT) michael@0: *aStates |= IA2_STATE_TRANSIENT; michael@0: if (state & states::VERTICAL) michael@0: *aStates |= IA2_STATE_VERTICAL; michael@0: if (state & states::CHECKED) michael@0: *aStates |= IA2_STATE_CHECKABLE; michael@0: if (state & states::PINNED) michael@0: *aStates |= IA2_STATE_PINNED; michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_extendedRole(BSTR* aExtendedRole) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aExtendedRole) michael@0: return E_INVALIDARG; michael@0: michael@0: *aExtendedRole = nullptr; michael@0: return E_NOTIMPL; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_localizedExtendedRole(BSTR* aLocalizedExtendedRole) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aLocalizedExtendedRole) michael@0: return E_INVALIDARG; michael@0: michael@0: *aLocalizedExtendedRole = nullptr; michael@0: return E_NOTIMPL; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_nExtendedStates(long* aNExtendedStates) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aNExtendedStates) michael@0: return E_INVALIDARG; michael@0: michael@0: *aNExtendedStates = 0; michael@0: return E_NOTIMPL; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_extendedStates(long aMaxExtendedStates, michael@0: BSTR** aExtendedStates, michael@0: long* aNExtendedStates) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aExtendedStates || !aNExtendedStates) michael@0: return E_INVALIDARG; michael@0: michael@0: *aExtendedStates = nullptr; michael@0: *aNExtendedStates = 0; michael@0: return E_NOTIMPL; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_localizedExtendedStates(long aMaxLocalizedExtendedStates, michael@0: BSTR** aLocalizedExtendedStates, michael@0: long* aNLocalizedExtendedStates) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aLocalizedExtendedStates || !aNLocalizedExtendedStates) michael@0: return E_INVALIDARG; michael@0: michael@0: *aLocalizedExtendedStates = nullptr; michael@0: *aNLocalizedExtendedStates = 0; michael@0: return E_NOTIMPL; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_uniqueID(long* aUniqueID) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aUniqueID) michael@0: return E_INVALIDARG; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: *aUniqueID = - reinterpret_cast(acc->UniqueID()); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_windowHandle(HWND* aWindowHandle) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aWindowHandle) michael@0: return E_INVALIDARG; michael@0: *aWindowHandle = 0; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: *aWindowHandle = AccessibleWrap::GetHWNDFor(acc); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_indexInParent(long* aIndexInParent) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aIndexInParent) michael@0: return E_INVALIDARG; michael@0: *aIndexInParent = -1; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: *aIndexInParent = acc->IndexInParent(); michael@0: if (*aIndexInParent == -1) michael@0: return S_FALSE; michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_locale(IA2Locale* aLocale) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aLocale) michael@0: return E_INVALIDARG; michael@0: michael@0: // Language codes consist of a primary code and a possibly empty series of michael@0: // subcodes: language-code = primary-code ( "-" subcode )* michael@0: // Two-letter primary codes are reserved for [ISO639] language abbreviations. michael@0: // Any two-letter subcode is understood to be a [ISO3166] country code. michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsAutoString lang; michael@0: acc->Language(lang); michael@0: michael@0: // If primary code consists from two letters then expose it as language. michael@0: int32_t offset = lang.FindChar('-', 0); michael@0: if (offset == -1) { michael@0: if (lang.Length() == 2) { michael@0: aLocale->language = ::SysAllocString(lang.get()); michael@0: return S_OK; michael@0: } michael@0: } else if (offset == 2) { michael@0: aLocale->language = ::SysAllocStringLen(lang.get(), 2); michael@0: michael@0: // If the first subcode consists from two letters then expose it as michael@0: // country. michael@0: offset = lang.FindChar('-', 3); michael@0: if (offset == -1) { michael@0: if (lang.Length() == 5) { michael@0: aLocale->country = ::SysAllocString(lang.get() + 3); michael@0: return S_OK; michael@0: } michael@0: } else if (offset == 5) { michael@0: aLocale->country = ::SysAllocStringLen(lang.get() + 3, 2); michael@0: } michael@0: } michael@0: michael@0: // Expose as a string if primary code or subcode cannot point to language or michael@0: // country abbreviations or if there are more than one subcode. michael@0: aLocale->variant = ::SysAllocString(lang.get()); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_attributes(BSTR* aAttributes) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aAttributes) michael@0: return E_INVALIDARG; michael@0: *aAttributes = nullptr; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: // The format is name:value;name:value; with \ for escaping these michael@0: // characters ":;=,\". michael@0: nsCOMPtr attributes = acc->Attributes(); michael@0: return ConvertToIA2Attributes(attributes, aAttributes); michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // IAccessible2_2 michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_attribute(BSTR name, VARIANT* aAttribute) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aAttribute) michael@0: return E_INVALIDARG; michael@0: michael@0: return E_NOTIMPL; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_accessibleWithCaret(IUnknown** aAccessible, michael@0: long* aCaretOffset) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aAccessible || !aCaretOffset) michael@0: return E_INVALIDARG; michael@0: michael@0: *aAccessible = nullptr; michael@0: *aCaretOffset = -1; michael@0: return E_NOTIMPL; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: ia2Accessible::get_relationTargetsOfType(BSTR aType, michael@0: long aMaxTargets, michael@0: IUnknown*** aTargets, michael@0: long* aNTargets) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!aTargets || !aNTargets) michael@0: return E_INVALIDARG; michael@0: *aNTargets = 0; michael@0: michael@0: Maybe relationType; michael@0: for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { michael@0: if (wcscmp(aType, sRelationTypePairs[idx].second) == 0) { michael@0: relationType.construct(sRelationTypePairs[idx].first); michael@0: break; michael@0: } michael@0: } michael@0: if (relationType.empty()) michael@0: return E_INVALIDARG; michael@0: michael@0: AccessibleWrap* acc = static_cast(this); michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Relation rel = acc->RelationByType(relationType.ref()); michael@0: michael@0: nsTArray targets; michael@0: Accessible* target = nullptr; michael@0: while ((target = rel.Next()) && michael@0: static_cast(targets.Length()) <= aMaxTargets) michael@0: targets.AppendElement(target); michael@0: michael@0: *aNTargets = targets.Length(); michael@0: *aTargets = static_cast( michael@0: ::CoTaskMemAlloc(sizeof(IUnknown*) * *aNTargets)); michael@0: if (!*aTargets) michael@0: return E_OUTOFMEMORY; michael@0: michael@0: for (int32_t i = 0; i < *aNTargets; i++) { michael@0: AccessibleWrap* target= static_cast(targets[i]); michael@0: (*aTargets)[i] = static_cast(target); michael@0: (*aTargets)[i]->AddRef(); michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Helpers michael@0: michael@0: HRESULT michael@0: ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes, michael@0: BSTR* aIA2Attributes) michael@0: { michael@0: *aIA2Attributes = nullptr; michael@0: michael@0: // The format is name:value;name:value; with \ for escaping these michael@0: // characters ":;=,\". michael@0: michael@0: if (!aAttributes) michael@0: return S_FALSE; michael@0: michael@0: nsCOMPtr propEnum; michael@0: aAttributes->Enumerate(getter_AddRefs(propEnum)); michael@0: if (!propEnum) michael@0: return E_FAIL; michael@0: michael@0: nsAutoString strAttrs; michael@0: michael@0: const char kCharsToEscape[] = ":;=,\\"; michael@0: michael@0: bool hasMore = false; michael@0: while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) { michael@0: nsCOMPtr propSupports; michael@0: propEnum->GetNext(getter_AddRefs(propSupports)); michael@0: michael@0: nsCOMPtr propElem(do_QueryInterface(propSupports)); michael@0: if (!propElem) michael@0: return E_FAIL; michael@0: michael@0: nsAutoCString name; michael@0: if (NS_FAILED(propElem->GetKey(name))) michael@0: return E_FAIL; michael@0: michael@0: int32_t offset = 0; michael@0: while ((offset = name.FindCharInSet(kCharsToEscape, offset)) != kNotFound) { michael@0: name.Insert('\\', offset); michael@0: offset += 2; michael@0: } michael@0: michael@0: nsAutoString value; michael@0: if (NS_FAILED(propElem->GetValue(value))) michael@0: return E_FAIL; michael@0: michael@0: offset = 0; michael@0: while ((offset = value.FindCharInSet(kCharsToEscape, offset)) != kNotFound) { michael@0: value.Insert('\\', offset); michael@0: offset += 2; michael@0: } michael@0: michael@0: AppendUTF8toUTF16(name, strAttrs); michael@0: strAttrs.Append(':'); michael@0: strAttrs.Append(value); michael@0: strAttrs.Append(';'); michael@0: } michael@0: michael@0: if (strAttrs.IsEmpty()) michael@0: return S_FALSE; michael@0: michael@0: *aIA2Attributes = ::SysAllocStringLen(strAttrs.get(), strAttrs.Length()); michael@0: return *aIA2Attributes ? S_OK : E_OUTOFMEMORY; michael@0: }