Commit message: None
CVE: CVE-2016-5158
CWE: CWE-190
Vulnerability description: Multiple integer overflows in the opj_tcd_init_tile function in tcd.c in OpenJPEG, as used in PDFium in Google Chrome before 53.0.2785.89 on Windows and OS X and before 53.0.2785.92 on Linux, allow remote attackers to cause a denial of service (heap-based buffer overflow) or possibly have unspecified other impact via crafted JPEG 2000 data.

Project: Chrome

void DumpAccessibilityTreeTest::AddDefaultFilters(
    std::vector<Filter>* filters) {


  AddFilter(filters, "ALERT*");
  AddFilter(filters, "ANIMATED*");
  AddFilter(filters, "BUSY");
  AddFilter(filters, "CHECKED");
  AddFilter(filters, "COLLAPSED");
  AddFilter(filters, "EXPANDED");
  AddFilter(filters, "FLOATING");
  AddFilter(filters, "FOCUSABLE");
  AddFilter(filters, "HASPOPUP");
  AddFilter(filters, "INVISIBLE");
  AddFilter(filters, "MARQUEED");
  AddFilter(filters, "MIXED");
  AddFilter(filters, "MOVEABLE");
  AddFilter(filters, "MULTISELECTABLE");
  AddFilter(filters, "PRESSED");
  AddFilter(filters, "PROTECTED");
  AddFilter(filters, "READONLY");
  AddFilter(filters, "SELECTED");
  AddFilter(filters, "SIZEABLE");
  AddFilter(filters, "TRAVERSED");
  AddFilter(filters, "UNAVAILABLE");
  AddFilter(filters, "IA2_STATE_ACTIVE");
  AddFilter(filters, "IA2_STATE_ARMED");
  AddFilter(filters, "IA2_STATE_CHECKABLE");
  AddFilter(filters, "IA2_STATE_DEFUNCT");
  AddFilter(filters, "IA2_STATE_HORIZONTAL");
  AddFilter(filters, "IA2_STATE_ICONIFIED");
  AddFilter(filters, "IA2_STATE_INVALID_ENTRY");
  AddFilter(filters, "IA2_STATE_MODAL");
  AddFilter(filters, "IA2_STATE_MULTI_LINE");
  AddFilter(filters, "IA2_STATE_PINNED");
  AddFilter(filters, "IA2_STATE_REQUIRED");
  AddFilter(filters, "IA2_STATE_STALE");
  AddFilter(filters, "IA2_STATE_TRANSIENT");
  AddFilter(filters, "FOCUSED", Filter::DENY);
  AddFilter(filters, "HOTTRACKED", Filter::DENY);
  AddFilter(filters, "OFFSCREEN", Filter::DENY);
  AddFilter(filters, "value='*'");
  AddFilter(filters, "value='http*'", Filter::DENY);
  AddFilter(filters, "layout-guess:*", Filter::ALLOW);


  AddFilter(filters, "check*");
  AddFilter(filters, "descript*");
  AddFilter(filters, "collapsed");
  AddFilter(filters, "haspopup");
  AddFilter(filters, "horizontal");
  AddFilter(filters, "invisible");
  AddFilter(filters, "multiline");
  AddFilter(filters, "multiselectable");
  AddFilter(filters, "protected");
  AddFilter(filters, "required");
  AddFilter(filters, "select*");
  AddFilter(filters, "visited");
  AddFilter(filters, "busy=true");
  AddFilter(filters, "valueForRange*");
  AddFilter(filters, "minValueForRange*");
  AddFilter(filters, "maxValueForRange*");
  AddFilter(filters, "hierarchicalLevel*");
  AddFilter(filters, "autoComplete*");
  AddFilter(filters, "restriction*");
  AddFilter(filters, "keyShortcuts*");
  AddFilter(filters, "activedescendantId*");
  AddFilter(filters, "controlsIds*");
  AddFilter(filters, "flowtoIds*");
  AddFilter(filters, "detailsIds*");
  AddFilter(filters, "invalidState=*");
  AddFilter(filters, "invalidState=false",
            Filter::DENY);  // Don't show false value
  AddFilter(filters, "roleDescription=*");
  AddFilter(filters, "errormessageId=*");


  AddFilter(filters, "AXValueAutofill*");
  AddFilter(filters, "AXAutocomplete*");


  AddFilter(filters, "hint=*");
  AddFilter(filters, "interesting", Filter::DENY);
  AddFilter(filters, "has_character_locations", Filter::DENY);
  AddFilter(filters, "has_image", Filter::DENY);


  AddFilter(filters, "*=''", Filter::DENY);
  AddFilter(filters, "name=*", Filter::ALLOW_EMPTY);
}

