| // Copyright 2017 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/accessibility/internal/live_region.h" |
| |
| #include <bitset> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/string_split.h" |
| #include "cobalt/base/tokens.h" |
| #include "cobalt/dom/element.h" |
| |
| namespace cobalt { |
| namespace accessibility { |
| namespace internal { |
| namespace { |
| typedef std::bitset<8> RelevantMutationsBitset; |
| |
| // Default value for the aria-relevant property per the spec. |
| // https://www.w3.org/TR/wai-aria/states_and_properties#aria-relevant |
| const char kDefaultAriaRelevantValue[] = "additions text"; |
| |
| bool HasValidLiveRegionProperty(const scoped_refptr<dom::Element>& element) { |
| std::string aria_live_attribute = |
| element->GetAttribute(base::Tokens::aria_live().c_str()).value_or(""); |
| |
| return aria_live_attribute == base::Tokens::assertive() || |
| aria_live_attribute == base::Tokens::polite(); |
| } |
| |
| RelevantMutationsBitset GetRelevantMutations( |
| const scoped_refptr<dom::Element>& element) { |
| RelevantMutationsBitset bitset; |
| |
| std::string aria_relevant_attribute = |
| element->GetAttribute(base::Tokens::aria_relevant().c_str()) |
| .value_or(kDefaultAriaRelevantValue); |
| std::vector<std::string> tokens; |
| base::SplitStringAlongWhitespace(aria_relevant_attribute, &tokens); |
| for (size_t i = 0; i < tokens.size(); ++i) { |
| if (tokens[i] == base::Tokens::additions()) { |
| bitset.set(LiveRegion::kMutationTypeAddition); |
| } else if (tokens[i] == base::Tokens::removals()) { |
| bitset.set(LiveRegion::kMutationTypeRemoval); |
| } else if (tokens[i] == base::Tokens::text()) { |
| bitset.set(LiveRegion::kMutationTypeText); |
| } else if (tokens[i] == base::Tokens::all()) { |
| bitset.set(); |
| } else { |
| DLOG(WARNING) << "Unexpected value for aria-relevant attribute: " |
| << tokens[i]; |
| } |
| } |
| return bitset; |
| } |
| } // namespace |
| |
| // static |
| scoped_ptr<LiveRegion> LiveRegion::GetLiveRegionForNode( |
| const scoped_refptr<dom::Node>& node) { |
| if (!node) { |
| return make_scoped_ptr<LiveRegion>(NULL); |
| } |
| scoped_refptr<dom::Element> element = node->AsElement(); |
| if (element && HasValidLiveRegionProperty(element)) { |
| return make_scoped_ptr(new LiveRegion(element)); |
| } |
| return GetLiveRegionForNode(node->parent_node()); |
| } |
| |
| bool LiveRegion::IsAssertive() const { |
| base::optional<std::string> aria_live_attribute = |
| root_->GetAttribute(base::Tokens::aria_live().c_str()); |
| if (!aria_live_attribute) { |
| NOTREACHED(); |
| return false; |
| } |
| return *aria_live_attribute == base::Tokens::assertive(); |
| } |
| |
| bool LiveRegion::IsMutationRelevant(MutationType mutation_type) const { |
| RelevantMutationsBitset bitset = GetRelevantMutations(root_); |
| return bitset.test(mutation_type); |
| } |
| |
| bool LiveRegion::IsAtomic(const scoped_refptr<dom::Node>& node) const { |
| // Stop searching if we go past the live region's root node. The default is |
| // non-atomic. |
| if (!node || node == root_->parent_node()) { |
| return false; |
| } |
| if (node->IsElement()) { |
| scoped_refptr<dom::Element> element = node->AsElement(); |
| // Search ancestors of the changed element to determine if this change is |
| // atomic or not, per the algorithm described in the spec. |
| // https://www.w3.org/TR/wai-aria/states_and_properties#aria-atomic |
| base::optional<std::string> aria_atomic_attribute = |
| element->GetAttribute(base::Tokens::aria_atomic().c_str()); |
| if (aria_atomic_attribute) { |
| if (*aria_atomic_attribute == base::Tokens::true_token()) { |
| return true; |
| } else if (*aria_atomic_attribute == base::Tokens::false_token()) { |
| return false; |
| } else { |
| DLOG(WARNING) << "Unexpected token for aria-atomic: " |
| << *aria_atomic_attribute; |
| } |
| } |
| } |
| return IsAtomic(node->parent_node()); |
| } |
| |
| } // namespace internal |
| } // namespace accessibility |
| } // namespace cobalt |