1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Neither the name of Google Inc. nor the names of its
12 * contributors may be used to endorse or promote products derived from
13 * this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "ShadowRoot.h"
30
31#include "CSSStyleSheet.h"
32#include "ElementTraversal.h"
33#include "HTMLSlotElement.h"
34#include "RenderElement.h"
35#include "RuntimeEnabledFeatures.h"
36#include "SlotAssignment.h"
37#include "StyleResolver.h"
38#include "StyleScope.h"
39#include "StyleSheetList.h"
40#include "markup.h"
41#include <wtf/IsoMallocInlines.h>
42
43namespace WebCore {
44
45WTF_MAKE_ISO_ALLOCATED_IMPL(ShadowRoot);
46
47struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope {
48 unsigned countersAndFlags[1];
49 void* styleScope;
50 void* styleSheetList;
51 void* host;
52 void* slotAssignment;
53};
54
55COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small);
56
57ShadowRoot::ShadowRoot(Document& document, ShadowRootMode type)
58 : DocumentFragment(document, CreateShadowRoot)
59 , TreeScope(*this, document)
60 , m_type(type)
61 , m_styleScope(std::make_unique<Style::Scope>(*this))
62{
63}
64
65
66ShadowRoot::ShadowRoot(Document& document, std::unique_ptr<SlotAssignment>&& slotAssignment)
67 : DocumentFragment(document, CreateShadowRoot)
68 , TreeScope(*this, document)
69 , m_type(ShadowRootMode::UserAgent)
70 , m_styleScope(std::make_unique<Style::Scope>(*this))
71 , m_slotAssignment(WTFMove(slotAssignment))
72{
73}
74
75
76ShadowRoot::~ShadowRoot()
77{
78 if (isConnected())
79 document().didRemoveInDocumentShadowRoot(*this);
80
81 if (m_styleSheetList)
82 m_styleSheetList->detach();
83
84 // We cannot let ContainerNode destructor call willBeDeletedFrom()
85 // for this ShadowRoot instance because TreeScope destructor
86 // clears Node::m_treeScope thus ContainerNode is no longer able
87 // to access it Document reference after that.
88 willBeDeletedFrom(document());
89
90 ASSERT(!m_hasBegunDeletingDetachedChildren);
91 m_hasBegunDeletingDetachedChildren = true;
92
93 // We must remove all of our children first before the TreeScope destructor
94 // runs so we don't go through Node::setTreeScopeRecursively for each child with a
95 // destructed tree scope in each descendant.
96 removeDetachedChildren();
97}
98
99Node::InsertedIntoAncestorResult ShadowRoot::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
100{
101 DocumentFragment::insertedIntoAncestor(insertionType, parentOfInsertedTree);
102 if (insertionType.connectedToDocument)
103 document().didInsertInDocumentShadowRoot(*this);
104 return InsertedIntoAncestorResult::Done;
105}
106
107void ShadowRoot::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
108{
109 DocumentFragment::removedFromAncestor(removalType, oldParentOfRemovedTree);
110 if (removalType.disconnectedFromDocument)
111 document().didRemoveInDocumentShadowRoot(*this);
112}
113
114void ShadowRoot::childrenChanged(const ChildChange& childChange)
115{
116 DocumentFragment::childrenChanged(childChange);
117
118 if (!m_host || m_type == ShadowRootMode::UserAgent)
119 return; // Don't support first-child, nth-of-type, etc... in UA shadow roots as an optimization.
120
121 // FIXME: Avoid always invalidating style just for first-child, etc... as done in Element::childrenChanged.
122 switch (childChange.type) {
123 case ElementInserted:
124 case ElementRemoved:
125 m_host->invalidateStyleForSubtreeInternal();
126 break;
127 case TextInserted:
128 case TextRemoved:
129 case TextChanged:
130 case AllChildrenRemoved:
131 case NonContentsChildRemoved:
132 case NonContentsChildInserted:
133 case AllChildrenReplaced:
134 break;
135 }
136}
137
138void ShadowRoot::moveShadowRootToNewParentScope(TreeScope& newScope, Document& newDocument)
139{
140 setParentTreeScope(newScope);
141 moveShadowRootToNewDocument(newDocument);
142}
143
144void ShadowRoot::moveShadowRootToNewDocument(Document& newDocument)
145{
146 setDocumentScope(newDocument);
147 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!parentTreeScope() || &parentTreeScope()->documentScope() == &newDocument);
148
149 // Style scopes are document specific.
150 m_styleScope = std::make_unique<Style::Scope>(*this);
151 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(&m_styleScope->document() == &newDocument);
152}
153
154Style::Scope& ShadowRoot::styleScope()
155{
156 return *m_styleScope;
157}
158
159StyleSheetList& ShadowRoot::styleSheets()
160{
161 if (!m_styleSheetList)
162 m_styleSheetList = StyleSheetList::create(*this);
163 return *m_styleSheetList;
164}
165
166String ShadowRoot::innerHTML() const
167{
168 return serializeFragment(*this, SerializedNodes::SubtreesOfChildren);
169}
170
171ExceptionOr<void> ShadowRoot::setInnerHTML(const String& markup)
172{
173 auto fragment = createFragmentForInnerOuterHTML(*host(), markup, AllowScriptingContent);
174 if (fragment.hasException())
175 return fragment.releaseException();
176 return replaceChildrenWithFragment(*this, fragment.releaseReturnValue());
177}
178
179bool ShadowRoot::childTypeAllowed(NodeType type) const
180{
181 switch (type) {
182 case ELEMENT_NODE:
183 case PROCESSING_INSTRUCTION_NODE:
184 case COMMENT_NODE:
185 case TEXT_NODE:
186 case CDATA_SECTION_NODE:
187 return true;
188 default:
189 return false;
190 }
191}
192
193void ShadowRoot::setResetStyleInheritance(bool value)
194{
195 // If this was ever changed after initialization, child styles would need to be invalidated here.
196 m_resetStyleInheritance = value;
197}
198
199Ref<Node> ShadowRoot::cloneNodeInternal(Document&, CloningOperation)
200{
201 RELEASE_ASSERT_NOT_REACHED();
202 return *static_cast<Node*>(nullptr); // ShadowRoots should never be cloned.
203}
204
205void ShadowRoot::removeAllEventListeners()
206{
207 DocumentFragment::removeAllEventListeners();
208 for (Node* node = firstChild(); node; node = NodeTraversal::next(*node))
209 node->removeAllEventListeners();
210}
211
212
213HTMLSlotElement* ShadowRoot::findAssignedSlot(const Node& node)
214{
215 ASSERT(node.parentNode() == host());
216 if (!m_slotAssignment)
217 return nullptr;
218 return m_slotAssignment->findAssignedSlot(node, *this);
219}
220
221void ShadowRoot::renameSlotElement(HTMLSlotElement& slot, const AtomString& oldName, const AtomString& newName)
222{
223 ASSERT(m_slotAssignment);
224 return m_slotAssignment->renameSlotElement(slot, oldName, newName, *this);
225}
226
227void ShadowRoot::addSlotElementByName(const AtomString& name, HTMLSlotElement& slot)
228{
229 ASSERT(&slot.rootNode() == this);
230 if (!m_slotAssignment)
231 m_slotAssignment = std::make_unique<SlotAssignment>();
232
233 return m_slotAssignment->addSlotElementByName(name, slot, *this);
234}
235
236void ShadowRoot::removeSlotElementByName(const AtomString& name, HTMLSlotElement& slot, ContainerNode& oldParentOfRemovedTree)
237{
238 ASSERT(m_slotAssignment);
239 return m_slotAssignment->removeSlotElementByName(name, slot, &oldParentOfRemovedTree, *this);
240}
241
242void ShadowRoot::slotFallbackDidChange(HTMLSlotElement& slot)
243{
244 ASSERT(&slot.rootNode() == this);
245 return m_slotAssignment->slotFallbackDidChange(slot, *this);
246}
247
248const Vector<Node*>* ShadowRoot::assignedNodesForSlot(const HTMLSlotElement& slot)
249{
250 if (!m_slotAssignment)
251 return nullptr;
252 return m_slotAssignment->assignedNodesForSlot(slot, *this);
253}
254
255Vector<ShadowRoot*> assignedShadowRootsIfSlotted(const Node& node)
256{
257 Vector<ShadowRoot*> result;
258 for (auto* slot = node.assignedSlot(); slot; slot = slot->assignedSlot()) {
259 ASSERT(slot->containingShadowRoot());
260 result.append(slot->containingShadowRoot());
261 }
262 return result;
263}
264
265}
266