Project: Chrome

  void AddFilter(std::vector<Filter>* filters,
                 std::string filter,
                 Filter::Type type = Filter::ALLOW) {
    filters->push_back(Filter(base::ASCIIToUTF16(filter), type));
  }

Project: Chrome

  void RunAomTest(const base::FilePath::CharType* file_path) {
    base::FilePath test_path = GetTestFilePath("accessibility", "aom");
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(base::PathExists(test_path)) << test_path.LossyDisplayName();
    }
    base::FilePath aom_file = test_path.Append(base::FilePath(file_path));

    RunTest(aom_file, "accessibility/aom");
  }

Project: Chrome

  void RunAriaTest(const base::FilePath::CharType* file_path) {
    base::FilePath test_path = GetTestFilePath("accessibility", "aria");
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(base::PathExists(test_path)) << test_path.LossyDisplayName();
    }
    base::FilePath aria_file = test_path.Append(base::FilePath(file_path));

    RunTest(aria_file, "accessibility/aria");
  }

Project: Chrome

  void RunCSSTest(const base::FilePath::CharType* file_path) {
    base::FilePath test_path = GetTestFilePath("accessibility", "css");
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(base::PathExists(test_path)) << test_path.LossyDisplayName();
    }
    base::FilePath css_file = test_path.Append(base::FilePath(file_path));

    RunTest(css_file, "accessibility/css");
  }

Project: Chrome

  void RunHtmlTest(const base::FilePath::CharType* file_path) {
    base::FilePath test_path = GetTestFilePath("accessibility", "html");
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(base::PathExists(test_path)) << test_path.LossyDisplayName();
    }
    base::FilePath html_file = test_path.Append(base::FilePath(file_path));

    RunTest(html_file, "accessibility/html");
  }

Project: Chrome

  void RunLanguageDetectionTest(const base::FilePath::CharType* file_path) {
    base::FilePath test_path =
        GetTestFilePath("accessibility", "language-detection");
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(base::PathExists(test_path)) << test_path.LossyDisplayName();
    }
    base::FilePath language_detection_file =
        test_path.Append(base::FilePath(file_path));

    RunTest(language_detection_file, "accessibility/language-detection");
  }

Project: Chrome

  void RunRegressionTest(const base::FilePath::CharType* file_path) {
    base::FilePath test_path = GetTestFilePath("accessibility", "regression");
    base::FilePath test_file = test_path.Append(base::FilePath(file_path));

    RunTest(test_file, "accessibility/regression");
  }

Project: Chrome

void AXTree::AddObserver(AXTreeObserver* observer) {
  observers_.AddObserver(observer);
}

Project: Chrome

