MacUtils.mm (6304B)
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/. */ 7 8 #import "MacUtils.h" 9 #include "mozAccessible.h" 10 11 #include "LocalAccessible.h" 12 #include "DocAccessible.h" 13 #include "DocAccessibleParent.h" 14 #include "nsCocoaUtils.h" 15 #include "mozilla/a11y/PDocAccessible.h" 16 17 namespace mozilla { 18 namespace a11y { 19 namespace utils { 20 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; 27 28 Accessible::TranslateString(aString, text); 29 30 return text.IsEmpty() ? nil : nsCocoaUtils::ToNSString(text); 31 } 32 33 NSString* GetAccAttr(mozAccessible* aNativeAccessible, nsAtom* aAttrName) { 34 nsAutoString result; 35 Accessible* acc = [aNativeAccessible geckoAccessible]; 36 RefPtr<AccAttributes> attributes = acc->Attributes(); 37 38 if (!attributes) { 39 return nil; 40 } 41 42 attributes->GetAttribute(aAttrName, result); 43 44 if (!result.IsEmpty()) { 45 return nsCocoaUtils::ToNSString(result); 46 } 47 48 return nil; 49 } 50 51 bool DocumentExists(Accessible* aDoc, uintptr_t aDocPtr) { 52 if (reinterpret_cast<uintptr_t>(aDoc) == aDocPtr) { 53 return true; 54 } 55 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 } 73 74 return false; 75 } 76 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]; 82 } 83 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 } 95 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::background_color) { 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 if (iter.Name() == nsGkAtoms::mark) { 139 if (auto value = iter.Value<bool>()) { 140 if (*value) { 141 [attrDict setObject:@YES forKey:@"AXHighlight"]; 142 } 143 } 144 } else { 145 nsAutoString valueStr; 146 iter.ValueAsString(valueStr); 147 nsAutoString keyStr; 148 iter.NameAsString(keyStr); 149 [attrDict setObject:nsCocoaUtils::ToNSString(valueStr) 150 forKey:nsCocoaUtils::ToNSString(keyStr)]; 151 } 152 } 153 154 mozAccessible* container = GetNativeFromGeckoAccessible(aContainer); 155 id<MOXAccessible> link = 156 [container moxFindAncestor:^BOOL(id<MOXAccessible> moxAcc, BOOL* stop) { 157 return [[moxAcc moxRole] isEqualToString:NSAccessibilityLinkRole]; 158 }]; 159 if (link) { 160 [attrDict setObject:link forKey:@"AXLink"]; 161 } 162 163 id<MOXAccessible> heading = 164 [container moxFindAncestor:^BOOL(id<MOXAccessible> moxAcc, BOOL* stop) { 165 return [[moxAcc moxRole] isEqualToString:@"AXHeading"]; 166 }]; 167 if (heading) { 168 [attrDict setObject:[heading moxValue] forKey:@"AXHeadingLevel"]; 169 } 170 171 return attrDict; 172 } 173 174 NSScreen* GetNSScreenForAcc(mozAccessible* aAcc) { 175 if (!aAcc) { 176 return nil; 177 } 178 179 NSWindow* window = [aAcc moxWindow]; 180 NSScreen* screen = window ? [window screen] : nil; 181 if (!screen) { 182 // default to the main screen if we can't find one 183 // specific to the window 184 screen = [[NSScreen screens] objectAtIndex:0]; 185 } 186 187 return screen; 188 } 189 } // namespace utils 190 } // namespace a11y 191 } // namespace mozilla