OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 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/renderer/unique_name_helper.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/containers/adapters.h" | |
10 #include "base/logging.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "base/strings/string_piece.h" | |
13 #include "content/renderer/render_frame_impl.h" | |
14 #include "content/renderer/render_frame_proxy.h" | |
15 #include "third_party/WebKit/public/web/WebLocalFrame.h" | |
16 | |
17 namespace content { | |
18 | |
19 namespace { | |
20 | |
21 const std::string& UniqueNameForFrame(blink::WebFrame* frame) { | |
22 return frame->isWebLocalFrame() | |
23 ? RenderFrameImpl::FromWebFrame(frame)->unique_name() | |
24 : RenderFrameProxy::FromWebFrame(frame)->unique_name(); | |
25 } | |
26 | |
27 bool UniqueNameExists(blink::WebFrame* top, const std::string& candidate) { | |
28 // This method is currently O(N), where N = number of frames in the tree. | |
29 | |
30 // Before recalculating or checking unique name, we set m_uniqueName | |
31 // to an empty string (so the soon-to-be-removed name does not count | |
32 // as a collision). This means that uniqueNameExists would return | |
33 // false positives when called with an empty |name|. | |
34 DCHECK(!candidate.empty()); | |
35 | |
36 for (blink::WebFrame* frame = top; frame; frame = frame->traverseNext()) { | |
37 if (UniqueNameForFrame(frame) == candidate) | |
38 return true; | |
39 } | |
40 | |
41 return false; | |
42 } | |
43 | |
44 std::string GenerateCandidate(blink::WebFrame* parent, blink::WebFrame* child) { | |
45 constexpr char kFramePathPrefix[] = "<!--framePath "; | |
46 constexpr int kFramePathPrefixLength = 14; | |
47 constexpr int kFramePathSuffixLength = 3; | |
48 | |
49 std::string new_name(kFramePathPrefix); | |
50 | |
51 // Find the nearest parent that has a frame with a path in it. | |
52 std::vector<blink::WebFrame*> chain; | |
53 for (blink::WebFrame* frame = parent; frame; frame = frame->parent()) { | |
54 base::StringPiece name = UniqueNameForFrame(frame); | |
55 if (name.starts_with(kFramePathPrefix) && name.ends_with("-->") && | |
Charlie Reis
2017/03/02 23:59:18
This doesn't match FrameTreeUniqueNameCandidate, w
dcheng
2017/03/03 10:22:08
There was actually a bug in the original code: if
Charlie Reis
2017/03/20 20:11:38
Thanks for fixing it. I suppose a test wouldn't h
| |
56 kFramePathPrefixLength + kFramePathSuffixLength < name.size()) { | |
57 name.substr(kFramePathPrefixLength, | |
58 name.size() - kFramePathPrefixLength - kFramePathSuffixLength) | |
59 .AppendToString(&new_name); | |
60 break; | |
61 } | |
62 chain.push_back(frame); | |
63 } | |
64 | |
65 for (auto* frame : base::Reversed(chain)) { | |
66 new_name += '/'; | |
67 new_name += UniqueNameForFrame(frame); | |
68 } | |
69 | |
70 int child_count = 0; | |
71 for (blink::WebFrame* frame = parent->firstChild(); frame != nullptr; | |
72 frame = frame->nextSibling()) { | |
73 ++child_count; | |
74 } | |
75 | |
76 // When generating a candidate, a null |child| means that this is the | |
77 // candidate name for a child frame that's not yet attached. | |
78 new_name += "/<!--frame"; | |
79 new_name += base::IntToString(child_count - (child ? 1 : 0)); | |
80 new_name += "-->-->"; | |
81 | |
82 // NOTE: This name might not be unique - see http://crbug.com/588800. | |
83 return new_name; | |
84 } | |
85 | |
86 std::string GenerateFramePosition(blink::WebFrame* parent, | |
87 blink::WebFrame* child) { | |
88 // This method is currently O(N), where N = number of frames in the tree. | |
89 | |
90 std::string position_string("<!--framePosition"); | |
91 | |
92 while (parent) { | |
Charlie Reis
2017/03/02 23:59:18
Same.
dcheng
2017/03/03 10:22:08
This code differs slightly, but the net result is
Charlie Reis
2017/03/20 20:11:38
Acknowledged.
| |
93 int position_in_parent = 0; | |
94 blink::WebFrame* sibling = parent->firstChild(); | |
95 while (sibling != child) { | |
96 sibling = sibling->nextSibling(); | |
97 ++position_in_parent; | |
98 } | |
99 | |
100 position_string += '-'; | |
101 position_string += base::IntToString(position_in_parent); | |
102 | |
103 child = parent; | |
104 parent = parent->parent(); | |
105 } | |
106 | |
107 // NOTE: The generated string is not guaranteed to be unique, but should | |
108 // have a better chance of being unique than the string generated by | |
109 // GenerateCandidate, because we embed extra information into the string: | |
110 // 1) we walk the full chain of ancestors, all the way to the main frame | |
111 // 2) we use frame-position-within-parent (aka |position_in_parent|) | |
112 // instead of sibling-count. | |
113 return position_string; | |
114 } | |
115 | |
116 std::string AppendUniqueSuffix(blink::WebFrame* top, | |
117 const std::string& prefix, | |
118 const std::string& likely_unique_suffix) { | |
119 // We want unique name to be stable across page reloads - this is why | |
120 // we use a deterministic |number_of_tries| rather than a random number | |
121 // (a random number would be more likely to avoid a collision, but | |
122 // would change after every page reload). | |
123 int number_of_retries = 0; | |
124 | |
125 // Keep trying |prefix| + |likely_unique_suffix| + |number_of_tries| | |
126 // concatenations until we get a truly unique name. | |
127 std::string candidate(prefix); | |
128 candidate += likely_unique_suffix; | |
129 candidate += '/'; | |
130 while (true) { | |
131 size_t current_length = candidate.size(); | |
132 candidate += base::IntToString(number_of_retries++); | |
133 candidate += "-->"; | |
134 if (!UniqueNameExists(top, candidate)) | |
135 break; | |
136 candidate.resize(current_length); | |
137 } | |
138 return candidate; | |
139 } | |
140 | |
141 std::string CalculateNewName(blink::WebFrame* parent, | |
142 blink::WebFrame* child, | |
143 const std::string& name) { | |
144 blink::WebFrame* top = parent->top(); | |
145 if (!name.empty() && !UniqueNameExists(top, name) && name != "_blank") | |
146 return name; | |
147 | |
148 std::string candidate = GenerateCandidate(parent, child); | |
149 if (!UniqueNameExists(top, candidate)) | |
150 return candidate; | |
151 | |
152 std::string likely_unique_suffix = GenerateFramePosition(parent, child); | |
153 return AppendUniqueSuffix(top, candidate, likely_unique_suffix); | |
154 } | |
155 | |
156 } // namespace | |
157 | |
158 UniqueNameHelper::UniqueNameHelper(RenderFrameImpl* render_frame) | |
159 : render_frame_(render_frame) {} | |
160 | |
161 UniqueNameHelper::~UniqueNameHelper() {} | |
162 | |
163 std::string UniqueNameHelper::GenerateNameForNewChildFrame( | |
164 blink::WebFrame* parent, | |
165 const std::string& name) { | |
166 return CalculateNewName(parent, nullptr, name); | |
167 } | |
168 | |
169 void UniqueNameHelper::UpdateName(const std::string& name) { | |
170 // The unique name of the main frame is always the empty string. | |
171 if (!GetWebFrame()->parent()) | |
172 return; | |
173 // It's important to clear this before calculating a new name, as the | |
174 // calculation checks for collisions with existing unique names. | |
175 unique_name_.clear(); | |
176 unique_name_ = CalculateNewName(GetWebFrame()->parent(), GetWebFrame(), name); | |
177 } | |
178 | |
179 blink::WebLocalFrame* UniqueNameHelper::GetWebFrame() const { | |
180 return render_frame_->GetWebFrame(); | |
181 } | |
182 | |
183 } // namespace content | |
OLD | NEW |