summaryrefslogtreecommitdiff
path: root/chromium/content/common/unique_name_helper.h
blob: 725b67f89a22559a1f06c4d38cb255e2cacc9225 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_COMMON_UNIQUE_NAME_HELPER_H_
#define CONTENT_COMMON_UNIQUE_NAME_HELPER_H_

#include <string>
#include <vector>

#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "content/common/content_export.h"

namespace content {

// Frame helper that manages the details of generating a quasi-stable unique
// name for the frame. The name is unique within a page, and is used for:
// - matching up session history items with recreated frames
// - web test results
//
// Description of the current unique name format
// ---------------------------------------------
// Changing the format of unique name has a high cost, because it breaks
// backwards compatibility of session history (which stores unique names
// generated in the past on user's disk). The evolution of unique name in a
// mostly backwards-compatible way has resulted in the rather baroque format...
//
// uniqueName ::= <nullForMainFrame> | <assignedName> | <generatedName>
// Note: generatedName is used if assignedName is non-unique / conflicts with
// other frame's unique name.
//
// assignedName ::= value of iframe's name attribute
//                  or value assigned to window.name
// Note: The name must be assigned *before* the first real commit: afterwards,
// the unique name is immutable.
//
// generatedName ::= oldGeneratedName newUniqueSuffix?
// Note: newUniqueSuffix is only present if oldGeneratedName is not unique.
//
// oldGeneratedName ::= "<!--framePath //" ancestorChain
//                      "/<!--frame" childCount "-->-->"
// Note: oldGeneratedName is generated by the internal GenerateCandidate()
// 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
// Note: Two examples are provided below, with each portion of the name from a
// distinct ancestor annotated.
//
// Example ancestor chain #1:
//     "grandparent/parent"
//     |    #1     |  #2  |
// Example ancestor chain #2:
//     "<!--framePath //foo/bar/<!--frame42-->-->/blah/foobar"
//     |                    #1                   | #2 |  #3  |
//
// newUniqueSuffix ::= "<!--framePosition" framePosition "/" retryNumber "-->"
//
// framePosition ::= "-" numberOfSiblingsBeforeChild
//                   [ framePosition-forParent? ]
//
// retryNumber ::= smallest non-negative integer resulting in unique name
class CONTENT_EXPORT UniqueNameHelper {
 public:
  // Adapter class so UniqueNameHelper can be used with both RenderFrameImpl and
  // ExplodedFrameState.
  class CONTENT_EXPORT FrameAdapter {
   public:
    FrameAdapter() {}
    virtual ~FrameAdapter();

    virtual bool IsMainFrame() const = 0;
    virtual bool IsCandidateUnique(base::StringPiece name) const = 0;
    // Returns the number of sibling frames of this frame. Note this should not
    // include this frame in the count.
    virtual int GetSiblingCount() const = 0;
    virtual int GetChildCount() const = 0;
    // Sets the reference point for iterations that walk up the frame tree.
    enum class BeginPoint {
      // This should be the default: it indicates the logical iteration
      // operation began on this frame and the walking logic should retrieve the
      // parent frame as normal.
      kParentFrame,
      // For implementing the pending child frame adapter, which delegates to
      // its future parent's FrameAdapter. Walking up the tree should not skip
      // this frame; instead it should treat this frame as the parent, since the
      // logical iteration began with a pending child frame.
      kThisFrame,
    };
    // Returns a vector of the strings representing the name of each frame in
    // the chain from this frame to the root frame. |begin_point| indicates the
    // reference point for starting the collection. |should_stop| is a
    // boolean predicate that indicates when to stop collection of names.
    virtual std::vector<base::StringPiece> CollectAncestorNames(
        BeginPoint begin_point,
        bool (*should_stop)(base::StringPiece)) const = 0;
    // Returns a vector of ints representing the child index of each frame in
    // the chain from this frame to the root.
    virtual std::vector<int> GetFramePosition(BeginPoint begin_point) const = 0;

