blob: 5ce80d28be5475fe1e661431743cf8cde39e434c [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
rdevlin.cronin731d1b42016-02-17 20:48:299#include "base/metrics/histogram_macros.h"
rob3e2a0732016-01-06 21:22:0910#include "content/public/browser/browser_thread.h"
naskoe419e2172016-02-09 22:41:1111#include "content/public/browser/navigation_handle.h"
rob3e2a0732016-01-06 21:22:0912#include "content/public/browser/render_frame_host.h"
13#include "content/public/browser/render_process_host.h"
14#include "content/public/browser/web_contents.h"
15#include "content/public/common/child_process_host.h"
rdevlin.cronin9a62870f2016-02-11 23:25:5816#include "extensions/browser/extensions_browser_client.h"
rob3e2a0732016-01-06 21:22:0917
18namespace extensions {
19
20namespace {
21
22// The map is accessed on the IO and UI thread, so construct it once and never
23// delete it.
24base::LazyInstance<ExtensionApiFrameIdMap>::Leaky g_map_instance =
25 LAZY_INSTANCE_INITIALIZER;
26
rdevlin.cronin9a62870f2016-02-11 23:25:5827int GetTabId(content::RenderFrameHost* rfh) {
28 if (!rfh)
29 return -1;
30 return ExtensionsBrowserClient::Get()->GetTabIdForWebContents(
31 content::WebContents::FromRenderFrameHost(rfh));
32}
33
34bool IsFrameRoutingIdValid(int frame_routing_id) {
35 // frame_routing_id == -2 = MSG_ROUTING_NONE -> not a RenderFrameHost.
36 // frame_routing_id == -1 -> should be MSG_ROUTING_NONE, but there are
37 // callers that use "-1" for unknown frames.
38 return frame_routing_id > -1;
39}
40
rob3e2a0732016-01-06 21:22:0941} // namespace
42
43const int ExtensionApiFrameIdMap::kInvalidFrameId = -1;
rob52277c82016-02-07 17:28:5744const int ExtensionApiFrameIdMap::kTopFrameId = 0;
rob3e2a0732016-01-06 21:22:0945
rdevlin.cronin9a62870f2016-02-11 23:25:5846ExtensionApiFrameIdMap::FrameData::FrameData()
47 : frame_id(kInvalidFrameId), parent_frame_id(kInvalidFrameId), tab_id(-1) {}
rob3e2a0732016-01-06 21:22:0948
rdevlin.cronin9a62870f2016-02-11 23:25:5849ExtensionApiFrameIdMap::FrameData::FrameData(int frame_id,
50 int parent_frame_id,
51 int tab_id)
52 : frame_id(frame_id), parent_frame_id(parent_frame_id), tab_id(tab_id) {}
rob3e2a0732016-01-06 21:22:0953
54ExtensionApiFrameIdMap::RenderFrameIdKey::RenderFrameIdKey()
55 : render_process_id(content::ChildProcessHost::kInvalidUniqueID),
56 frame_routing_id(MSG_ROUTING_NONE) {}
57
58ExtensionApiFrameIdMap::RenderFrameIdKey::RenderFrameIdKey(
59 int render_process_id,
60 int frame_routing_id)
61 : render_process_id(render_process_id),
62 frame_routing_id(frame_routing_id) {}
63
rdevlin.cronin9a62870f2016-02-11 23:25:5864ExtensionApiFrameIdMap::FrameDataCallbacks::FrameDataCallbacks()
rob3e2a0732016-01-06 21:22:0965 : is_iterating(false) {}
66
rdevlin.cronin9a62870f2016-02-11 23:25:5867ExtensionApiFrameIdMap::FrameDataCallbacks::~FrameDataCallbacks() {}
rob3e2a0732016-01-06 21:22:0968
69bool ExtensionApiFrameIdMap::RenderFrameIdKey::operator<(
70 const RenderFrameIdKey& other) const {
71 return std::tie(render_process_id, frame_routing_id) <
72 std::tie(other.render_process_id, other.frame_routing_id);
73}
74
75bool ExtensionApiFrameIdMap::RenderFrameIdKey::operator==(
76 const RenderFrameIdKey& other) const {
77 return render_process_id == other.render_process_id &&
78 frame_routing_id == other.frame_routing_id;
79}
80
81ExtensionApiFrameIdMap::ExtensionApiFrameIdMap() {}
82
83ExtensionApiFrameIdMap::~ExtensionApiFrameIdMap() {}
84
naskoe419e2172016-02-09 22:41:1185// static
rob3e2a0732016-01-06 21:22:0986ExtensionApiFrameIdMap* ExtensionApiFrameIdMap::Get() {
87 return g_map_instance.Pointer();
88}
89
naskoe419e2172016-02-09 22:41:1190// static
rob3e2a0732016-01-06 21:22:0991int ExtensionApiFrameIdMap::GetFrameId(content::RenderFrameHost* rfh) {
rob3e2a0732016-01-06 21:22:0992 if (!rfh)
93 return kInvalidFrameId;
94 if (rfh->GetParent())
95 return rfh->GetFrameTreeNodeId();
rob52277c82016-02-07 17:28:5796 return kTopFrameId;
rob3e2a0732016-01-06 21:22:0997}
98
naskoe419e2172016-02-09 22:41:1199// static
100int ExtensionApiFrameIdMap::GetFrameId(
101 content::NavigationHandle* navigation_handle) {
102 return navigation_handle->IsInMainFrame()
103 ? 0
104 : navigation_handle->GetFrameTreeNodeId();
105}
rob3e2a0732016-01-06 21:22:09106
naskoe419e2172016-02-09 22:41:11107// static
108int ExtensionApiFrameIdMap::GetParentFrameId(content::RenderFrameHost* rfh) {
rob3e2a0732016-01-06 21:22:09109 return rfh ? GetFrameId(rfh->GetParent()) : kInvalidFrameId;
110}
111
naskoe419e2172016-02-09 22:41:11112// static
113int ExtensionApiFrameIdMap::GetParentFrameId(
114 content::NavigationHandle* navigation_handle) {
115 if (navigation_handle->IsInMainFrame())
116 return -1;
117
118 if (navigation_handle->IsParentMainFrame())
119 return 0;
120
121 return navigation_handle->GetParentFrameTreeNodeId();
122}
123
124// static
rob3e2a0732016-01-06 21:22:09125content::RenderFrameHost* ExtensionApiFrameIdMap::GetRenderFrameHostById(
126 content::WebContents* web_contents,
127 int frame_id) {
128 // Although it is technically possible to map |frame_id| to a RenderFrameHost
129 // without WebContents, we choose to not do that because in the extension API
130 // frameIds are only guaranteed to be meaningful in combination with a tabId.
131 if (!web_contents)
132 return nullptr;
133
134 if (frame_id == kInvalidFrameId)
135 return nullptr;
136
rob52277c82016-02-07 17:28:57137 if (frame_id == kTopFrameId)
rob3e2a0732016-01-06 21:22:09138 return web_contents->GetMainFrame();
139
140 DCHECK_GE(frame_id, 1);
141 return web_contents->FindFrameByFrameTreeNodeId(frame_id);
142}
143
rdevlin.cronin9a62870f2016-02-11 23:25:58144ExtensionApiFrameIdMap::FrameData ExtensionApiFrameIdMap::KeyToValue(
rob3e2a0732016-01-06 21:22:09145 const RenderFrameIdKey& key) const {
146 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
147 key.render_process_id, key.frame_routing_id);
rdevlin.cronin9a62870f2016-02-11 23:25:58148 return FrameData(GetFrameId(rfh), GetParentFrameId(rfh), GetTabId(rfh));
rob3e2a0732016-01-06 21:22:09149}
150
rdevlin.cronin9a62870f2016-02-11 23:25:58151ExtensionApiFrameIdMap::FrameData ExtensionApiFrameIdMap::LookupFrameDataOnUI(
152 const RenderFrameIdKey& key) {
rob3e2a0732016-01-06 21:22:09153 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
154
rdevlin.cronin9a62870f2016-02-11 23:25:58155 FrameDataMap::const_iterator frame_id_iter = frame_data_map_.find(key);
156 if (frame_id_iter != frame_data_map_.end())
rob3e2a0732016-01-06 21:22:09157 return frame_id_iter->second;
158
rdevlin.cronin9a62870f2016-02-11 23:25:58159 FrameData cached_frame_data = KeyToValue(key);
rob3e2a0732016-01-06 21:22:09160 // Don't save invalid values in the map.
rdevlin.cronin9a62870f2016-02-11 23:25:58161 if (cached_frame_data.frame_id == kInvalidFrameId)
162 return cached_frame_data;
rob3e2a0732016-01-06 21:22:09163
rdevlin.cronin9a62870f2016-02-11 23:25:58164 auto kvpair = FrameDataMap::value_type(key, cached_frame_data);
165 base::AutoLock lock(frame_data_map_lock_);
166 return frame_data_map_.insert(kvpair).first->second;
rob3e2a0732016-01-06 21:22:09167}
168
rdevlin.cronin9a62870f2016-02-11 23:25:58169void ExtensionApiFrameIdMap::ReceivedFrameDataOnIO(
rob3e2a0732016-01-06 21:22:09170 const RenderFrameIdKey& key,
rdevlin.cronin9a62870f2016-02-11 23:25:58171 const FrameData& cached_frame_data) {
rob3e2a0732016-01-06 21:22:09172 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
173
rdevlin.cronin9a62870f2016-02-11 23:25:58174 FrameDataCallbacksMap::iterator map_iter = callbacks_map_.find(key);
rob3e2a0732016-01-06 21:22:09175 if (map_iter == callbacks_map_.end()) {
176 // Can happen if ReceivedFrameIdOnIO was called after the frame ID was
rdevlin.cronin9a62870f2016-02-11 23:25:58177 // resolved (e.g. via GetFrameDataOnIO), but before PostTaskAndReply
178 // replied.
rob3e2a0732016-01-06 21:22:09179 return;
180 }
181
rdevlin.cronin9a62870f2016-02-11 23:25:58182 FrameDataCallbacks& callbacks = map_iter->second;
rob3e2a0732016-01-06 21:22:09183
184 if (callbacks.is_iterating)
185 return;
186 callbacks.is_iterating = true;
187
188 // Note: Extra items can be appended to |callbacks| during this loop if a
rdevlin.cronin9a62870f2016-02-11 23:25:58189 // callback calls GetFrameDataOnIO().
190 for (std::list<FrameDataCallback>::iterator it = callbacks.callbacks.begin();
rob3e2a0732016-01-06 21:22:09191 it != callbacks.callbacks.end(); ++it) {
rdevlin.cronin9a62870f2016-02-11 23:25:58192 it->Run(cached_frame_data);
rob3e2a0732016-01-06 21:22:09193 }
194 callbacks_map_.erase(key);
195}
196
rdevlin.cronin9a62870f2016-02-11 23:25:58197void ExtensionApiFrameIdMap::GetFrameDataOnIO(
198 int render_process_id,
199 int frame_routing_id,
200 const FrameDataCallback& callback) {
rob3e2a0732016-01-06 21:22:09201 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
202
rdevlin.cronin9a62870f2016-02-11 23:25:58203 // TODO(robwu): Enable assertion when all callers have been fixed.
204 // DCHECK_EQ(MSG_ROUTING_NONE, -1);
205 if (!IsFrameRoutingIdValid(frame_routing_id)) {
206 callback.Run(FrameData());
207 return;
208 }
209
rob3e2a0732016-01-06 21:22:09210 if (frame_routing_id <= -1) {
211 // frame_routing_id == -2 = MSG_ROUTING_NONE -> not a RenderFrameHost.
212 // frame_routing_id == -1 -> should be MSG_ROUTING_NONE, but there are
213 // callers that use "-1" for unknown frames.
rdevlin.cronin9a62870f2016-02-11 23:25:58214 callback.Run(FrameData());
rob3e2a0732016-01-06 21:22:09215 return;
216 }
rdevlin.cronin9a62870f2016-02-11 23:25:58217
218 FrameData cached_frame_data;
219 bool did_find_cached_frame_data = GetCachedFrameDataOnIO(
220 render_process_id, frame_routing_id, &cached_frame_data);
rob3e2a0732016-01-06 21:22:09221
222 const RenderFrameIdKey key(render_process_id, frame_routing_id);
rdevlin.cronin9a62870f2016-02-11 23:25:58223 FrameDataCallbacksMap::iterator map_iter = callbacks_map_.find(key);
rob3e2a0732016-01-06 21:22:09224
rdevlin.cronin9a62870f2016-02-11 23:25:58225 if (did_find_cached_frame_data) {
rob3e2a0732016-01-06 21:22:09226 // Value already cached, thread hopping is not needed.
227 if (map_iter == callbacks_map_.end()) {
228 // If the frame ID was cached, then it is likely that there are no pending
229 // callbacks. So do not unnecessarily copy the callback, but run it.
rdevlin.cronin9a62870f2016-02-11 23:25:58230 callback.Run(cached_frame_data);
rob3e2a0732016-01-06 21:22:09231 } else {
232 map_iter->second.callbacks.push_back(callback);
rdevlin.cronin9a62870f2016-02-11 23:25:58233 ReceivedFrameDataOnIO(key, cached_frame_data);
rob3e2a0732016-01-06 21:22:09234 }
235 return;
236 }
237
238 // The key was seen for the first time (or the frame has been removed).
239 // Hop to the UI thread to look up the extension API frame ID.
240 callbacks_map_[key].callbacks.push_back(callback);
241 content::BrowserThread::PostTaskAndReplyWithResult(
242 content::BrowserThread::UI, FROM_HERE,
rdevlin.cronin9a62870f2016-02-11 23:25:58243 base::Bind(&ExtensionApiFrameIdMap::LookupFrameDataOnUI,
rob3e2a0732016-01-06 21:22:09244 base::Unretained(this), key),
rdevlin.cronin9a62870f2016-02-11 23:25:58245 base::Bind(&ExtensionApiFrameIdMap::ReceivedFrameDataOnIO,
rob3e2a0732016-01-06 21:22:09246 base::Unretained(this), key));
247}
248
rdevlin.cronin9a62870f2016-02-11 23:25:58249bool ExtensionApiFrameIdMap::GetCachedFrameDataOnIO(int render_process_id,
250 int frame_routing_id,
251 FrameData* frame_data_out) {
252 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
253
254 // TODO(robwu): Enable assertion when all callers have been fixed.
255 // DCHECK_EQ(MSG_ROUTING_NONE, -1);
256 if (!IsFrameRoutingIdValid(frame_routing_id))
257 return false;
258
259 // A valid routing ID is only meaningful with a valid process ID.
260 DCHECK_GE(render_process_id, 0);
261
262 base::AutoLock lock(frame_data_map_lock_);
263 FrameDataMap::const_iterator frame_id_iter = frame_data_map_.find(
264 RenderFrameIdKey(render_process_id, frame_routing_id));
rdevlin.cronin731d1b42016-02-17 20:48:29265 bool found = false;
rdevlin.cronin9a62870f2016-02-11 23:25:58266 if (frame_id_iter != frame_data_map_.end()) {
267 // This is very likely to happen because CacheFrameId() is called as soon
268 // as the frame is created.
269 *frame_data_out = frame_id_iter->second;
rdevlin.cronin731d1b42016-02-17 20:48:29270 found = true;
rdevlin.cronin9a62870f2016-02-11 23:25:58271 }
272
rdevlin.cronin731d1b42016-02-17 20:48:29273 // TODO(devlin): Depending on how the data looks, this may be removable after
274 // a few cycles. Check back in M52 to see if it's still needed.
275 UMA_HISTOGRAM_BOOLEAN("Extensions.ExtensionFrameMapCacheHit", found);
276 return found;
rdevlin.cronin9a62870f2016-02-11 23:25:58277}
278
279void ExtensionApiFrameIdMap::CacheFrameData(content::RenderFrameHost* rfh) {
rob3e2a0732016-01-06 21:22:09280 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
281
282 const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
rdevlin.cronin9a62870f2016-02-11 23:25:58283 CacheFrameData(key);
284 DCHECK(frame_data_map_.find(key) != frame_data_map_.end());
rob3e2a0732016-01-06 21:22:09285}
286
rdevlin.cronin9a62870f2016-02-11 23:25:58287void ExtensionApiFrameIdMap::CacheFrameData(const RenderFrameIdKey& key) {
288 LookupFrameDataOnUI(key);
rob3e2a0732016-01-06 21:22:09289}
290
rdevlin.cronin9a62870f2016-02-11 23:25:58291void ExtensionApiFrameIdMap::RemoveFrameData(content::RenderFrameHost* rfh) {
rob3e2a0732016-01-06 21:22:09292 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
293 DCHECK(rfh);
294
295 const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
rdevlin.cronin9a62870f2016-02-11 23:25:58296 RemoveFrameData(key);
rob3e2a0732016-01-06 21:22:09297}
298
rdevlin.cronin9a62870f2016-02-11 23:25:58299void ExtensionApiFrameIdMap::RemoveFrameData(const RenderFrameIdKey& key) {
rob3e2a0732016-01-06 21:22:09300 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
301
rdevlin.cronin9a62870f2016-02-11 23:25:58302 base::AutoLock lock(frame_data_map_lock_);
303 frame_data_map_.erase(key);
rob3e2a0732016-01-06 21:22:09304}
305
306} // namespace extensions