1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/frame_host/frame_accessibility.h"
6
7#include "content/browser/frame_host/frame_tree.h"
8#include "content/browser/frame_host/frame_tree_node.h"
9#include "content/browser/frame_host/render_frame_host_delegate.h"
10#include "content/browser/frame_host/render_frame_host_impl.h"
11#include "content/public/browser/browser_context.h"
12
13namespace content {
14
15// static
16FrameAccessibility* FrameAccessibility::GetInstance() {
17  return Singleton<FrameAccessibility>::get();
18}
19
20FrameAccessibility::ChildFrameMapping::ChildFrameMapping()
21    : parent_frame_host(NULL),
22      accessibility_node_id(0),
23      child_frame_tree_node_id(0),
24      browser_plugin_instance_id(0) {}
25
26FrameAccessibility::FrameAccessibility() {}
27
28FrameAccessibility::~FrameAccessibility() {}
29
30void FrameAccessibility::AddChildFrame(
31    RenderFrameHostImpl* parent_frame_host,
32    int accessibility_node_id,
33    int64 child_frame_tree_node_id) {
34  for (std::vector<ChildFrameMapping>::iterator iter = mappings_.begin();
35       iter != mappings_.end();
36       ++iter) {
37    // TODO(dmazzoni): the renderer should keep track of these mappings
38    // and clear an existing mapping before setting a new one, that would
39    // be safer than just updating existing mappings. http://crbug.com/413464
40    if (iter->parent_frame_host == parent_frame_host &&
41        (iter->accessibility_node_id == accessibility_node_id ||
42         iter->child_frame_tree_node_id == child_frame_tree_node_id)) {
43      iter->accessibility_node_id = accessibility_node_id;
44      iter->child_frame_tree_node_id = child_frame_tree_node_id;
45      return;
46    }
47  }
48
49  ChildFrameMapping new_mapping;
50  new_mapping.parent_frame_host = parent_frame_host;
51  new_mapping.accessibility_node_id = accessibility_node_id;
52  new_mapping.child_frame_tree_node_id = child_frame_tree_node_id;
53  mappings_.push_back(new_mapping);
54}
55
56void FrameAccessibility::AddGuestWebContents(
57    RenderFrameHostImpl* parent_frame_host,
58    int accessibility_node_id,
59    int browser_plugin_instance_id) {
60  for (std::vector<ChildFrameMapping>::iterator iter = mappings_.begin();
61       iter != mappings_.end();
62       ++iter) {
63    // TODO(dmazzoni): the renderer should keep track of these mappings
64    // and clear an existing mapping before setting a new one, that would
65    // be safer than just updating existing mappings. http://crbug.com/413464
66    if (iter->parent_frame_host == parent_frame_host &&
67        (iter->accessibility_node_id == accessibility_node_id ||
68         iter->browser_plugin_instance_id == browser_plugin_instance_id)) {
69      iter->accessibility_node_id = accessibility_node_id;
70      iter->browser_plugin_instance_id = browser_plugin_instance_id;
71      return;
72    }
73  }
74
75  ChildFrameMapping new_mapping;
76  new_mapping.parent_frame_host = parent_frame_host;
77  new_mapping.accessibility_node_id = accessibility_node_id;
78  new_mapping.browser_plugin_instance_id = browser_plugin_instance_id;
79  mappings_.push_back(new_mapping);
80}
81
82void FrameAccessibility::OnRenderFrameHostDestroyed(
83    RenderFrameHostImpl* render_frame_host) {
84  // Since the order doesn't matter, the fastest way to remove all items
85  // with this render_frame_host is to iterate over the vector backwards,
86  // swapping each one with the back element if we need to delete it.
87  int initial_len = static_cast<int>(mappings_.size());
88  for (int i = initial_len - 1; i >= 0; i--) {
89    if (mappings_[i].parent_frame_host == render_frame_host) {
90      mappings_[i] = mappings_.back();
91      mappings_.pop_back();
92    }
93  }
94}
95
96RenderFrameHostImpl* FrameAccessibility::GetChild(
97    RenderFrameHostImpl* parent_frame_host,
98    int accessibility_node_id) {
99  for (std::vector<ChildFrameMapping>::iterator iter = mappings_.begin();
100       iter != mappings_.end();
101       ++iter) {
102    if (iter->parent_frame_host != parent_frame_host ||
103        iter->accessibility_node_id != accessibility_node_id) {
104      continue;
105    }
106
107    if (iter->child_frame_tree_node_id) {
108      FrameTreeNode* child_node =
109          FrameTree::GloballyFindByID(iter->child_frame_tree_node_id);
110      if (!child_node)
111        return NULL;
112
113      // We should have gotten a node in the same frame tree.
114      if (child_node->frame_tree() !=
115              parent_frame_host->frame_tree_node()->frame_tree()) {
116        NOTREACHED();
117        return NULL;
118      }
119
120      return child_node->current_frame_host();
121    }
122
123    if (iter->browser_plugin_instance_id) {
124      RenderFrameHost* guest =
125          parent_frame_host->delegate()->GetGuestByInstanceID(
126              iter->browser_plugin_instance_id);
127      if (guest)
128        return static_cast<RenderFrameHostImpl*>(guest);
129    }
130  }
131
132  return NULL;
133}
134
135bool FrameAccessibility::GetParent(
136    RenderFrameHostImpl* child_frame_host,
137    RenderFrameHostImpl** out_parent_frame_host,
138    int* out_accessibility_node_id) {
139  for (std::vector<ChildFrameMapping>::iterator iter = mappings_.begin();
140       iter != mappings_.end();
141       ++iter) {
142    if (iter->child_frame_tree_node_id) {
143      FrameTreeNode* child_node =
144          FrameTree::GloballyFindByID(iter->child_frame_tree_node_id);
145      if (child_node &&
146          child_node->current_frame_host() == child_frame_host) {
147        // We should have gotten a node in the same frame tree.
148        if (child_node->frame_tree() !=
149                iter->parent_frame_host->frame_tree_node()->frame_tree()) {
150          NOTREACHED();
151          return false;
152        }
153
154        if (out_parent_frame_host)
155          *out_parent_frame_host = iter->parent_frame_host;
156        if (out_accessibility_node_id)
157          *out_accessibility_node_id = iter->accessibility_node_id;
158        return true;
159      }
160    }
161
162    if (iter->browser_plugin_instance_id) {
163      RenderFrameHost* guest =
164          iter->parent_frame_host->delegate()->GetGuestByInstanceID(
165              iter->browser_plugin_instance_id);
166      if (guest == child_frame_host) {
167        if (out_parent_frame_host)
168          *out_parent_frame_host = iter->parent_frame_host;
169        if (out_accessibility_node_id)
170          *out_accessibility_node_id = iter->accessibility_node_id;
171        return true;
172      }
173    }
174  }
175
176  return false;
177}
178
179}  // namespace content
180