File: CustomElementDefinition.cpp

package info (click to toggle)
chromium-browser 57.0.2987.98-1~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 2,637,852 kB
  • ctags: 2,544,394
  • sloc: cpp: 12,815,961; ansic: 3,676,222; python: 1,147,112; asm: 526,608; java: 523,212; xml: 286,794; perl: 92,654; sh: 86,408; objc: 73,271; makefile: 27,698; cs: 18,487; yacc: 13,031; tcl: 12,957; pascal: 4,875; ml: 4,716; lex: 3,904; sql: 3,862; ruby: 1,982; lisp: 1,508; php: 1,368; exp: 404; awk: 325; csh: 117; jsp: 39; sed: 37
file content (221 lines) | stat: -rw-r--r-- 8,837 bytes parent folder | download
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
// 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 "core/dom/custom/CustomElementDefinition.h"

#include "core/dom/Attr.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/custom/CustomElement.h"
#include "core/dom/custom/CustomElementAdoptedCallbackReaction.h"
#include "core/dom/custom/CustomElementAttributeChangedCallbackReaction.h"
#include "core/dom/custom/CustomElementConnectedCallbackReaction.h"
#include "core/dom/custom/CustomElementDisconnectedCallbackReaction.h"
#include "core/dom/custom/CustomElementReaction.h"
#include "core/dom/custom/CustomElementReactionStack.h"
#include "core/dom/custom/CustomElementUpgradeReaction.h"
#include "core/html/HTMLElement.h"