void CallIfAttributeValuesChanged(const std::vector<std::pair<K, V>>& pairs1,
                                  const std::vector<std::pair<K, V>>& pairs2,
                                  const V& empty_value,
                                  F callback) {
  if (KeyValuePairsKeysMatch(pairs1, pairs2)) {
    for (size_t i = 0; i < pairs1.size(); ++i) {
      if (pairs1[i].second != pairs2[i].second)
        callback(pairs1[i].first, pairs1[i].second, pairs2[i].second);
    }
    return;
  }

  auto map1 = MapFromKeyValuePairs(pairs1);
  auto map2 = MapFromKeyValuePairs(pairs2);
  for (size_t i = 0; i < pairs1.size(); ++i) {
    const auto& new_iter = map2.find(pairs1[i].first);
    if (pairs1[i].second != empty_value && new_iter == map2.end())
      callback(pairs1[i].first, pairs1[i].second, empty_value);
  }

  for (size_t i = 0; i < pairs2.size(); ++i) {
    const auto& iter = map1.find(pairs2[i].first);
    if (iter == map1.end())
      callback(pairs2[i].first, empty_value, pairs2[i].second);
    else if (iter->second != pairs2[i].second)
      callback(pairs2[i].first, iter->second, pairs2[i].second);
   }
 }

Project: Chrome

void AXTree::CallNodeChangeCallbacks(AXNode* node,
                                     const AXNodeData& old_data,
                                     const AXNodeData& new_data) {
  for (AXTreeObserver& observer : observers_)
    observer.OnNodeDataWillChange(this, old_data, new_data);

  if (old_data.role != new_data.role) {
    for (AXTreeObserver& observer : observers_)
      observer.OnRoleChanged(this, node, old_data.role, new_data.role);
  }

  if (old_data.state != new_data.state) {
    for (int32_t i = static_cast<int32_t>(ax::mojom::State::kNone) + 1;
         i <= static_cast<int32_t>(ax::mojom::State::kMaxValue); ++i) {
      ax::mojom::State state = static_cast<ax::mojom::State>(i);
      if (old_data.HasState(state) != new_data.HasState(state)) {
        for (AXTreeObserver& observer : observers_)
          observer.OnStateChanged(this, node, state, new_data.HasState(state));
      }
    }
  }

  auto string_callback = [this, node](ax::mojom::StringAttribute attr,
                                      const std::string& old_string,
                                      const std::string& new_string) {
    for (AXTreeObserver& observer : observers_) {
      observer.OnStringAttributeChanged(this, node, attr, old_string,
                                        new_string);
    }
  };
  CallIfAttributeValuesChanged(old_data.string_attributes,
                               new_data.string_attributes, std::string(),
                               string_callback);

  auto bool_callback = [this, node](ax::mojom::BoolAttribute attr,
                                    const bool& old_bool,
                                    const bool& new_bool) {
    for (AXTreeObserver& observer : observers_)
      observer.OnBoolAttributeChanged(this, node, attr, new_bool);
  };
  CallIfAttributeValuesChanged(old_data.bool_attributes,
                               new_data.bool_attributes, false, bool_callback);

  auto float_callback = [this, node](ax::mojom::FloatAttribute attr,
                                     const float& old_float,
                                     const float& new_float) {
    for (AXTreeObserver& observer : observers_)
      observer.OnFloatAttributeChanged(this, node, attr, old_float, new_float);
  };
  CallIfAttributeValuesChanged(old_data.float_attributes,
                               new_data.float_attributes, 0.0f, float_callback);

  auto int_callback = [this, node](ax::mojom::IntAttribute attr,
                                   const int& old_int, const int& new_int) {
    for (AXTreeObserver& observer : observers_)
      observer.OnIntAttributeChanged(this, node, attr, old_int, new_int);
  };
  CallIfAttributeValuesChanged(old_data.int_attributes, new_data.int_attributes,
                               0, int_callback);

  auto intlist_callback = [this, node](
                              ax::mojom::IntListAttribute attr,
                              const std::vector<int32_t>& old_intlist,
                              const std::vector<int32_t>& new_intlist) {
    for (AXTreeObserver& observer : observers_)
      observer.OnIntListAttributeChanged(this, node, attr, old_intlist,
                                         new_intlist);
  };
  CallIfAttributeValuesChanged(old_data.intlist_attributes,
                               new_data.intlist_attributes,
                               std::vector<int32_t>(), intlist_callback);

  auto stringlist_callback =
      [this, node](ax::mojom::StringListAttribute attr,
                   const std::vector<std::string>& old_stringlist,
                   const std::vector<std::string>& new_stringlist) {
        for (AXTreeObserver& observer : observers_)
          observer.OnStringListAttributeChanged(this, node, attr,
                                                old_stringlist, new_stringlist);
      };
  CallIfAttributeValuesChanged(old_data.stringlist_attributes,
                               new_data.stringlist_attributes,
                               std::vector<std::string>(), stringlist_callback);
}