   private:
    DISALLOW_COPY_AND_ASSIGN(FrameAdapter);
  };

  struct Replacement {
    Replacement(std::string old_name, std::string new_name);

    const std::string old_name;
    const std::string new_name;
  };

  explicit UniqueNameHelper(FrameAdapter* frame);
  ~UniqueNameHelper();

  // Returns the generated unique name.
  const std::string& value() const { return unique_name_; }

  // Used to propagate an already calculated unique name.
  //
  // TODO(lukasza): It would be nice to assert uniqueness of the propagated
  // name here 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 unique name: see https://crbug.com/558680#c14.
  void set_propagated_name(const std::string& name) { unique_name_ = name; }

  // Note: when creating a new child frame, the unique name needs to be
  // calculated before the child frame is created. To avoid this chicken and
  // egg problem, this method is designed to be called on the *parent* frame of
  // the future new child frame and return the value the new child frame should
  // use.
  //
  // |is_created_by_script| indicates if the new child is created via javascript
  // (as opposed to being created from static html). In this case, the new child
  // cannot be reliably identified in session history entries. To avoid
  // accidentally using incorrect session history entries such a child gets a
  // fresh, random, unique name every time it is created or recreated. See also
  // https://crbug.com/500260.
  std::string GenerateNameForNewChildFrame(const std::string& name,
                                           bool is_created_by_script) const;

  // Called after a browsing context name change to generate a new name. Note
  // that this should not be called if the frame is no longer displaying the
  // initial empty document, as unique name changes after that point will break
  // history navigations. See https://crbug.com/607205.
  void UpdateName(const std::string& name);

  // Prevents future changes of the unique name. This avoids changing the
  // unique name when the frame's assigned name changes in the future.
  //
  // Such freeze is desirable in case of frames created from javascript
  // (see |is_created_by_script| parameter of GenerateNameForNewChildFrame)
  // because their order of creation can be undeterministic and therefore
  // their unique name should NOT be derived from their assigned name
  // (because in case of a conflicting assigned name, their final unique
  // names would be undetetministic potentially leading to
  // https://crbug.com/500260).
  //
  // TODO(dcheng, lukasza): Consider making frame's unique name immutable (and
  // if this is possible remove the explicit Freeze method). For more context
  // see https://crbug.com/607205#c6.
  void Freeze() { frozen_ = true; }

  // Helper to update legacy names generated for PageState v24 and earlier. This
  // function should be invoked starting from the root of the tree, traversing
  // downwards. The exact traversal order is unimportant as long as this
  // function has been called on all ancestor frames of the node associated with
  // |legacy_name|. A single instance of |replacements| should be used per frame
  // tree.
  static std::string UpdateLegacyNameFromV24(
      std::string legacy_name,
      std::vector<Replacement>* replacements);

  static std::string CalculateLegacyNameForTesting(const FrameAdapter* frame,
                                                   const std::string& name);

  // Enters a mode causing future uses of GenerateNameForNewChildFrame to
  // preserve the original, stable unique name, so that it can be recovered
  // (e.g. for web tests) by ExtractStableNameForTesting method below. This
  // mode is not enabled by default, because it makes unique names longer, and
  // thus negatively affects memory usage.
  static void PreserveStableUniqueNameForTesting();

  // Removes the random components of |unique_name|, so it can be used in test
  // output that needs to be stable across test runs.
  //
  // Note: This method only works if |unique_name| was calculated after calling
  // PreserveStableUniqueNameForTesting (see above).
  static std::string ExtractStableNameForTesting(base::StringPiece unique_name);

 private:
  FrameAdapter* const frame_;
  std::string unique_name_;
  bool frozen_ = false;

  DISALLOW_COPY_AND_ASSIGN(UniqueNameHelper);
};

}  // namespace content

#endif  // CONTENT_COMMON_UNIQUE_NAME_HELPER_H_