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

Unified Diff: third_party/WebKit/Source/core/page/FrameTree.cpp

Issue 1968213002: Try harder to make sure that blink::FrameTree::m_uniqueName is truly unique. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed CR comments from dcheng@. Created 4 years, 7 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/WebKit/Source/core/page/FrameTree.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/Source/core/page/FrameTree.cpp
diff --git a/third_party/WebKit/Source/core/page/FrameTree.cpp b/third_party/WebKit/Source/core/page/FrameTree.cpp
index c218e8b0f95a0bb7b3a1206b5158dc434674bcae..364082c9c8ade53f01b6f2f81145911746916fdf 100644
--- a/third_party/WebKit/Source/core/page/FrameTree.cpp
+++ b/third_party/WebKit/Source/core/page/FrameTree.cpp
@@ -27,6 +27,7 @@
#include "core/frame/RemoteFrame.h"
#include "core/frame/RemoteFrameView.h"
#include "core/page/Page.h"
+#include "wtf/Assertions.h"
#include "wtf/Vector.h"
#include "wtf/text/CString.h"
#include "wtf/text/StringBuilder.h"
@@ -51,29 +52,42 @@ FrameTree::~FrameTree()
{
}
-void FrameTree::setName(const AtomicString& name, const AtomicString& fallbackName)
+void FrameTree::setName(const AtomicString& name)
{
m_name = name;
- if (!parent()) {
- m_uniqueName = name;
- return;
- }
- // Remove our old frame name so it's not considered in calculateUniqueNameForChildFrame.
+ // Remove our old frame name so it's not considered in calculateUniqueNameForChildFrame
+ // and appendUniqueSuffix calls below.
m_uniqueName = AtomicString();
- m_uniqueName = parent()->tree().calculateUniqueNameForChildFrame(true, name, fallbackName);
+ // Calculate a new unique name based on inputs.
+ if (parent()) {
+ setUniqueName(
+ parent()->tree().calculateUniqueNameForChildFrame(m_thisFrame, name, nullAtom));
+ } else if (name.isEmpty() || !uniqueNameExists(name)) {
+ // Only main frame can have an empty unique name, so for main frames
+ // emptiness guarantees uniquness.
+ setUniqueName(name);
+ } else {
+ setUniqueName(appendUniqueSuffix(name, "<!--framePosition"));
+ }
}
void FrameTree::setPrecalculatedName(const AtomicString& name, const AtomicString& uniqueName)
{
if (!parent()) {
- ASSERT(uniqueName == name);
+ DCHECK(uniqueName == name);
} else {
- ASSERT(!uniqueName.isEmpty());
+ DCHECK(!uniqueName.isEmpty());
}
m_name = name;
+
+ // TODO(lukasza): We would like to assert uniqueness below (i.e. by calling
+ // setUniqueName), but
+ // 1) uniqueness is currently violated by provisional/old frame pairs.
+ // 2) there is an unresolved race between 2 OOPIFs, that can result in a
+ // non-unique |uniqueName| - see https://crbug.com/558680#c14.
m_uniqueName = uniqueName;
}
@@ -123,10 +137,18 @@ Frame* FrameTree::lastChild() const
return m_thisFrame->client()->lastChild();
}
-bool FrameTree::uniqueNameExists(const AtomicString& name) const
+bool FrameTree::uniqueNameExists(const String& uniqueNameCandidate) const
{
+ // This method is currently O(N), where N = number of frames in the tree.
+
+ // Before recalculating or checking unique name, we set m_uniqueName
+ // to an empty string (so the soon-to-be-removed name does not count
+ // as a collision). This means that uniqueNameExists would return
+ // false positives when called with an empty |name|.
+ DCHECK(!uniqueNameCandidate.isEmpty());
+
for (Frame* frame = top(); frame; frame = frame->tree().traverseNext()) {
- if (frame->tree().uniqueName() == name)
+ if (frame->tree().uniqueName() == uniqueNameCandidate)
return true;
}
return false;
@@ -136,25 +158,18 @@ AtomicString FrameTree::calculateUniqueNameForNewChildFrame(
const AtomicString& name,
const AtomicString& fallbackName) const
{
- return calculateUniqueNameForChildFrame(false, name, fallbackName);
-}
+ AtomicString uniqueName = calculateUniqueNameForChildFrame(nullptr, name, fallbackName);
-AtomicString FrameTree::calculateUniqueNameForChildFrame(
- bool existingChildFrame,
- const AtomicString& name,
- const AtomicString& fallbackName) const
-{
- const AtomicString& requestedName = name.isEmpty() ? fallbackName : name;
- if (!requestedName.isEmpty() && !uniqueNameExists(requestedName) && requestedName != "_blank")
- return requestedName;
+ // Caller will likely set the name via setPrecalculatedName, which
+ // unfortunately cannot currently assert uniqueness of the name - let's
+ // therefore assert the uniqueness here.
+ DCHECK(!uniqueNameExists(uniqueName));
- // Create a repeatable name for a child about to be added to us. The name must be
- // unique within the frame tree. The string we generate includes a "path" of names
- // from the root frame down to us. For this path to be unique, each set of siblings must
- // contribute a unique name to the path, which can't collide with any HTML-assigned names.
- // We generate this path component by index in the child list along with an unlikely
- // frame name that can't be set in HTML because it collides with comment syntax.
+ return uniqueName;
+}
+String FrameTree::generateUniqueNameCandidate(bool existingChildFrame) const
+{
const char framePathPrefix[] = "<!--framePath ";
const int framePathPrefixLength = 14;
const int framePathSuffixLength = 3;
@@ -183,9 +198,149 @@ AtomicString FrameTree::calculateUniqueNameForChildFrame(
uniqueName.appendNumber(childCount() - (existingChildFrame ? 1 : 0));
uniqueName.appendLiteral("-->-->");
+ // NOTE: This name might not be unique - see http://crbug.com/588800.
return uniqueName.toAtomicString();
}
+String FrameTree::generateFramePosition(Frame* child) const
+{
+ // This method is currently O(N), where N = number of frames in the tree.
+
+ StringBuilder framePositionBuilder;
+ framePositionBuilder.append("<!--framePosition");
+
+ if (!child) {
+ framePositionBuilder.append('-');
+ framePositionBuilder.appendNumber(childCount());
+ child = m_thisFrame;
+ }
+
+ while (child->tree().parent()) {
+ int numberOfSiblingsBeforeChild = 0;
+ Frame* sibling = child->tree().previousSibling();
+ while (sibling) {
+ sibling = sibling->tree().previousSibling();
+ numberOfSiblingsBeforeChild++;
+ }
+
+ framePositionBuilder.append('-');
+ framePositionBuilder.appendNumber(numberOfSiblingsBeforeChild);
+
+ child = child->tree().parent();
+ }
+
+ // NOTE: The generated string is not guaranteed to be unique, but should
+ // have a better chance of being unique than the string generated by
+ // generateUniqueNameCandidate, because we embed extra information into the
+ // string:
+ // 1) we walk the full chain of ancestors, all the way to the main frame
+ // 2) we use frame-position-within-parent (aka |numberOfSiblingsBeforeChild|)
+ // instead of sibling-count.
+ return framePositionBuilder.toString();
+}
+
+AtomicString FrameTree::appendUniqueSuffix(
+ const String& prefix,
+ const String& likelyUniqueSuffix) const
+{
+ // Verify that we are not doing unnecessary work.
+ DCHECK(uniqueNameExists(prefix));
+
+ // We want unique name to be stable across page reloads - this is why
+ // we use a deterministic |numberOfTries| rather than a random number
+ // (a random number would be more likely to avoid a collision, but
+ // would change after every page reload).
+ int numberOfTries = 0;
+
+ // Keep trying |prefix| + |likelyUniqueSuffix| + |numberOfTries|
+ // concatenations until we get a truly unique name.
+ String candidate;
+ do {
+ StringBuilder uniqueNameBuilder;
+ uniqueNameBuilder.append(prefix);
+ uniqueNameBuilder.append(likelyUniqueSuffix);
+ uniqueNameBuilder.append('/');
+ uniqueNameBuilder.appendNumber(numberOfTries++);
+ uniqueNameBuilder.append("-->");
+
+ candidate = uniqueNameBuilder.toString();
+ } while (uniqueNameExists(candidate));
+ return AtomicString(candidate);
+}
+
+AtomicString FrameTree::calculateUniqueNameForChildFrame(
+ Frame* child,
+ const AtomicString& assignedName,
+ const AtomicString& fallbackName) const
+{
+ // Try to use |assignedName| (i.e. window.name or iframe.name) or |fallbackName| if possible.
+ const AtomicString& requestedName = assignedName.isEmpty() ? fallbackName : assignedName;
+ if (!requestedName.isEmpty() && !uniqueNameExists(requestedName) && requestedName != "_blank")
+ return requestedName;
+
+ String candidate = generateUniqueNameCandidate(child);
+ if (!uniqueNameExists(candidate))
+ return AtomicString(candidate);
+
+ String likelyUniqueSuffix = generateFramePosition(child);
+ return appendUniqueSuffix(candidate, likelyUniqueSuffix);
+
+ // Description of the current unique name format
+ // ---------------------------------------------
+ //
+ // Changing the format of unique name is undesirable, because it breaks
+ // backcompatibility of session history (which stores unique names
+ // generated in the past on user's disk). This incremental,
+ // backcompatibility-aware approach has resulted so far in the following
+ // rather baroque format... :
+ //
+ // uniqueName ::= <assignedName> | <generatedName>
+ // (generatedName is used if assignedName is
+ // 1) non-unique / conflicts with other frame's unique name or
+ // 2) assignedName is empty for a non-main frame)
+ //
+ // assignedName ::= value of iframe's name attribute
+ // or value assigned to window.name
+ //
+ // generatedName ::= oldGeneratedName newUniqueSuffix?
+ // (newUniqueSuffix is only present if oldGeneratedName was
+ // not unique after all)
+ //
+ // oldGeneratedName ::= "<!--framePath //" ancestorChain "/<!--frame" childCount "-->-->"
+ // (main frame is special - oldGeneratedName for main frame
+ // is always the frame's assignedName; oldGeneratedName is
+ // generated by generateUniqueNameCandidate method).
+ //
+ // childCount ::= current number of siblings
+ //
+ // ancestorChain ::= concatenated unique names of ancestor chain,
+ // terminated on the first ancestor (if any) starting with "<!--framePath"
+ // each ancestor's unique name is separated by "/" character
+ // ancestorChain example1: "grandparent/parent"
+ // (ancestor's unique names : #1--^ | #2-^ )
+ // ancestorChain example2: "<!--framePath //foo/bar/<!--frame42-->-->/blah/foobar"
+ // (ancestor's unique names : ^--#1--^ | #2 | #3-^ )
+ //
+ // newUniqueSuffix ::= "<!--framePosition" framePosition "/" retryNumber "-->"
+ //
+ // framePosition ::= "-" numberOfSiblingsBeforeChild [ framePosition-forParent? ]
+ // | <empty string for main frame>
+ //
+ // retryNumber ::= smallest non-negative integer resulting in unique name
+}
+
+void FrameTree::setUniqueName(const AtomicString& uniqueName)
+{
+ // Main frame is the only frame that can have an empty unique name.
+ if (parent()) {
+ DCHECK(!uniqueName.isEmpty() && !uniqueNameExists(uniqueName));
+ } else {
+ DCHECK(uniqueName.isEmpty() || !uniqueNameExists(uniqueName));
+ }
+
+ m_uniqueName = uniqueName;
+}
+
Frame* FrameTree::scopedChild(unsigned index) const
{
unsigned scopedIndex = 0;
« no previous file with comments | « third_party/WebKit/Source/core/page/FrameTree.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698