910e62b5创建于 1月15日历史提交
/*
 * Copyright (C) 2010 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_H_

#include "build/build_config.h"
#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
#include "third_party/blink/renderer/platform/bindings/wrapper_type_info.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/type_traits.h"
#include "v8/include/v8.h"

namespace blink {

class DOMDataStore;
class ScriptState;

// ScriptWrappable provides a way to map from/to C++ DOM implementation to/from
// JavaScript object (platform object).  ToV8() converts a ScriptWrappable to
// a v8::Object and ToScriptWrappable() converts a v8::Object back to
// a ScriptWrappable.  v8::Object as platform object is called "wrapper object".
// The wrapper object for the main world is stored in ScriptWrappable.  Wrapper
// objects for other worlds are stored in DOMDataStore.
class PLATFORM_EXPORT ScriptWrappable : public v8::Object::Wrappable {
 public:
  // This is a type dispatcher from ScriptWrappable* to a subtype, optimized for
  // use cases that perform downcasts multiple times.
  class TypeDispatcher final {
    STACK_ALLOCATED();

   public:
    // The input parameter `script_wrappable` must not be null.
    explicit TypeDispatcher(ScriptWrappable* script_wrappable)
        : script_wrappable_(script_wrappable),
          wrapper_type_info_(ToWrapperTypeInfo(script_wrappable)) {}
    ~TypeDispatcher() = default;

    TypeDispatcher(const TypeDispatcher&) = delete;
    TypeDispatcher& operator=(const TypeDispatcher&) = delete;

    // Downcasts the ScriptWrappable to the given template parameter type or
    // nullptr if the ScriptWrappable doesn't implement the given type. The
    // inheritance is checked with WrapperTypeInfo, i.e. the check is based on
    // the IDL definitions in *.idl files, not based on C++ class inheritance.
    template <typename T>
    T* DowncastTo() {
      if (wrapper_type_info_->IsSubclass(T::GetStaticWrapperTypeInfo()))
        return static_cast<T*>(script_wrappable_);
      return nullptr;
    }

    // Downcasts the ScriptWrappable to the given template parameter type iff
    // the ScriptWrappable implements the type as the most derived class (i.e.
    // the ScriptWrappable does _not_ implement a subtype of the given type).
    // Otherwise, returns nullptr. The inheritance is checked with
    // WrapperTypeInfo, i.e. the check is based on the IDL definitions in *.idl
    // files, not based on C++ class inheritance.
    template <typename T>
    T* ToMostDerived() {
      if (wrapper_type_info_ == T::GetStaticWrapperTypeInfo())
        return static_cast<T*>(script_wrappable_);
      return nullptr;
    }

   private:
    ScriptWrappable* script_wrappable_ = nullptr;
    const WrapperTypeInfo* wrapper_type_info_ = nullptr;
  };

  ScriptWrappable(const ScriptWrappable&) = delete;
  ScriptWrappable& operator=(const ScriptWrappable&) = delete;
  ~ScriptWrappable() override = default;

  const char* GetHumanReadableName() const override;

  void Trace(Visitor*) const override;

  // Returns a wrapper object, creating it if needed.
  v8::Local<v8::Value> ToV8(ScriptState*);

  // This overload is used for the case when a `ToV8()` caller does not have
  // `script_state` but does have a receiver object (a creation context object)
  // which is needed to create a wrapper. If a wrapper object corresponding to
  // the receiver object exists, `ToV8()` can return it without a call to
  // `ScriptState::ForRelevantRealm`, which is slow.
  v8::Local<v8::Value> ToV8(v8::Isolate*,
                            v8::Local<v8::Object> creation_context_object);

  // Creates and returns a new wrapper object. This DCHECKs that a wrapper does
  // not exist yet. Use ToV8() if a wrapper might already exist.
  virtual v8::Local<v8::Value> Wrap(ScriptState*);

  // Associates the instance with the given |wrapper| if this instance is not
  // yet associated with any wrapper.  Returns the wrapper already associated
  // or |wrapper| if not yet associated.
  // The caller should always use the returned value rather than |wrapper|.
  [[nodiscard]] virtual v8::Local<v8::Object> AssociateWithWrapper(
      v8::Isolate*,
      const WrapperTypeInfo*,
      v8::Local<v8::Object> wrapper);

 protected:
  ScriptWrappable() = default;

 private:
  static_assert(
      std::is_trivially_destructible<
          TraceWrapperV8Reference<v8::Object>>::value,
      "TraceWrapperV8Reference<v8::Object> should be trivially destructible.");

  // Inline storage for the a single wrapper reference. Only
  // `DOMDataStore::UncheckedInlineStorageForWrappable()` should access this
  // field.
  TraceWrapperV8Reference<v8::Object> wrapper_;
  friend class DOMDataStore;
};

template <typename T>
  requires std::derived_from<T, ScriptWrappable>
T* ToScriptWrappable(v8::Isolate* isolate, v8::Local<v8::Object> wrapper) {
  const WrapperTypeInfo* wrapper_type_info = T::GetStaticWrapperTypeInfo();
  return static_cast<T*>(v8::Object::Unwrap<ScriptWrappable>(
      isolate, wrapper,
      v8::CppHeapPointerTagRange(wrapper_type_info->this_tag,
                                 wrapper_type_info->max_subclass_tag)));
}

// Defines |GetWrapperTypeInfo| virtual method which returns the WrapperTypeInfo
// of the instance. Also declares a static member of type WrapperTypeInfo, of
// which the definition is given by the IDL code generator.
//
// All the derived classes of ScriptWrappable, regardless of directly or
// indirectly, must write this macro in the class definition as long as the
// class has a corresponding .idl file.
#define DEFINE_WRAPPERTYPEINFO()                               \
 public:                                                       \
  const WrapperTypeInfo* GetWrapperTypeInfo() const override { \
    return &wrapper_type_info_;                                \
  }                                                            \
  static const WrapperTypeInfo* GetStaticWrapperTypeInfo() {   \
    return &wrapper_type_info_;                                \
  }                                                            \
                                                               \
 private:                                                      \
  static const WrapperTypeInfo& wrapper_type_info_

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_SCRIPT_WRAPPABLE_H_