Project: Chrome

void AXTree::ComputeSetSizePosInSetAndCache(const AXNode& node,
                                            const AXNode* ordered_set) {
  DCHECK(ordered_set);
  std::vector<const AXNode*> items;

  bool node_is_radio_button =
      (node.data().role == ax::mojom::Role::kRadioButton);

  PopulateOrderedSetItems(ordered_set, ordered_set, items,
                          node_is_radio_button);

  int32_t num_elements = 0;

  int32_t largest_assigned_set_size = 0;

  for (size_t i = 0; i < items.size(); ++i) {
    const AXNode* item = items[i];
    ordered_set_info_map_[item->id()] = OrderedSetInfo();
    int32_t pos_in_set_value = 0;

    pos_in_set_value = num_elements + 1;

    pos_in_set_value =
        std::max(pos_in_set_value,
                 item->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet));

    ordered_set_info_map_[item->id()].pos_in_set = pos_in_set_value;
    num_elements = pos_in_set_value;

    if (item->HasIntAttribute(ax::mojom::IntAttribute::kSetSize))
      largest_assigned_set_size =
          std::max(largest_assigned_set_size,
                   item->GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
  }


  int32_t ordered_set_candidate =
      ordered_set->GetIntAttribute(ax::mojom::IntAttribute::kSetSize);

  int32_t set_size_value = std::max(
      std::max(num_elements, largest_assigned_set_size), ordered_set_candidate);

  if (ordered_set_info_map_.find(ordered_set->id()) ==
      ordered_set_info_map_.end())
    ordered_set_info_map_[ordered_set->id()] = OrderedSetInfo();

  if (node.SetRoleMatchesItemRole(ordered_set) || ordered_set == &node)
    ordered_set_info_map_[ordered_set->id()].set_size = set_size_value;

  for (size_t j = 0; j < items.size(); ++j) {
    const AXNode* item = items[j];
    ordered_set_info_map_[item->id()].set_size = set_size_value;
  }
}

Project: Chrome

AXNode* AXTree::CreateNode(AXNode* parent,
                           int32_t id,
                           int32_t index_in_parent,
                           AXTreeUpdateState* update_state) {
  AXNode* new_node = new AXNode(this, parent, id, index_in_parent);
  id_map_[new_node->id()] = new_node;
  for (AXTreeObserver& observer : observers_) {
    if (update_state->IsChangedNode(new_node) &&
        !update_state->IsRemovedNode(new_node))
      observer.OnNodeCreated(this, new_node);
    else
      observer.OnNodeReparented(this, new_node);
  }
  return new_node;
}

Project: Chrome

bool AXTree::DeleteOldChildren(AXNode* node,
                               const std::vector<int32_t>& new_child_ids,
                               AXTreeUpdateState* update_state) {
  std::set<int32_t> new_child_id_set;
  for (size_t i = 0; i < new_child_ids.size(); ++i) {
    if (new_child_id_set.find(new_child_ids[i]) != new_child_id_set.end()) {
      error_ = base::StringPrintf("Node %d has duplicate child id %d",
                                  node->id(), new_child_ids[i]);
      return false;
    }
    new_child_id_set.insert(new_child_ids[i]);
  }

  const std::vector<AXNode*>& old_children = node->children();
  for (size_t i = 0; i < old_children.size(); ++i) {
    int old_id = old_children[i]->id();
    if (new_child_id_set.find(old_id) == new_child_id_set.end())
      DestroySubtree(old_children[i], update_state);
  }

  return true;
}

Project: Chrome