namespace blink {

CustomElementDefinition::CustomElementDefinition(
    const CustomElementDescriptor& descriptor)
    : m_descriptor(descriptor) {}

CustomElementDefinition::CustomElementDefinition(
    const CustomElementDescriptor& descriptor,
    const HashSet<AtomicString>& observedAttributes)
    : m_descriptor(descriptor),
      m_observedAttributes(observedAttributes),
      m_hasStyleAttributeChangedCallback(
          observedAttributes.contains(HTMLNames::styleAttr.localName())) {}

CustomElementDefinition::~CustomElementDefinition() {}

DEFINE_TRACE(CustomElementDefinition) {
  visitor->trace(m_constructionStack);
}

static String errorMessageForConstructorResult(Element* element,
                                               Document& document,
                                               const QualifiedName& tagName) {
  // https://dom.spec.whatwg.org/#concept-create-element
  // 6.1.4. If result's attribute list is not empty, then throw a
  // NotSupportedError.
  if (element->hasAttributes())
    return "The result must not have attributes";
  // 6.1.5. If result has children, then throw a NotSupportedError.
  if (element->hasChildren())
    return "The result must not have children";
  // 6.1.6. If result's parent is not null, then throw a NotSupportedError.
  if (element->parentNode())
    return "The result must not have a parent";
  // 6.1.7. If result's node document is not document, then throw a
  // NotSupportedError.
  if (&element->document() != &document)
    return "The result must be in the same document";
  // 6.1.8. If result's namespace is not the HTML namespace, then throw a
  // NotSupportedError.
  if (element->namespaceURI() != HTMLNames::xhtmlNamespaceURI)
    return "The result must have HTML namespace";
  // 6.1.9. If result's local name is not equal to localName, then throw a
  // NotSupportedError.
  if (element->localName() != tagName.localName())
    return "The result must have the same localName";
  return String();
}

void CustomElementDefinition::checkConstructorResult(
    Element* element,
    Document& document,
    const QualifiedName& tagName,
    ExceptionState& exceptionState) {
  // https://dom.spec.whatwg.org/#concept-create-element
  // 6.1.3. If result does not implement the HTMLElement interface, throw a
  // TypeError.
  // See https://github.com/whatwg/html/issues/1402 for more clarifications.
  if (!element || !element->isHTMLElement()) {
    exceptionState.throwTypeError(
        "The result must implement HTMLElement interface");
    return;
  }

  // 6.1.4. through 6.1.9.
  const String message =
      errorMessageForConstructorResult(element, document, tagName);
  if (!message.isEmpty())
    exceptionState.throwDOMException(NotSupportedError, message);
}

HTMLElement* CustomElementDefinition::createElementForConstructor(
    Document& document) {
  // TODO(kojii): When HTMLElementFactory has an option not to queue
  // upgrade, call that instead of HTMLElement. HTMLElement is enough
  // for now, but type extension will require HTMLElementFactory.
  HTMLElement* element =
      HTMLElement::create(QualifiedName(nullAtom, descriptor().localName(),
                                        HTMLNames::xhtmlNamespaceURI),
                          document);
  // TODO(davaajav): write this as one call to setCustomElementState instead of
  // two
  element->setCustomElementState(CustomElementState::Undefined);
  element->setCustomElementDefinition(this);
  return element;
}

HTMLElement* CustomElementDefinition::createElementAsync(
    Document& document,
    const QualifiedName& tagName) {
  // https://dom.spec.whatwg.org/#concept-create-element
  // 6. If definition is non-null, then:
  // 6.2. If the synchronous custom elements flag is not set:
  // 6.2.1. Set result to a new element that implements the HTMLElement
  // interface, with no attributes, namespace set to the HTML namespace,
  // namespace prefix set to prefix, local name set to localName, custom
  // element state set to "undefined", and node document set to document.
  HTMLElement* element = HTMLElement::create(tagName, document);
  element->setCustomElementState(CustomElementState::Undefined);
  // 6.2.2. Enqueue a custom element upgrade reaction given result and
  // definition.
  enqueueUpgradeReaction(element);
  return element;
}

CustomElementDefinition::ConstructionStackScope::ConstructionStackScope(
    CustomElementDefinition* definition,
    Element* element)
    : m_constructionStack(definition->m_constructionStack), m_element(element) {
  // Push the construction stack.
  m_constructionStack.push_back(element);
  m_depth = m_constructionStack.size();
}

CustomElementDefinition::ConstructionStackScope::~ConstructionStackScope() {
  // Pop the construction stack.
  DCHECK(!m_constructionStack.back() ||
         m_constructionStack.back() == m_element);
  DCHECK_EQ(m_constructionStack.size(), m_depth);  // It's a *stack*.
  m_constructionStack.pop_back();
}

// https://html.spec.whatwg.org/multipage/scripting.html#concept-upgrade-an-element
void CustomElementDefinition::upgrade(Element* element) {
  DCHECK_EQ(element->getCustomElementState(), CustomElementState::Undefined);

  if (!m_observedAttributes.isEmpty())
    enqueueAttributeChangedCallbackForAllAttributes(element);

  if (element->isConnected() && hasConnectedCallback())
    enqueueConnectedCallback(element);

  bool succeeded = false;
  {
    ConstructionStackScope constructionStackScope(this, element);
    succeeded = runConstructor(element);
  }
  if (!succeeded) {
    element->setCustomElementState(CustomElementState::Failed);
    CustomElementReactionStack::current().clearQueue(element);
    return;
  }

  element->setCustomElementDefinition(this);
}

bool CustomElementDefinition::hasAttributeChangedCallback(
    const QualifiedName& name) const {
  return m_observedAttributes.contains(name.localName());
}

bool CustomElementDefinition::hasStyleAttributeChangedCallback() const {
  return m_hasStyleAttributeChangedCallback;
}

void CustomElementDefinition::enqueueUpgradeReaction(Element* element) {
  CustomElement::enqueue(element, new CustomElementUpgradeReaction(this));
}

void CustomElementDefinition::enqueueConnectedCallback(Element* element) {
  CustomElement::enqueue(element,
                         new CustomElementConnectedCallbackReaction(this));
}

void CustomElementDefinition::enqueueDisconnectedCallback(Element* element) {
  CustomElement::enqueue(element,
                         new CustomElementDisconnectedCallbackReaction(this));
}

void CustomElementDefinition::enqueueAdoptedCallback(Element* element,
                                                     Document* oldDocument,
                                                     Document* newDocument) {
  CustomElementReaction* reaction =
      new CustomElementAdoptedCallbackReaction(this, oldDocument, newDocument);
  CustomElement::enqueue(element, reaction);
}

void CustomElementDefinition::enqueueAttributeChangedCallback(
    Element* element,
    const QualifiedName& name,
    const AtomicString& oldValue,
    const AtomicString& newValue) {
  CustomElement::enqueue(element,
                         new CustomElementAttributeChangedCallbackReaction(
                             this, name, oldValue, newValue));
}

void CustomElementDefinition::enqueueAttributeChangedCallbackForAllAttributes(
    Element* element) {
  // Avoid synchronizing all attributes unless it is needed, while enqueing
  // callbacks "in order" as defined in the spec.
  // https://html.spec.whatwg.org/multipage/scripting.html#concept-upgrade-an-element
  for (const AtomicString& name : m_observedAttributes)
    element->synchronizeAttribute(name);
  for (const auto& attribute : element->attributesWithoutUpdate()) {
    if (hasAttributeChangedCallback(attribute.name())) {
      enqueueAttributeChangedCallback(element, attribute.name(), nullAtom,
                                      attribute.value());
    }
  }
}

}  // namespace blink