Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: extensions/browser/extension_api_frame_id_map.cc

Issue 1413543005: Use FrameTreeNode ID as frameId in extension APIs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add override specifier to destructor Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // 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 <list>
8 #include <map>
9 #include <tuple>
10
11 #include "base/lazy_instance.h"
12 #include "base/synchronization/lock.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/web_contents.h"
17
18 namespace extensions {
19
20 namespace {
21
22 // A set of identifiers that uniquely identifies a RenderFrame.
23 struct RenderFrameIdKey {
24 RenderFrameIdKey(int render_process_id, int frame_routing_id)
25 : render_process_id(render_process_id),
26 frame_routing_id(frame_routing_id) {}
27
28 // The process ID of the renderer that contains the RenderFrame.
29 int render_process_id;
30 // The routing ID of the RenderFrame.
31 int frame_routing_id;
32
33 bool operator<(const RenderFrameIdKey& other) const {
34 return std::tie(render_process_id, frame_routing_id) <
35 std::tie(other.render_process_id, other.frame_routing_id);
36 }
37 };
38
39 // This is a std::list so that iterators are not invalidated when the list is
40 // modified during an iteration.
41 using FrameIdCallbackList = std::list<ExtensionApiFrameIdMap::FrameIdCallback>;
42 using FrameIdMap = std::map<RenderFrameIdKey, ExtensionApiFrameId>;
43 using CallbackMap = std::map<RenderFrameIdKey, FrameIdCallbackList>;
44
45 // Queued callbacks for use on the IO thread. This queue is only used when the
46 // frameId needs to be fetched from the UI thread.
47 base::LazyInstance<CallbackMap> g_callbacks_map = LAZY_INSTANCE_INITIALIZER;
Devlin 2015/12/12 14:25:00 When we have 3 different members, I think it would
robwu 2015/12/14 20:55:42 Done (separately from the other changes, in patch
48
49 // This frameId map is only modified on the UI thread and used for two purposes:
50 // - On the IO thread, it avoids unnecessary thread hops.
51 // - On the UI thread and the IO thread, it ensures that the frameId remains
52 // constant, even after removing a frame.
53 // Items are never removed from this map.
Devlin 2015/12/12 14:25:00 This actually makes me a little worried. Frames a
Charlie Reis 2015/12/14 20:45:00 Yes, this seems like a nontrivial leak to me, sinc
54 base::LazyInstance<FrameIdMap> g_frame_id_map = LAZY_INSTANCE_INITIALIZER;
55
56 // This lock protects |g_frame_id_map| from being concurrently written on the UI
57 // thread and read on the IO thread.
58 base::LazyInstance<base::Lock>::Leaky g_frame_id_map_lock =
59 LAZY_INSTANCE_INITIALIZER;
60
61 int GetFrameIdFromFrame(content::RenderFrameHost* rfh) {
62 if (!rfh)
63 return ExtensionApiFrameId::kInvalidFrameId;
64 if (rfh->GetParent())
65 return rfh->GetFrameTreeNodeId();
66 return 0; // Main frame.
67 }
68
69
70 } // namespace
71
72 ExtensionApiFrameId::ExtensionApiFrameId()
73 : frame_id(kInvalidFrameId), parent_frame_id(kInvalidFrameId) {}
74
75 ExtensionApiFrameId::ExtensionApiFrameId(content::RenderFrameHost* rfh)
76 : frame_id(GetFrameIdFromFrame(rfh)),
77 parent_frame_id(GetFrameIdFromFrame(rfh ? rfh->GetParent() : nullptr)) {
78 }
79
80 const ExtensionApiFrameId& LookupFrameIdOnUI(const RenderFrameIdKey& key) {
81 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
82
83 FrameIdMap& frame_id_map = g_frame_id_map.Get();
84 FrameIdMap::const_iterator frame_id_iter = frame_id_map.find(key);
85 if (frame_id_iter != frame_id_map.end())
86 return frame_id_iter->second;
87
88 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
89 key.render_process_id, key.frame_routing_id);
90 auto kvpair = FrameIdMap::value_type(key, ExtensionApiFrameId(rfh));
91 base::AutoLock lock(g_frame_id_map_lock.Get());
92 return frame_id_map.insert(kvpair).first->second;
93 }
94
95 void GotFrameId(const RenderFrameIdKey& key) {
Devlin 2015/12/12 14:25:00 nit: let's add "OnIO"
robwu 2015/12/14 20:55:42 Done.
96 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
97
98 // |g_frame_id_map_lock| is not needed here because GotFrameId is only called
Devlin 2015/12/12 14:25:00 Wow, talk about relying on C++'s underlying guaran
robwu 2015/12/14 20:55:42 It is indeed safe because the pointers are valid (
Devlin 2015/12/14 21:31:55 Hmm, yeah, passing a const& to a thread hop seems
99 // after returning from LookupFrameIdOnUI(). Items from the map are never
100 // removed, so it is safe to read values without lock.
101 FrameIdMap& frame_id_map = g_frame_id_map.Get();
102 FrameIdMap::const_iterator frame_id_iter = frame_id_map.find(key);
103 CHECK(frame_id_iter != frame_id_map.end());
104
105 const ExtensionApiFrameId& extension_api_frame_id = frame_id_iter->second;
106 CallbackMap& callbacks_map = g_callbacks_map.Get();
107 FrameIdCallbackList& callbacks = callbacks_map[key];
108 // If GetFrameIdOnIO is called in one of the callbacks, another callback is
109 // appended to the list. So we cannot use a fancy range-based for loop.
110 for (FrameIdCallbackList::iterator it = callbacks.begin();
111 it != callbacks.end(); ++it) {
112 it->Run(extension_api_frame_id);
113 }
114
115 // Remove the callback list. Because the extension frame ID is cached, this
116 // list is never created again for |key|.
117 callbacks_map.erase(key);
Devlin 2015/12/12 14:25:00 In conjunction with the comment on line 108 and th
robwu 2015/12/14 20:55:42 Both methods can only be called on the same thread
Devlin 2015/12/14 21:31:55 Yeah, you're right, this should be safe.
118 }
119
120 void ExtensionApiFrameIdMap::GetFrameIdOnIO(int render_process_id,
121 int frame_routing_id,
122 const FrameIdCallback& callback) {
123 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
124
125 const RenderFrameIdKey key(render_process_id, frame_routing_id);
126 CallbackMap& callbacks_map = g_callbacks_map.Get();
127 CallbackMap::iterator callbacks_iter = callbacks_map.find(key);
128 if (callbacks_iter != callbacks_map.end()) {
129 // There is a pending lookup for the extension frame ID.
130 callbacks_iter->second.push_back(callback);
131 return;
132 }
133
134 FrameIdMap& frame_id_map = g_frame_id_map.Get();
135 FrameIdMap::const_iterator frame_id_iter;
136 bool is_frame_id_found;
Devlin 2015/12/12 14:25:00 nit: initialize.
robwu 2015/12/14 20:55:42 Done.
137 {
138 base::AutoLock lock(g_frame_id_map_lock.Get());
139 frame_id_iter = frame_id_map.find(key);
140 is_frame_id_found = frame_id_iter != frame_id_map.end();
141 }
142 if (is_frame_id_found) {
143 callback.Run(frame_id_iter->second);
144 return;
145 }
146
147 // The key was seen for the first time, hop to the UI thread to look up the
148 // extension frame ID.
149 callbacks_map[key].push_back(callback);
150 content::BrowserThread::PostTaskAndReply(
151 content::BrowserThread::UI, FROM_HERE,
152 base::Bind(base::IgnoreResult(&LookupFrameIdOnUI), key),
153 base::Bind(&GotFrameId, key));
154 }
155
156 const ExtensionApiFrameId& ExtensionApiFrameIdMap::GetFrameId(
157 int render_process_id,
158 int frame_routing_id) {
159 return LookupFrameIdOnUI(
160 RenderFrameIdKey(render_process_id, frame_routing_id));
161 }
162
163 const ExtensionApiFrameId& ExtensionApiFrameIdMap::GetFrameId(
Devlin 2015/12/12 14:25:00 Should this also use LookupFrameIdOnUI?
robwu 2015/12/14 20:55:42 Yes. At first I split up the methods to save a map
164 content::RenderFrameHost* rfh) {
165 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
166
167 FrameIdMap& frame_id_map = g_frame_id_map.Get();
168 if (!rfh)
169 return frame_id_map[RenderFrameIdKey(-1, -1)];
170
171 const RenderFrameIdKey key(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
172
173 FrameIdMap::const_iterator frame_id_iter = frame_id_map.find(key);
174 if (frame_id_iter != frame_id_map.end())
175 return frame_id_iter->second;
176
177 return frame_id_map.insert(
178 FrameIdMap::value_type(key, ExtensionApiFrameId(rfh))).first->second;
179 }
180
181 content::RenderFrameHost* ExtensionApiFrameIdMap::GetRenderFrameHostById(
182 content::WebContents* web_contents,
183 int frame_id) {
184 // Although it is technically possible to map |frame_id| to a RenderFrameHost
185 // without WebContents, we choose to not do that because in the extension API
186 // frameIds are only guaranteed to be meaningful in combination with a tabId.
187 if (!web_contents)
188 return nullptr;
189
190 if (frame_id == 0)
191 return web_contents->GetMainFrame();
192
193 if (frame_id == -1)
194 return nullptr;
195
196 DCHECK_GE(frame_id, 1);
197 return web_contents->FindFrameByFrameTreeNodeId(frame_id);
198 }
199
200 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698