void AXTree::DestroyNodeAndSubtree(AXNode* node,
                                   AXTreeUpdateState* update_state) {
  AXNodeData empty_data;
  empty_data.id = node->id();
  UpdateReverseRelations(node, empty_data);

  const auto& table_info_entry = table_info_map_.find(node->id());
  if (table_info_entry != table_info_map_.end()) {
    delete table_info_entry->second;
    table_info_map_.erase(node->id());
  }

  for (AXTreeObserver& observer : observers_) {
    if (!update_state || !update_state->IsChangedNode(node))
      observer.OnNodeWillBeDeleted(this, node);
    else
      observer.OnNodeWillBeReparented(this, node);
  }
  id_map_.erase(node->id());
  for (int i = 0; i < node->child_count(); ++i)
    DestroyNodeAndSubtree(node->ChildAtIndex(i), update_state);
  if (update_state) {
    update_state->pending_nodes.erase(node);
    update_state->removed_node_ids.insert(node->id());
  }

  if (update_state && update_state->IsChangedNode(node)) {
    update_state->reparented_node_id_to_data.insert(
        std::make_pair(node->id(), node->TakeData()));
  }
  node->Destroy();
}

Project: Chrome

void AXTree::DestroySubtree(AXNode* node,
                            AXTreeUpdateState* update_state) {
  DCHECK(update_state);
  for (AXTreeObserver& observer : observers_) {
    if (!update_state->IsChangedNode(node))
      observer.OnSubtreeWillBeDeleted(this, node);
    else
      observer.OnSubtreeWillBeReparented(this, node);
  }
  DestroyNodeAndSubtree(node, update_state);
}

Project: Chrome

const std::set<AXTreeID> AXTree::GetAllChildTreeIds() const {
  std::set<AXTreeID> result;
  for (auto entry : child_tree_id_reverse_map_)
    result.insert(entry.first);
  return result;
}

Project: Chrome

AXNode* AXTree::GetFromId(int32_t id) const {
  auto iter = id_map_.find(id);
  return iter != id_map_.end() ? iter->second : nullptr;
}

Project: Chrome

int32_t AXTree::GetNextNegativeInternalNodeId() {
  int32_t return_value = next_negative_internal_node_id_;
  next_negative_internal_node_id_--;
  if (next_negative_internal_node_id_ > 0)
    next_negative_internal_node_id_ = -1;
  return return_value;
}

Project: Chrome

std::set<int32_t> AXTree::GetNodeIdsForChildTreeId(
    AXTreeID child_tree_id) const {
  const auto& result = child_tree_id_reverse_map_.find(child_tree_id);
  if (result != child_tree_id_reverse_map_.end())
    return result->second;
  return std::set<int32_t>();
}

Project: Chrome

int32_t AXTree::GetPosInSet(const AXNode& node, const AXNode* ordered_set) {
  if (ordered_set_info_map_.find(node.id()) == ordered_set_info_map_.end())
    ComputeSetSizePosInSetAndCache(node, ordered_set);
  return ordered_set_info_map_[node.id()].pos_in_set;
}

Project: Chrome

std::set<int32_t> AXTree::GetReverseRelations(ax::mojom::IntAttribute attr,
                                              int32_t dst_id) const {
  DCHECK(IsNodeIdIntAttribute(attr));

  const auto& attr_relations = int_reverse_relations_.find(attr);
  if (attr_relations != int_reverse_relations_.end()) {
    const auto& result = attr_relations->second.find(dst_id);
    if (result != attr_relations->second.end())
      return result->second;
  }
  return std::set<int32_t>();
}

Project: Chrome

std::set<int32_t> AXTree::GetReverseRelations(ax::mojom::IntListAttribute attr,
                                              int32_t dst_id) const {
  DCHECK(IsNodeIdIntListAttribute(attr));

  const auto& attr_relations = intlist_reverse_relations_.find(attr);
  if (attr_relations != intlist_reverse_relations_.end()) {
    const auto& result = attr_relations->second.find(dst_id);
    if (result != attr_relations->second.end())
      return result->second;
  }
  return std::set<int32_t>();
}

