Bug 1850635 [wpt PR 41704] - Implement parseHTMLUnsafe and setHTMLUnsafe, a=testonly
[gecko.git] / accessible / mac / MacUtils.mm
blob5534e8fcc8b659ccf56a11d6e2bbdf4e45b5ed2e
1 /* clang-format off */
2 /* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* clang-format on */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #import "MacUtils.h"
9 #include "mozAccessible.h"
11 #include "LocalAccessible.h"
12 #include "DocAccessible.h"
13 #include "DocAccessibleParent.h"
14 #include "nsCocoaUtils.h"
15 #include "mozilla/a11y/PDocAccessible.h"
17 namespace mozilla {
18 namespace a11y {
19 namespace utils {
21 /**
22  * Get a localized string from the a11y string bundle.
23  * Return nil if not found.
24  */
25 NSString* LocalizedString(const nsString& aString) {
26   nsString text;
28   Accessible::TranslateString(aString, text);
30   return text.IsEmpty() ? nil : nsCocoaUtils::ToNSString(text);
33 NSString* GetAccAttr(mozAccessible* aNativeAccessible, nsAtom* aAttrName) {
34   nsAutoString result;
35   Accessible* acc = [aNativeAccessible geckoAccessible];
36   RefPtr<AccAttributes> attributes = acc->Attributes();
38   if (!attributes) {
39     return nil;
40   }
42   attributes->GetAttribute(aAttrName, result);
44   if (!result.IsEmpty()) {
45     return nsCocoaUtils::ToNSString(result);
46   }
48   return nil;
51 bool DocumentExists(Accessible* aDoc, uintptr_t aDocPtr) {
52   if (reinterpret_cast<uintptr_t>(aDoc) == aDocPtr) {
53     return true;
54   }
56   if (aDoc->IsLocal()) {
57     DocAccessible* docAcc = aDoc->AsLocal()->AsDoc();
58     uint32_t docCount = docAcc->ChildDocumentCount();
59     for (uint32_t i = 0; i < docCount; i++) {
60       if (DocumentExists(docAcc->GetChildDocumentAt(i), aDocPtr)) {
61         return true;
62       }
63     }
64   } else {
65     DocAccessibleParent* docProxy = aDoc->AsRemote()->AsDoc();
66     size_t docCount = docProxy->ChildDocCount();
67     for (uint32_t i = 0; i < docCount; i++) {
68       if (DocumentExists(docProxy->ChildDocAt(i), aDocPtr)) {
69         return true;
70       }
71     }
72   }
74   return false;
77 static NSColor* ColorFromColor(const Color& aColor) {
78   return [NSColor colorWithCalibratedRed:NS_GET_R(aColor.mValue) / 255.0
79                                    green:NS_GET_G(aColor.mValue) / 255.0
80                                     blue:NS_GET_B(aColor.mValue) / 255.0
81                                    alpha:1.0];
84 NSDictionary* StringAttributesFromAccAttributes(AccAttributes* aAttributes,
85                                                 Accessible* aContainer) {
86   if (!aAttributes) {
87     if (mozAccessible* mozAcc = GetNativeFromGeckoAccessible(aContainer)) {
88       // If we don't have attributes provided this is probably a control like
89       // a button or empty entry. Just provide the accessible as an
90       // AXAttachment.
91       return @{@"AXAttachment" : mozAcc};
92     }
93     return @{};
94   }
96   NSMutableDictionary* attrDict =
97       [NSMutableDictionary dictionaryWithCapacity:aAttributes->Count()];
98   NSMutableDictionary* fontAttrDict = [[NSMutableDictionary alloc] init];
99   [attrDict setObject:fontAttrDict forKey:@"AXFont"];
100   for (auto iter : *aAttributes) {
101     if (iter.Name() == nsGkAtoms::backgroundColor) {
102       if (Maybe<Color> value = iter.Value<Color>()) {
103         NSColor* color = ColorFromColor(*value);
104         [attrDict setObject:(__bridge id)color.CGColor
105                      forKey:@"AXBackgroundColor"];
106       }
107     } else if (iter.Name() == nsGkAtoms::color) {
108       if (Maybe<Color> value = iter.Value<Color>()) {
109         NSColor* color = ColorFromColor(*value);
110         [attrDict setObject:(__bridge id)color.CGColor
111                      forKey:@"AXForegroundColor"];
112       }
113     } else if (iter.Name() == nsGkAtoms::font_size) {
114       if (Maybe<FontSize> pointSize = iter.Value<FontSize>()) {
115         int32_t fontPixelSize = static_cast<int32_t>(pointSize->mValue * 4 / 3);
116         [fontAttrDict setObject:@(fontPixelSize) forKey:@"AXFontSize"];
117       }
118     } else if (iter.Name() == nsGkAtoms::font_family) {
119       nsAutoString fontFamily;
120       iter.ValueAsString(fontFamily);
121       [fontAttrDict setObject:nsCocoaUtils::ToNSString(fontFamily)
122                        forKey:@"AXFontFamily"];
123     } else if (iter.Name() == nsGkAtoms::textUnderlineColor) {
124       [attrDict setObject:@1 forKey:@"AXUnderline"];
125       if (Maybe<Color> value = iter.Value<Color>()) {
126         NSColor* color = ColorFromColor(*value);
127         [attrDict setObject:(__bridge id)color.CGColor
128                      forKey:@"AXUnderlineColor"];
129       }
130     } else if (iter.Name() == nsGkAtoms::invalid) {
131       // XXX: There is currently no attribute for grammar
132       if (auto value = iter.Value<RefPtr<nsAtom>>()) {
133         if (*value == nsGkAtoms::spelling) {
134           [attrDict setObject:@YES
135                        forKey:NSAccessibilityMarkedMisspelledTextAttribute];
136         }
137       }
138     } else {
139       nsAutoString valueStr;
140       iter.ValueAsString(valueStr);
141       nsAutoString keyStr;
142       iter.NameAsString(keyStr);
143       [attrDict setObject:nsCocoaUtils::ToNSString(valueStr)
144                    forKey:nsCocoaUtils::ToNSString(keyStr)];
145     }
146   }
148   mozAccessible* container = GetNativeFromGeckoAccessible(aContainer);
149   id<MOXAccessible> link =
150       [container moxFindAncestor:^BOOL(id<MOXAccessible> moxAcc, BOOL* stop) {
151         return [[moxAcc moxRole] isEqualToString:NSAccessibilityLinkRole];
152       }];
153   if (link) {
154     [attrDict setObject:link forKey:@"AXLink"];
155   }
157   id<MOXAccessible> heading =
158       [container moxFindAncestor:^BOOL(id<MOXAccessible> moxAcc, BOOL* stop) {
159         return [[moxAcc moxRole] isEqualToString:@"AXHeading"];
160       }];
161   if (heading) {
162     [attrDict setObject:[heading moxValue] forKey:@"AXHeadingLevel"];
163   }
165   return attrDict;
167 }  // namespace utils
168 }  // namespace a11y
169 }  // namespace mozilla