blob: b7ae5eafd2ffaecf3d6d2e70b3822182bc21693a [file] [log] [blame]
rob3e2a0732016-01-06 21:22:091// Copyright 2015 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 "extensions/browser/extension_api_frame_id_map.h"
6
7#include <tuple>
8
9#include "content/public/browser/browser_thread.h"
naskoe419e2172016-02-09 22:41:1110#include "content/public/browser/navigation_handle.h"
rob3e2a0732016-01-06 21:22:0911#include "content/public/browser/render_frame_host.h"
12#include "content/public/browser/render_process_host.h"
13#include "content/public/browser/web_contents.h"
14#include "content/public/common/child_process_host.h"
rdevlin.cronin9a62870f2016-02-11 23:25:5815#include "extensions/browser/extensions_browser_client.h"
rob3e2a0732016-01-06 21:22:0916
17namespace extensions {
18
19namespace {
20
21// The map is accessed on the IO and UI thread, so construct it once and never
22// delete it.
23base::LazyInstance<ExtensionApiFrameIdMap>::Leaky g_map_instance =
24 LAZY_INSTANCE_INITIALIZER;
25
rdevlin.cronin9a62870f2016-02-11 23:25:5826int GetTabId(content::RenderFrameHost* rfh) {
27 if (!rfh)
28 return -1;
29 return ExtensionsBrowserClient::Get()->GetTabIdForWebContents(
30 content::WebContents::FromRenderFrameHost(rfh));
31}
32
33bool IsFrameRoutingIdValid(int frame_routing_id) {
34 // frame_routing_id == -2 = MSG_ROUTING_NONE -> not a RenderFrameHost.
35 // frame_routing_id == -1 -> should be MSG_ROUTING_NONE, but there are
36 // callers that use "-1" for unknown frames.
37 return frame_routing_id > -1;
38}
39
rob3e2a0732016-01-06 21:22:0940} // namespace
41
42const int ExtensionApiFrameIdMap::kInvalidFrameId = -1;
rob52277c82016-02-07 17:28:5743const int ExtensionApiFrameIdMap::kTopFrameId = 0;
rob3e2a0732016-01-06 21:22:0944
rdevlin.cronin9a62870f2016-02-11 23:25:5845ExtensionApiFrameIdMap::FrameData::FrameData()
46 : frame_id(kInvalidFrameId), parent_frame_id(kInvalidFrameId), tab_id(-1) {}
rob3e2a0732016-01-06 21:22:0947
rdevlin.cronin9a62870f2016-02-11 23:25:5848ExtensionApiFrameIdMap::FrameData::FrameData(int frame_id,
49 int parent_frame_id,
50 int tab_id)
51 : frame_id(frame_id), parent_frame_id(parent_frame_id), tab_id(tab_id) {}
rob3e2a0732016-01-06 21:22:0952
53ExtensionApiFrameIdMap::RenderFrameIdKey::RenderFrameIdKey()
54 : render_process_id(content::ChildProcessHost::kInvalidUniqueID),
55 frame_routing_id(MSG_ROUTING_NONE) {}
56
57ExtensionApiFrameIdMap::RenderFrameIdKey::RenderFrameIdKey(
58 int render_process_id,
59 int frame_routing_id)
60 : render_process_id(render_process_id),
61 frame_routing_id(frame_routing_id) {}
62
rdevlin.cronin9a62870f2016-02-11 23:25:5863ExtensionApiFrameIdMap::FrameDataCallbacks::FrameDataCallbacks()
rob3e2a0732016-01-06 21:22:0964 : is_iterating(false) {}
65
rdevlin.cronin9a62870f2016-02-11 23:25:5866ExtensionApiFrameIdMap::FrameDataCallbacks::~FrameDataCallbacks() {}
rob3e2a0732016-01-06 21:22:0967
68bool ExtensionApiFrameIdMap::RenderFrameIdKey::operator<(
69 const RenderFrameIdKey& other) const {
70 return std::tie(render_process_id, frame_routing_id) <
71 std::tie(other.render_process_id, other.frame_routing_id);
72}
73
74bool ExtensionApiFrameIdMap::RenderFrameIdKey::operator==(
75 const RenderFrameIdKey& other) const {
76 return render_process_id == other.render_process_id &&
77 frame_routing_id == other.frame_routing_id;
78}
79
80ExtensionApiFrameIdMap::ExtensionApiFrameIdMap() {}
81
82ExtensionApiFrameIdMap::~ExtensionApiFrameIdMap() {}
83
naskoe419e2172016-02-09 22:41:1184// static
rob3e2a0732016-01-06 21:22:0985ExtensionApiFrameIdMap* ExtensionApiFrameIdMap::Get() {
86 return g_map_instance.Pointer();
87}
88
naskoe419e2172016-02-09 22:41:1189// static
rob3e2a0732016-01-06 21:22:0990int ExtensionApiFrameIdMap::GetFrameId(content::RenderFrameHost* rfh) {
rob3e2a0732016-01-06 21:22:0991 if (!rfh)
92 return kInvalidFrameId;
93 if (rfh->GetParent())
94 return rfh->GetFrameTreeNodeId();
rob52277c82016-02-07 17:28:5795 return kTopFrameId;
rob3e2a0732016-01-06 21:22:0996}
97
naskoe419e2172016-02-09 22:41:1198// static
99int ExtensionApiFrameIdMap::GetFrameId(
100 content::NavigationHandle* navigation_handle) {
101 return navigation_handle->IsInMainFrame()
102 ? 0
103 : navigation_handle->GetFrameTreeNodeId();
104}
rob3e2a0732016-01-06 21:22:09105
naskoe419e2172016-02-09 22:41:11106// static
107int ExtensionApiFrameIdMap::GetParentFrameId(content::RenderFrameHost* rfh) {
rob3e2a0732016-01-06 21:22:09108 return rfh ? GetFrameId(rfh->GetParent()) : kInvalidFrameId;
109}
110
naskoe419e2172016-02-09 22:41:11111// static
112int ExtensionApiFrameIdMap::GetParentFrameId(
113 content::NavigationHandle* navigation_handle) {
114 if (navigation_handle->IsInMainFrame())
115 return -1;
116
117 if (navigation_handle->IsParentMainFrame())
118 return 0;
119
120 return navigation_handle->GetParentFrameTreeNodeId();
121}
122
123// static
rob3e2a0732016-01-06 21:22:09124content::RenderFrameHost* ExtensionApiFrameIdMap::GetRenderFrameHostById(
125 content::WebContents* web_contents,
126 int frame_id) {
127 // Although it is technically possible to map |frame_id| to a RenderFrameHost
128 // without WebContents, we choose to not do that because in the extension API
129 // frameIds are only guaranteed to be meaningful in combination with a tabId.
130 if (!web_contents)
131 return nullptr;
132
133 if (frame_id == kInvalidFrameId)
134 return nullptr;
135
rob52277c82016-02-07 17:28:57136 if (frame_id == kTopFrameId)
rob3e2a0732016-01-06 21:22:09137 return web_contents->GetMainFrame();
138
139 DCHECK_GE(frame_id, 1);
140 return web_contents->FindFrameByFrameTreeNodeId(frame_id);
141}
142
rdevlin.cronin9a62870f2016-02-11 23:25:58143ExtensionApiFrameIdMap::FrameData ExtensionApiFrameIdMap::KeyToValue(
rob3e2a0732016-01-06 21:22:09144 const RenderFrameIdKey& key) const {
145 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
146 key.render_process_id, key.frame_routing_id);
rdevlin.cronin9a62870f2016-02-11 23:25:58147 return FrameData(GetFrameId(rfh), GetParentFrameId(rfh), GetTabId(rfh));
rob3e2a0732016-01-06 21:22:09148}
149
rdevlin.cronin9a62870f2016-02-11 23:25:58150ExtensionApiFrameIdMap::FrameData ExtensionApiFrameIdMap::LookupFrameDataOnUI(
151 const RenderFrameIdKey& key) {
rob3e2a0732016-01-06 21:22:09152 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
153
rdevlin.cronin9a62870f2016-02-11 23:25:58154 FrameDataMap::const_iterator frame_id_iter = frame_data_map_.find(key);
155 if (frame_id_iter != frame_data_map_.end())
rob3e2a0732016-01-06 21:22:09156 return frame_id_iter->second;
157
rdevlin.cronin9a62870f2016-02-11 23:25:58158 FrameData cached_frame_data = KeyToValue(key);
rob3e2a0732016-01-06 21:22:09159 // Don't save invalid values in the map.
rdevlin.cronin9a62870f2016-02-11 23:25:58160 if (cached_frame_data.frame_id == kInvalidFrameId)
161 return cached_frame_data;
rob3e2a0732016-01-06 21:22:09162
rdevlin.cronin9a62870f2016-02-11 23:25:58163 auto kvpair = FrameDataMap::value_type(key, cached_frame_data);
164 base::AutoLock lock(frame_data_map_lock_);
165 return frame_data_map_.insert(kvpair).first->second;
rob3e2a0732016-01-06 21:22:09166}
167
rdevlin.cronin9a62870f2016-02-11 23:25:58168void ExtensionApiFrameIdMap::ReceivedFrameDataOnIO(
rob3e2a0732016-01-06 21:22:09169 const RenderFrameIdKey& key,
rdevlin.cronin9a62870f2016-02-11 23:25:58170 const FrameData& cached_frame_data) {
rob3e2a0732016-01-06 21:22:09171 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
172
rdevlin.cronin9a62870f2016-02-11 23:25:58173 FrameDataCallbacksMap::iterator map_iter = callbacks_map_.find(key);
rob3e2a0732016-01-06 21:22:09174 if (map_iter == callbacks_map_.end()) {
175 // Can happen if ReceivedFrameIdOnIO was called after the frame ID was
rdevlin.cronin9a62870f2016-02-11 23:25:58176 // resolved (e.g. via GetFrameDataOnIO), but before PostTaskAndReply
177 // replied.
rob3e2a0732016-01-06 21:22:09178 return;
179 }
180
rdevlin.cronin9a62870f2016-02-11 23:25:58181 FrameDataCallbacks& callbacks = map_iter->second;
rob3e2a0732016-01-06 21:22:09182
183 if (callbacks.is_iterating)
184 return;
185 callbacks.is_iterating = true;
186
187 // Note: Extra items can be appended to |callbacks| during this loop if a
rdevlin.cronin9a62870f2016-02-11 23:25:58188 // callback calls GetFrameDataOnIO().
189 for (std::list<FrameDataCallback>::iterator it = callbacks.callbacks.begin();
rob3e2a0732016-01-06 21:22:09190 it != callbacks.callbacks.end(); ++it) {
rdevlin.cronin9a62870f2016-02-11 23:25:58191 it->Run(cached_frame_data);
rob3e2a0732016-01-06 21:22:09192 }
193 callbacks_map_.erase(key);
194}
195
rdevlin.cronin9a62870f2016-02-11 23:25:58196void ExtensionApiFrameIdMap::GetFrameDataOnIO(
197 int render_process_id,
198 int frame_routing_id,
199 const FrameDataCallback& callback) {
rob3e2a0732016-01-06 21:22:09200 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
201
rdevlin.cronin9a62870f2016-02-11 23:25:58202 // TODO(robwu): Enable assertion when all callers have been fixed.
203 // DCHECK_EQ(MSG_ROUTING_NONE, -1);
204 if (!IsFrameRoutingIdValid(frame_routing_id)) {
205 callback.Run(FrameData());
206 return;
207 }
208
rob3e2a0732016-01-06 21:22:09209 if (frame_routing_id <= -1) {
210 // frame_routing_id == -2 = MSG_ROUTING_NONE -> not a RenderFrameHost.
211 // frame_routing_id == -1 -> should be MSG_ROUTING_NONE, but there are
212 // callers that use "-1" for unknown frames.
rdevlin.cronin9a62870f2016-02-11 23:25:58213 callback.Run(FrameData());
rob3e2a0732016-01-06 21:22:09214 return;
215 }
rdevlin.cronin9a62870f2016-02-11 23:25:58216
217 FrameData cached_frame_data;
218 bool did_find_cached_frame_data = GetCachedFrameDataOnIO(
219 render_process_id, frame_routing_id, &cached_frame_data);
rob3e2a0732016-01-06 21:22:09220
221 const RenderFrameIdKey key(render_process_id, frame_routing_id);
rdevlin.cronin9a62870f2016-02-11 23:25:58222 FrameDataCallbacksMap::iterator map_iter = callbacks_map_.find(key);
rob3e2a0732016-01-06 21:22:09223
rdevlin.cronin9a62870f2016-02-11 23:25:58224 if (did_find_cached_frame_data) {
rob3e2a0732016-01-06 21:22:09225 // Value already cached, thread hopping is not needed.
226 if (map_iter == callbacks_map_.end()) {
227 // If the frame ID was cached, then it is likely that there are no pending
228 // callbacks. So do not unnecessarily copy the callback, but run it.
rdevlin.cronin9a62870f2016-02-11 23:25:58229 callback.Run(cached_frame_data);
rob3e2a0732016-01-06 21:22:09230 } else {
231 map_iter->second.callbacks.push_back(callback);
rdevlin.cronin9a62870f2016-02-11 23:25:58232 ReceivedFrameDataOnIO(key, cached_frame_data);
rob3e2a0732016-01-06 21:22:09233 }
234 return;
235 }
236
237 // The key was seen for the first time (or the frame has been removed).
238 // Hop to the UI thread to look up the extension API frame ID.
239 callbacks_map_[key].callbacks.push_back(callback);
240 content::BrowserThread::PostTaskAndReplyWithResult(
241 content::BrowserThread::UI, FROM_HERE,
rdevlin.cronin9a62870f2016-02-11 23:25:58242 base::Bind(&ExtensionApiFrameIdMap::LookupFrameDataOnUI,
rob3e2a0732016-01-06 21:22:09243 base::Unretained(this), key),
rdevlin.cronin9a62870f2016-02-11 23:25:58244 base::Bind(&ExtensionApiFrameIdMap::ReceivedFrameDataOnIO,
rob3e2a0732016-01-06 21:22:09245 base::Unretained(this), key));
246}
247
rdevlin.cronin9a62870f2016-02-11 23:25:58248bool ExtensionApiFrameIdMap::GetCachedFrameDataOnIO(int render_process_id,
249 int frame_routing_id,
250 FrameData* frame_data_out) {
251 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
252
253 // TODO(robwu): Enable assertion when all callers have been fixed.
254 // DCHECK_EQ(MSG_ROUTING_NONE, -1);
255 if (!IsFrameRoutingIdValid(frame_routing_id))
256 return false;
257
258 // A valid routing ID is only meaningful with a valid process ID.
259 DCHECK_GE(render_process_id, 0);
260
261 base::AutoLock lock(frame_data_map_lock_);
262 FrameDataMap::const_iterator frame_id_iter = frame_data_map_.find(
263 RenderFrameIdKey(render_process_id, frame_routing_id));
264 if (frame_id_iter != frame_data_map_.end()) {
265 // This is very likely to happen because CacheFrameId() is called as soon
266 // as the frame is created.
267 *frame_data_out = frame_id_iter->second;
268 return true;
269 }
270
271 return false;
272}
273
274void ExtensionApiFrameIdMap::CacheFrameData(content::RenderFrameHost* rfh) {
rob3e2a0732016-01-06 21:22:09275 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
276
277 const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
rdevlin.cronin9a62870f2016-02-11 23:25:58278 CacheFrameData(key);
279 DCHECK(frame_data_map_.find(key) != frame_data_map_.end());
rob3e2a0732016-01-06 21:22:09280}
281
rdevlin.cronin9a62870f2016-02-11 23:25:58282void ExtensionApiFrameIdMap::CacheFrameData(const RenderFrameIdKey& key) {
283 LookupFrameDataOnUI(key);
rob3e2a0732016-01-06 21:22:09284}
285
rdevlin.cronin9a62870f2016-02-11 23:25:58286void ExtensionApiFrameIdMap::RemoveFrameData(content::RenderFrameHost* rfh) {
rob3e2a0732016-01-06 21:22:09287 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
288 DCHECK(rfh);
289
290 const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
rdevlin.cronin9a62870f2016-02-11 23:25:58291 RemoveFrameData(key);
rob3e2a0732016-01-06 21:22:09292}
293
rdevlin.cronin9a62870f2016-02-11 23:25:58294void ExtensionApiFrameIdMap::RemoveFrameData(const RenderFrameIdKey& key) {
rob3e2a0732016-01-06 21:22:09295 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
296
rdevlin.cronin9a62870f2016-02-11 23:25:58297 base::AutoLock lock(frame_data_map_lock_);
298 frame_data_map_.erase(key);
rob3e2a0732016-01-06 21:22:09299}
300
301} // namespace extensions