Chromium Code Reviews| 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..da272d97a28a4279a38303a19bd02ae4c94d0ca1 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,43 @@ 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 ensureUniquenessOfUniqueName 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(ensureUniquenessOfUniqueName( |
| + 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 +138,18 @@ Frame* FrameTree::lastChild() const |
| return m_thisFrame->client()->lastChild(); |
| } |
| -bool FrameTree::uniqueNameExists(const AtomicString& name) const |
| +bool FrameTree::uniqueNameExists(const AtomicString& 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 +159,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; |
| +} |
| +AtomicString FrameTree::generateOldStyleMaybeUniqueName(bool existingChildFrame) const |
| +{ |
| const char framePathPrefix[] = "<!--framePath "; |
| const int framePathPrefixLength = 14; |
| const int framePathSuffixLength = 3; |
| @@ -183,9 +199,146 @@ 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::generateLikelyUniqueSuffix(Frame* child) const |
| +{ |
| + // This method is currently O(N), where N = number of frames in the tree. |
| + |
| + StringBuilder likelyUniqueSuffixBuilder; |
| + likelyUniqueSuffixBuilder.append("<!--framePosition"); |
| + |
| + if (!child) { |
|
dcheng
2016/05/31 19:57:47
When will child be null?
Łukasz Anforowicz
2016/05/31 23:50:20
|child| will be null in cases when we need to calc
|
| + likelyUniqueSuffixBuilder.append('-'); |
| + likelyUniqueSuffixBuilder.appendNumber(childCount()); |
| + child = m_thisFrame; |
| + } |
| + |
| + while (child->tree().parent()) { |
| + int numberOfSiblingsBeforeChild = 0; |
| + Frame* sibling = child->tree().previousSibling(); |
| + while (sibling) { |
| + sibling = sibling->tree().previousSibling(); |
| + numberOfSiblingsBeforeChild++; |
| + } |
| + |
| + likelyUniqueSuffixBuilder.append('-'); |
| + likelyUniqueSuffixBuilder.appendNumber(numberOfSiblingsBeforeChild); |
| + |
| + child = child->tree().parent(); |
| + } |
| + |
| + // NOTE: The generated suffix is not guaranteed to be unique, but should |
| + // have a better chance of being unique than the string generated by |
| + // generateOldStyleMaybeUniqueName, 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|) |
| + return likelyUniqueSuffixBuilder.toString(); |
| +} |
| + |
| +AtomicString FrameTree::ensureUniquenessOfUniqueName( |
| + const AtomicString& notYetUniqueName, |
| + const String& likelyUniqueSuffix) const |
| +{ |
| + // Verify that we are not doing unnecessary work. |
| + DCHECK(uniqueNameExists(notYetUniqueName)); |
| + |
| + // 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 |notYetUniqueName| + |likelyUniqueSuffix| + |numberOfTries| |
| + // concatenations until we get a truly unique name. |
| + AtomicString uniqueNameCandidate; |
| + do { |
| + StringBuilder uniqueNameBuilder; |
| + uniqueNameBuilder.append(notYetUniqueName); |
| + uniqueNameBuilder.append(likelyUniqueSuffix); |
| + uniqueNameBuilder.append('/'); |
| + uniqueNameBuilder.appendNumber(numberOfTries++); |
| + uniqueNameBuilder.append("-->"); |
| + |
| + uniqueNameCandidate = uniqueNameBuilder.toAtomicString(); |
| + } while (uniqueNameExists(uniqueNameCandidate)); |
| + return uniqueNameCandidate; |
| +} |
| + |
| +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; |
| + |
| + AtomicString uniqueNameCandidate = generateOldStyleMaybeUniqueName(child); |
| + if (!uniqueNameExists(uniqueNameCandidate)) |
| + return uniqueNameCandidate; |
| + |
| + return ensureUniquenessOfUniqueName(uniqueNameCandidate, generateLikelyUniqueSuffix(child)); |
| + |
| + // Description of the current unique name format |
|
Łukasz Anforowicz
2016/05/27 16:55:27
I am not sure if I am happy with the comment here:
dcheng
2016/05/31 19:57:47
This is fine. It's far better than not having anyt
Łukasz Anforowicz
2016/05/31 23:50:20
Acknowledged.
|
| + // --------------------------------------------- |
| + // |
| + // 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) |
| + // |
| + // 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; |