summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/bindings/v8_private_property.h
blob: 8dfd6ec588cafa0551db69c0614789e24926caf1 (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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// Copyright 2016 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PRIVATE_PROPERTY_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PRIVATE_PROPERTY_H_

#include <memory>

#include "base/memory/ptr_util.h"
#include "third_party/blink/renderer/platform/bindings/script_promise_properties.h"
#include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h"
#include "v8/include/v8.h"

namespace blink {

class ScriptWrappable;

// TODO(peria): Remove properties just to keep V8 objects alive.
// e.g. IDBCursor.Request.
// Apply |X| for each pair of (InterfaceName, PrivateKeyName).
#define V8_PRIVATE_PROPERTY_FOR_EACH(X)               \
  X(CustomElement, Document)                          \
  X(CustomElement, IsInterfacePrototypeObject)        \
  X(CustomElement, NamespaceURI)                      \
  X(CustomElement, TagName)                           \
  X(CustomElement, Type)                              \
  X(CustomElementLifecycle, AttachedCallback)         \
  X(CustomElementLifecycle, AttributeChangedCallback) \
  X(CustomElementLifecycle, CreatedCallback)          \
  X(CustomElementLifecycle, DetachedCallback)         \
  X(DOMException, Error)                              \
  X(Global, Event)                                    \
  X(IDBCursor, Request)                               \
  X(IntersectionObserver, Callback)                   \
  X(MessageChannel, Port1)                            \
  X(MessageChannel, Port2)                            \
  X(MessageEvent, CachedData)                         \
  X(MutationObserver, Callback)                       \
  X(NamedConstructor, Initialized)                    \
  X(PopStateEvent, State)                             \
  X(SameObject, DetectedBarcodeCornerPoints)          \
  X(SameObject, DetectedFaceLandmarks)                \
  X(SameObject, NotificationActions)                  \
  X(SameObject, NotificationData)                     \
  X(SameObject, NotificationVibrate)                  \
  X(SameObject, PerformanceLongTaskTimingAttribution) \
  X(SameObject, PushManagerSupportedContentEncodings) \
  SCRIPT_PROMISE_PROPERTIES(X, Promise)               \
  SCRIPT_PROMISE_PROPERTIES(X, Resolver)

// The getter's name for a private property.
#define V8_PRIVATE_PROPERTY_GETTER_NAME(InterfaceName, PrivateKeyName) \
  Get##InterfaceName##PrivateKeyName

// The member variable's name for a private property.
#define V8_PRIVATE_PROPERTY_MEMBER_NAME(InterfaceName, PrivateKeyName) \
  m_symbol##InterfaceName##PrivateKeyName

// The string used to create a private symbol.  Must be unique per V8 instance.
#define V8_PRIVATE_PROPERTY_SYMBOL_STRING(InterfaceName, PrivateKeyName) \
  #InterfaceName "#" #PrivateKeyName  // NOLINT(whitespace/indent)

// Provides access to V8's private properties.
//
// Usage 1) Fast path to use a pre-registered symbol.
//   auto private = V8PrivateProperty::getMessageEventCachedData(isolate);
//   v8::Local<v8::Object> object = ...;
//   v8::Local<v8::Value> value;
//   if (!private.GetOrUndefined(object).ToLocal(&value)) return;
//   value = ...;
//   private.set(object, value);
//
// Usage 2) Slow path to create a global private symbol.
//   const char symbolName[] = "Interface#PrivateKeyName";
//   auto private = V8PrivateProperty::createSymbol(isolate, symbolName);
//   ...
class PLATFORM_EXPORT V8PrivateProperty {
  USING_FAST_MALLOC(V8PrivateProperty);
  WTF_MAKE_NONCOPYABLE(V8PrivateProperty);

 public:
  enum CachedAccessorSymbol : unsigned {
    kNoCachedAccessor = 0,
    kWindowDocumentCachedAccessor,
  };

  // Provides fast access to V8's private properties.
  //
  // Retrieving/creating a global private symbol from a string is very
  // expensive compared to get or set a private property.  This class
  // provides a way to cache a private symbol and re-use it.
  class PLATFORM_EXPORT Symbol {
    STACK_ALLOCATED();

   public:
    bool HasValue(v8::Local<v8::Object> object) const {
      return object->HasPrivate(GetContext(), private_symbol_).ToChecked();
    }

    // Returns the value of the private property if set, or undefined.
    WARN_UNUSED_RESULT v8::MaybeLocal<v8::Value> GetOrUndefined(
        v8::Local<v8::Object> object) const {
      return object->GetPrivate(GetContext(), private_symbol_);
    }

    bool Set(v8::Local<v8::Object> object, v8::Local<v8::Value> value) const {
      return object->SetPrivate(GetContext(), private_symbol_, value)
          .ToChecked();
    }

    bool DeleteProperty(v8::Local<v8::Object> object) const {
      return object->DeletePrivate(GetContext(), private_symbol_).ToChecked();
    }

    v8::Local<v8::Private> GetPrivate() const { return private_symbol_; }

   private:
    friend class V8PrivateProperty;
    // The following classes are exceptionally allowed to call to
    // getFromMainWorld.
    friend class V8CustomEvent;
    friend class V8ExtendableMessageEvent;

    Symbol(v8::Isolate* isolate, v8::Local<v8::Private> private_symbol)
        : private_symbol_(private_symbol), isolate_(isolate) {}

    // To get/set private property, we should use the current context.
    v8::Local<v8::Context> GetContext() const {
      return isolate_->GetCurrentContext();
    }

    // Only friend classes are allowed to use this API.
    WARN_UNUSED_RESULT v8::MaybeLocal<v8::Value> GetFromMainWorld(
        ScriptWrappable*);

    v8::Local<v8::Private> private_symbol_;
    v8::Isolate* isolate_;
  };

  static std::unique_ptr<V8PrivateProperty> Create() {
    return base::WrapUnique(new V8PrivateProperty());
  }

#define V8_PRIVATE_PROPERTY_DEFINE_GETTER(InterfaceName, KeyName)              \
  static Symbol V8_PRIVATE_PROPERTY_GETTER_NAME(/* // NOLINT */                \
                                                InterfaceName, KeyName)(       \
      v8::Isolate * isolate) {                                                 \
    V8PrivateProperty* private_prop =                                          \
        V8PerIsolateData::From(isolate)->PrivateProperty();                    \
    v8::Eternal<v8::Private>& property_handle =                                \
        private_prop->V8_PRIVATE_PROPERTY_MEMBER_NAME(InterfaceName, KeyName); \
    if (UNLIKELY(property_handle.IsEmpty())) {                                 \
      property_handle.Set(                                                     \
          isolate, CreateV8Private(isolate, V8_PRIVATE_PROPERTY_SYMBOL_STRING( \
                                                InterfaceName, KeyName)));     \
    }                                                                          \
    return Symbol(isolate, property_handle.Get(isolate));                      \
  }

  V8_PRIVATE_PROPERTY_FOR_EACH(V8_PRIVATE_PROPERTY_DEFINE_GETTER)
#undef V8_PRIVATE_PROPERTY_DEFINE_GETTER

  // TODO(peria): Do not use this specialized hack. See a TODO comment
  // on m_symbolWindowDocumentCachedAccessor.
  static Symbol GetWindowDocumentCachedAccessor(v8::Isolate* isolate) {
    V8PrivateProperty* private_prop =
        V8PerIsolateData::From(isolate)->PrivateProperty();
    if (UNLIKELY(
            private_prop->symbol_window_document_cached_accessor_.IsEmpty())) {
      private_prop->symbol_window_document_cached_accessor_.Set(
          isolate, CreateCachedV8Private(
                       isolate, V8_PRIVATE_PROPERTY_SYMBOL_STRING(
                                    "Window", "DocumentCachedAccessor")));
    }
    return Symbol(
        isolate, private_prop->symbol_window_document_cached_accessor_.NewLocal(
                     isolate));
  }

  static Symbol GetCachedAccessor(v8::Isolate* isolate,
                                  CachedAccessorSymbol symbol_id) {
    switch (symbol_id) {
      case kWindowDocumentCachedAccessor:
        return GetWindowDocumentCachedAccessor(isolate);
      case kNoCachedAccessor:
        break;
    };
    NOTREACHED();
    return GetSymbol(isolate, "unexpected cached accessor");
  }

  static Symbol GetSymbol(v8::Isolate* isolate, const char* symbol) {
    return Symbol(isolate, CreateCachedV8Private(isolate, symbol));
  }

 private:
  V8PrivateProperty() = default;

  static v8::Local<v8::Private> CreateV8Private(v8::Isolate*,
                                                const char* symbol);
  // TODO(peria): Remove this method. We should not use v8::Private::ForApi().
  static v8::Local<v8::Private> CreateCachedV8Private(v8::Isolate*,
                                                      const char* symbol);

#define V8_PRIVATE_PROPERTY_DECLARE_MEMBER(InterfaceName, KeyName) \
  v8::Eternal<v8::Private> V8_PRIVATE_PROPERTY_MEMBER_NAME(        \
      InterfaceName, KeyName);  // NOLINT(readability/naming/underscores)
  V8_PRIVATE_PROPERTY_FOR_EACH(V8_PRIVATE_PROPERTY_DECLARE_MEMBER)
#undef V8_PRIVATE_PROPERTY_DECLARE_MEMBER

  // TODO(peria): Do not use this specialized hack for
  // Window#DocumentCachedAccessor. This is required to put v8::Private key in
  // a snapshot, and it cannot be a v8::Eternal<> due to V8 serializer's
  // requirement.
  ScopedPersistent<v8::Private> symbol_window_document_cached_accessor_;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PRIVATE_PROPERTY_H_