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

content / public / browser / document_user_data.h [blame]

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_PUBLIC_BROWSER_DOCUMENT_USER_DATA_H_
#define CONTENT_PUBLIC_BROWSER_DOCUMENT_USER_DATA_H_

#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/supports_user_data.h"
#include "content/common/content_export.h"
#include "url/origin.h"

namespace content {

class RenderFrameHost;

namespace internal {

CONTENT_EXPORT base::SupportsUserData::Data* GetDocumentUserData(
    const RenderFrameHost* rfh,
    const void* key);

CONTENT_EXPORT void SetDocumentUserData(
    RenderFrameHost* rfh,
    const void* key,
    std::unique_ptr<base::SupportsUserData::Data> data);

CONTENT_EXPORT void RemoveDocumentUserData(RenderFrameHost* rfh,
                                           const void* key);

}  // namespace internal

// This class approximates the lifetime of a single blink::Document in the
// browser process. At the moment RenderFrameHost can correspond to multiple
// blink::Documents (when RenderFrameHost is reused for same-process
// navigation). DocumentUserData is created when a user of an API
// inherits this class and calls CreateForCurrentDocument.
//
// DocumentUserData is cleared when either:
// - RenderFrameHost is deleted, or
// - A cross-document non-bfcached navigation is committed in the same
// RenderFrameHost i.e., DocumentUserData persists when a document is
// put in the BackForwardCache. It will still be present when the user navigates
// back to the document.
//
// DocumentUserData is assumed to be associated with the document in
// the RenderFrameHost. It can be associated even before RenderFrameHost commits
// i.e., on speculative RFHs and gets destroyed along with speculative RFHs if
// it ends up never committing.
//
// In case of crashes, DocumentUserData's lifetime doesn't match blink::Document
// lifetime. DocumentUserData is not cleared when the RenderFrame is deleted but
// is cleared on a subsequent navigation after a crash. This is done to ensure
// that the data associated with RenderFrameHost is not reset when the non-live
// RenderFrameHost is still in use by browser features like permissions or
// settings, which continue to work on the crashed pages. For more details,
// please refer to crbug.com/1099237.
//
// Note: RenderFrameHost is being replaced with RenderDocumentHost
// [https://crbug.com/936696]. After this is completed, every
// DocumentUserData object will be 1:1 with RenderFrameHost. Also
// RenderFrameHost/RenderDocument would start inheriting directly from
// SupportsUserData then we wouldn't need the use of
// GetDocumentUserData/SetDocumentUserData anymore.
//
// This is similar to WebContentsUserData but attached to a document instead.
// Example usage of DocumentUserData:
//
// --- in foo_document_helper.h ---
// class FooDocumentHelper
//     : public content::DocumentUserData<FooDocumentHelper> {
//  public:
//   ~FooDocumentHelper() override;
//
//   // ... more public stuff here ...
//
//  private:
//   // No public constructors to force going through static methods of
//   // DocumentUserData (e.g. CreateForCurrentDocument).
//   explicit FooDocumentHelper(content::RenderFrameHost* rfh);
//
//   friend DocumentUserData;
//   DOCUMENT_USER_DATA_KEY_DECL();
//
//   // ... more private stuff here ...
// };
//
// --- in foo_document_helper.cc ---
// DOCUMENT_USER_DATA_KEY_IMPL(FooDocumentHelper);
//
// FooDocumentHelper::FooDocumentHelper(content::RenderFrameHost* rfh)
//     : DocumentUserData(rfh) {}
//
// FooDocumentHelper::~FooDocumentHelper() {}
//
template <typename T>
class DocumentUserData : public base::SupportsUserData::Data {
 public:
  template <typename... Args>
  static void CreateForCurrentDocument(RenderFrameHost* rfh, Args&&... args) {
    DCHECK(rfh);
    if (!GetForCurrentDocument(rfh)) {
      T* data = new T(rfh, std::forward<Args>(args)...);
      internal::SetDocumentUserData(rfh, UserDataKey(), base::WrapUnique(data));
    }
  }

  static T* GetForCurrentDocument(RenderFrameHost* rfh) {
    DCHECK(rfh);
    return static_cast<T*>(internal::GetDocumentUserData(rfh, UserDataKey()));
  }

  static const T* GetForCurrentDocument(const RenderFrameHost* rfh) {
    DCHECK(rfh);
    return static_cast<const T*>(
        internal::GetDocumentUserData(rfh, UserDataKey()));
  }

  static T* GetOrCreateForCurrentDocument(RenderFrameHost* rfh) {
    DCHECK(rfh);
    if (auto* data = GetForCurrentDocument(rfh)) {
      return data;
    }

    CreateForCurrentDocument(rfh);
    return GetForCurrentDocument(rfh);
  }

  static void DeleteForCurrentDocument(RenderFrameHost* rfh) {
    DCHECK(rfh);
    DCHECK(GetForCurrentDocument(rfh));
    internal::RemoveDocumentUserData(rfh, UserDataKey());
  }

  // Returns the RenderFrameHost associated with `this` object of a subclass
  // which inherits from DocumentUserData.
  //
  // The returned `render_frame_host()` is guaranteed to live as long as `this`
  // DocumentUserData (due to how UserData works - RenderFrameHost
  // owns `this` UserData).  Note that only the lifetime of
  // `render_frame_host()` is guaranteed, but not its state - e.g. the frame may
  // be `!IsActive()`.
  RenderFrameHost& render_frame_host() const { return *render_frame_host_; }

 protected:
  // TODO(crbug.com/40198594): Take a reference instead of a pointer
  // (here + transitively/as-far-as-reasonably-possible in callers).
  explicit DocumentUserData(RenderFrameHost* rfh) : render_frame_host_(rfh) {
    CHECK(rfh);
  }

  // Returns the origin of the associated document.
  //
  // Note that a DocumentUserData can be attached to a speculative
  // RenderFrameHost, but while the RenderFrameHost remains speculative/pending
  // commit, `origin()` returns a meaningless unique opaque origin. Only after
  // the commit will the returned value become meaningful.
  const url::Origin& origin() const {
    // `this` is promptly deleted if `render_frame_host_` commits a
    // cross-document navigation, so it is always safe to simply call
    // `GetLastCommittedOrigin()` directly, even without RenderDocument.
    return render_frame_host().GetLastCommittedOrigin();
  }

 private:
  static const void* UserDataKey() { return &T::kUserDataKey; }

  // This is a pointer (rather than a reference) to ensure that go/miracleptr
  // can cover this field (see also //base/memory/raw_ptr.md).
  const raw_ptr<RenderFrameHost> render_frame_host_ = nullptr;
};

// Users won't be able to instantiate the template if they miss declaring the
// user data key.
// This macro declares a static variable inside the class that inherits from
// DocumentUserData. The address of this static variable is used as
// the key to store/retrieve an instance of the class on/from a WebState.
#define DOCUMENT_USER_DATA_KEY_DECL() static const int kUserDataKey = 0

// This macro instantiates the static variable declared by the previous macro.
// It must live in a .cc file to ensure that there is only one instantiation
// of the static variable.
#define DOCUMENT_USER_DATA_KEY_IMPL(Type) const int Type::kUserDataKey

}  // namespace content

#endif  // CONTENT_PUBLIC_BROWSER_DOCUMENT_USER_DATA_H_