Project: Chrome

int32_t AXTree::GetSetSize(const AXNode& node, const AXNode* ordered_set) {
  if (ordered_set_info_map_.find(node.id()) == ordered_set_info_map_.end())
    ComputeSetSizePosInSetAndCache(node, ordered_set);
  return ordered_set_info_map_[node.id()].set_size;
}

Project: Chrome

AXTableInfo* AXTree::GetTableInfo(const AXNode* const_table_node) const {
  AXNode* table_node = const_cast<AXNode*>(const_table_node);
  AXTree* tree = const_cast<AXTree*>(this);

  DCHECK(table_node);
  const auto& cached = table_info_map_.find(table_node->id());
  if (cached != table_info_map_.end()) {
    AXTableInfo* table_info = cached->second;
    if (!table_info->valid()) {
      bool success = table_info->Update();
      if (!success) {
        delete table_info;
        table_info = nullptr;
        table_info_map_.erase(table_node->id());
      }
      for (AXTreeObserver& observer : observers_)
        observer.OnNodeChanged(tree, table_node);
    }
    return table_info;
  }

  AXTableInfo* table_info = AXTableInfo::Create(tree, table_node);
  if (!table_info)
    return nullptr;

  table_info_map_[table_node->id()] = table_info;
  for (AXTreeObserver& observer : observers_)
    observer.OnNodeChanged(tree, table_node);

  return table_info;
}

Project: Chrome

gfx::RectF AXTree::GetTreeBounds(const AXNode* node,
                                 bool* offscreen,
                                 bool clip_bounds) const {
  return RelativeToTreeBounds(node, gfx::RectF(), offscreen, clip_bounds);
}

Project: Chrome

bool AXTree::HasObserver(AXTreeObserver* observer) {
  return observers_.HasObserver(observer);
}

Project: Chrome

  bool IsChangedNode(const AXNode* node) {
    return changed_node_ids.find(node->id()) != changed_node_ids.end();
  }

Project: Chrome

  bool IsNewNode(const AXNode* node) {
    return new_nodes.find(node) != new_nodes.end();
  }

Project: Chrome

  bool IsRemovedNode(const AXNode* node) {
    return removed_node_ids.find(node->id()) != removed_node_ids.end();
  }

Project: Chrome

void AXTree::RemoveObserver(const AXTreeObserver* observer) {
  observers_.RemoveObserver(observer);
}

Project: Chrome

void AXTree::SetEnableExtraMacNodes(bool enabled) {
  DCHECK(enable_extra_mac_nodes_ != enabled);
  DCHECK_EQ(0U, table_info_map_.size());
  enable_extra_mac_nodes_ = enabled;
}

Project: Chrome

std::string AXTree::ToString() const {
  return "AXTree" + data_.ToString() + "\n" + TreeToStringHelper(root_, 0);
}

Project: Chrome

std::string TreeToStringHelper(AXNode* node, int indent) {
  std::string result = std::string(2 * indent, ' ');
  result += node->data().ToString() + "\n";
  for (int i = 0; i < node->child_count(); ++i)
    result += TreeToStringHelper(node->ChildAtIndex(i), indent + 1);
  return result;
}

Project: Chrome

