summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/bindings/core/v8/v8_html_constructor.cc
blob: 1a0d6564935edd779179bd7322b8bc1eacb86d72 (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
// 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.

#include "third_party/blink/renderer/bindings/core/v8/v8_html_constructor.h"

#include "third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_html_element.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/html/custom/custom_element_registry.h"
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
#include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
#include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"

namespace blink {

// https://html.spec.whatwg.org/multipage/dom.html#html-element-constructors
void V8HTMLConstructor::HtmlConstructor(
    const v8::FunctionCallbackInfo<v8::Value>& info,
    const WrapperTypeInfo& wrapper_type_info,
    const HTMLElementType element_interface_name) {
  TRACE_EVENT0("blink", "HTMLConstructor");
  DCHECK(info.IsConstructCall());

  v8::Isolate* isolate = info.GetIsolate();
  ScriptState* script_state = ScriptState::Current(isolate);
  v8::Local<v8::Value> new_target = info.NewTarget();

  if (!script_state->ContextIsValid()) {
    V8ThrowException::ThrowError(isolate, "The context has been destroyed");
    return;
  }

  if (!script_state->World().IsMainWorld()) {
    V8ThrowException::ThrowTypeError(isolate, "Illegal constructor");
    return;
  }

  // 2. If NewTarget is equal to the active function object, then
  // throw a TypeError and abort these steps.
  v8::Local<v8::Function> active_function_object =
      script_state->PerContextData()->ConstructorForType(&wrapper_type_info);
  if (new_target == active_function_object) {
    V8ThrowException::ThrowTypeError(isolate, "Illegal constructor");
    return;
  }

  LocalDOMWindow* window = LocalDOMWindow::From(script_state);
  CustomElementRegistry* registry = window->customElements();

  // 3. Let definition be the entry in registry with constructor equal to
  // NewTarget.
  // If there is no such definition, then throw a TypeError and abort these
  // steps.
  ScriptCustomElementDefinition* definition =
      ScriptCustomElementDefinition::ForConstructor(script_state, registry,
                                                    new_target);
  if (!definition) {
    V8ThrowException::ThrowTypeError(isolate, "Illegal constructor");
    return;
  }

  const AtomicString& local_name = definition->Descriptor().LocalName();
  const AtomicString& name = definition->Descriptor().GetName();

  if (local_name == name) {
    // Autonomous custom element
    // 4.1. If the active function object is not HTMLElement, then throw a
    // TypeError
    if (!V8HTMLElement::GetWrapperTypeInfo()->Equals(&wrapper_type_info)) {
      V8ThrowException::ThrowTypeError(isolate,
                                       "Illegal constructor: autonomous custom "
                                       "elements must extend HTMLElement");
      return;
    }
  } else {
    // Customized built-in element
    // 5. If local name is not valid for interface, throw TypeError
    if (htmlElementTypeForTag(local_name) != element_interface_name) {
      V8ThrowException::ThrowTypeError(isolate,
                                       "Illegal constructor: localName does "
                                       "not match the HTML element interface");
      return;
    }
  }

  ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
                                 "HTMLElement");
  // 6. Let prototype be Get(NewTarget, "prototype"). Rethrow any exceptions.
  v8::Local<v8::Value> prototype;
  v8::Local<v8::String> prototype_string = V8AtomicString(isolate, "prototype");
  if (!new_target.As<v8::Object>()
           ->Get(script_state->GetContext(), prototype_string)
           .ToLocal(&prototype)) {
    return;
  }

  // 7. If Type(prototype) is not Object, then: ...
  if (!prototype->IsObject()) {
    if (V8PerContextData* per_context_data = V8PerContextData::From(
            new_target.As<v8::Object>()->CreationContext())) {
      prototype = per_context_data->PrototypeForType(&wrapper_type_info);
    } else {
      V8ThrowException::ThrowError(isolate, "The context has been destroyed");
      return;
    }
  }

  // 8. If definition's construction stack is empty...
  Element* element;
  if (definition->GetConstructionStack().IsEmpty()) {
    // This is an element being created with 'new' from script
    element = definition->CreateElementForConstructor(*window->document());
  } else {
    element = definition->GetConstructionStack().back();
    if (element) {
      // This is an element being upgraded that has called super
      definition->GetConstructionStack().back().Clear();
    } else {
      // During upgrade an element has invoked the same constructor
      // before calling 'super' and that invocation has poached the
      // element.
      exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                        "this instance is already constructed");
      return;
    }
  }
  const WrapperTypeInfo* wrapper_type = element->GetWrapperTypeInfo();
  v8::Local<v8::Object> wrapper = V8DOMWrapper::AssociateObjectWithWrapper(
      isolate, element, wrapper_type, info.Holder());
  // If the element had a wrapper, we now update and return that
  // instead.
  V8SetReturnValue(info, wrapper);

  // 11. Perform element.[[SetPrototypeOf]](prototype). Rethrow any exceptions.
  // Note: I don't think this prototype set *can* throw exceptions.
  wrapper->SetPrototype(script_state->GetContext(), prototype.As<v8::Object>())
      .ToChecked();
}
}  // namespace blink