bool AXTree::Unserialize(const AXTreeUpdate& update) {
  AXTreeUpdateState update_state;
  int32_t old_root_id = root_ ? root_->id() : 0;

  for (size_t i = 0; i < update.nodes.size(); ++i)
    update_state.changed_node_ids.insert(update.nodes[i].id);

  if (update.has_tree_data)
    UpdateData(update.tree_data);

  bool root_updated = false;
  if (update.node_id_to_clear != 0) {
    AXNode* node = GetFromId(update.node_id_to_clear);

    if (node && node == root_) {
      if (update.root_id != old_root_id) {
        AXNode* old_root = root_;
        root_ = nullptr;
        DestroySubtree(old_root, &update_state);
      } else {
        root_updated = true;
      }
    }

    if (node && root_ && (node != root_ || root_updated)) {
      for (int i = 0; i < node->child_count(); ++i)
        DestroySubtree(node->ChildAtIndex(i), &update_state);
      std::vector<AXNode*> children;
      node->SwapChildren(children);
      update_state.pending_nodes.insert(node);
    }
  }

  bool root_exists = GetFromId(update.root_id) != nullptr;
  for (size_t i = 0; i < update.nodes.size(); ++i) {
    bool is_new_root = !root_exists && update.nodes[i].id == update.root_id;
    if (!UpdateNode(update.nodes[i], is_new_root, &update_state))
      return false;
  }

  if (!root_) {
    error_ = "Tree has no root.";
    return false;
  }

  if (!update_state.pending_nodes.empty()) {
    error_ = "Nodes left pending by the update:";
    for (const AXNode* pending : update_state.pending_nodes)
      error_ += base::StringPrintf(" %d", pending->id());
    return false;
  }

  std::set<int32_t> table_ids_checked;
  for (size_t i = 0; i < update.nodes.size(); ++i) {
    AXNode* node = GetFromId(update.nodes[i].id);
    while (node) {
      if (table_ids_checked.find(node->id()) != table_ids_checked.end())
        break;
      const auto& table_info_entry = table_info_map_.find(node->id());
      if (table_info_entry != table_info_map_.end())
        table_info_entry->second->Invalidate();
      table_ids_checked.insert(node->id());
      node = node->parent();
    }
  }

  std::set<const AXNode*>& new_nodes = update_state.new_nodes;
  std::vector<AXTreeObserver::Change> changes;
  changes.reserve(update.nodes.size());
  for (size_t i = 0; i < update.nodes.size(); ++i) {
    AXNode* node = GetFromId(update.nodes[i].id);
    if (!node)
      continue;

    bool is_new_node = update_state.IsNewNode(node);
    bool is_reparented_node = update_state.IsReparentedNode(node);

    AXTreeObserver::ChangeType change = AXTreeObserver::NODE_CHANGED;
    if (is_new_node) {
      if (is_reparented_node) {
        bool is_subtree = !node->parent() ||
                          new_nodes.find(node->parent()) == new_nodes.end() ||
                          (node->parent() == root_ && root_updated);
        change = is_subtree ? AXTreeObserver::SUBTREE_REPARENTED
                            : AXTreeObserver::NODE_REPARENTED;
      } else {
        bool is_subtree = !node->parent() ||
                          new_nodes.find(node->parent()) == new_nodes.end() ||
                          update_state.IsRemovedNode(node->parent()) ||
                          (node->parent() == root_ && root_updated);
        change = is_subtree ? AXTreeObserver::SUBTREE_CREATED
                            : AXTreeObserver::NODE_CREATED;
      }
    }
    changes.push_back(AXTreeObserver::Change(node, change));
  }
  for (AXTreeObserver& observer : observers_) {
    observer.OnAtomicUpdateFinished(this, root_->id() != old_root_id, changes);
  }

  ordered_set_info_map_.clear();

  return true;
}

Project: Chrome

void AXTree::UpdateData(const AXTreeData& new_data) {
  if (data_ == new_data)
    return;

  AXTreeData old_data = data_;
  data_ = new_data;
  for (AXTreeObserver& observer : observers_)
    observer.OnTreeDataChanged(this, old_data, new_data);
}

Project: Chrome

bool AXTree::UpdateNode(const AXNodeData& src,
                        bool is_new_root,
                        AXTreeUpdateState* update_state) {

  AXNode* node = GetFromId(src.id);
  if (node) {
    update_state->pending_nodes.erase(node);

    if (!update_state->IsNewNode(node) ||
        update_state->IsReparentedNode(node)) {
      auto it = update_state->reparented_node_id_to_data.find(node->id());
      if (it != update_state->reparented_node_id_to_data.end())
        CallNodeChangeCallbacks(node, it->second, src);
      else
        CallNodeChangeCallbacks(node, node->data(), src);
    }
    UpdateReverseRelations(node, src);
    node->SetData(src);
  } else {
    if (!is_new_root) {
      error_ = base::StringPrintf(
          "%d is not in the tree and not the new root", src.id);
      return false;
    }

    update_state->new_root = CreateNode(nullptr, src.id, 0, update_state);
    node = update_state->new_root;
    update_state->new_nodes.insert(node);
    UpdateReverseRelations(node, src);
    node->SetData(src);
  }

  for (AXTreeObserver& observer : observers_)
    observer.OnNodeChanged(this, node);

  if (!DeleteOldChildren(node, src.child_ids, update_state)) {
    if (update_state->new_root) {
      AXNode* old_root = root_;
      root_ = nullptr;

      DestroySubtree(old_root, update_state);

      if (update_state->removed_node_ids.find(src.id) ==
              update_state->removed_node_ids.end() &&
          update_state->new_nodes.find(node) != update_state->new_nodes.end()) {
        DestroySubtree(node, update_state);
      }
    }
    return false;
  }

  std::vector<AXNode*> new_children;
  bool success = CreateNewChildVector(
      node, src.child_ids, &new_children, update_state);
  node->SwapChildren(new_children);

  if (is_new_root) {
    AXNode* old_root = root_;
    root_ = node;
    if (old_root && old_root != node)
      DestroySubtree(old_root, update_state);
  }

  return success;
}

Project: Chrome

void AXTree::UpdateReverseRelations(AXNode* node, const AXNodeData& new_data) {
  const AXNodeData& old_data = node->data();
  int id = new_data.id;
  auto int_callback = [this, id](ax::mojom::IntAttribute attr,
                                 const int& old_id, const int& new_id) {
    if (!IsNodeIdIntAttribute(attr))
      return;

    auto& map = int_reverse_relations_[attr];
    if (map.find(old_id) != map.end()) {
      map[old_id].erase(id);
      if (map[old_id].empty())
        map.erase(old_id);
    }

    if (new_id)
      map[new_id].insert(id);
  };
  CallIfAttributeValuesChanged(old_data.int_attributes, new_data.int_attributes,
                               0, int_callback);

  auto intlist_callback = [this, id](ax::mojom::IntListAttribute attr,
                                     const std::vector<int32_t>& old_idlist,
                                     const std::vector<int32_t>& new_idlist) {
    if (!IsNodeIdIntListAttribute(attr))
      return;

    auto& map = intlist_reverse_relations_[attr];
    for (int32_t old_id : old_idlist) {
      if (map.find(old_id) != map.end()) {
        map[old_id].erase(id);
        if (map[old_id].empty())
          map.erase(old_id);
      }
    }
    for (int32_t new_id : new_idlist)
      intlist_reverse_relations_[attr][new_id].insert(id);
  };
  CallIfAttributeValuesChanged(old_data.intlist_attributes,
                               new_data.intlist_attributes,
                               std::vector<int32_t>(), intlist_callback);

  auto string_callback = [this, id](ax::mojom::StringAttribute attr,
                                    const std::string& old_string,
                                    const std::string& new_string) {
    if (attr == ax::mojom::StringAttribute::kChildTreeId) {
      AXTreeID old_ax_tree_id = AXTreeID::FromString(old_string);
      if (child_tree_id_reverse_map_.find(old_ax_tree_id) !=
          child_tree_id_reverse_map_.end()) {
        child_tree_id_reverse_map_[old_ax_tree_id].erase(id);
        if (child_tree_id_reverse_map_[old_ax_tree_id].empty())
          child_tree_id_reverse_map_.erase(old_ax_tree_id);
      }

      if (!new_string.empty()) {
        AXTreeID new_ax_tree_id = AXTreeID::FromString(new_string);
        child_tree_id_reverse_map_[new_ax_tree_id].insert(id);
      }
    }
  };

  CallIfAttributeValuesChanged(old_data.string_attributes,
                               new_data.string_attributes, std::string(),
                               string_callback);
}