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 | |
43 | namespace WebCore { |
44 | |
45 | WTF_MAKE_ISO_ALLOCATED_IMPL(ShadowRoot); |
46 | |
47 | struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope { |
48 | unsigned countersAndFlags[1]; |
49 | void* styleScope; |
50 | void* styleSheetList; |
51 | void* host; |
52 | void* slotAssignment; |
53 | }; |
54 | |
55 | COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small); |
56 | |
57 | ShadowRoot::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 | |
66 | ShadowRoot::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 | |
76 | ShadowRoot::~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 | |
99 | Node::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 | |
107 | void ShadowRoot::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree) |
108 | { |
109 | DocumentFragment::removedFromAncestor(removalType, oldParentOfRemovedTree); |
110 | if (removalType.disconnectedFromDocument) |
111 | document().didRemoveInDocumentShadowRoot(*this); |
112 | } |
113 | |
114 | void 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 | |
138 | void ShadowRoot::moveShadowRootToNewParentScope(TreeScope& newScope, Document& newDocument) |
139 | { |
140 | setParentTreeScope(newScope); |
141 | moveShadowRootToNewDocument(newDocument); |
142 | } |
143 | |
144 | void 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 | |
154 | Style::Scope& ShadowRoot::styleScope() |
155 | { |
156 | return *m_styleScope; |
157 | } |
158 | |
159 | StyleSheetList& ShadowRoot::styleSheets() |
160 | { |
161 | if (!m_styleSheetList) |
162 | m_styleSheetList = StyleSheetList::create(*this); |
163 | return *m_styleSheetList; |
164 | } |
165 | |
166 | String ShadowRoot::innerHTML() const |
167 | { |
168 | return serializeFragment(*this, SerializedNodes::SubtreesOfChildren); |
169 | } |
170 | |
171 | ExceptionOr<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 | |
179 | bool 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 | |
193 | void 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 | |
199 | Ref<Node> ShadowRoot::cloneNodeInternal(Document&, CloningOperation) |
200 | { |
201 | RELEASE_ASSERT_NOT_REACHED(); |
202 | return *static_cast<Node*>(nullptr); // ShadowRoots should never be cloned. |
203 | } |
204 | |
205 | void ShadowRoot::removeAllEventListeners() |
206 | { |
207 | DocumentFragment::removeAllEventListeners(); |
208 | for (Node* node = firstChild(); node; node = NodeTraversal::next(*node)) |
209 | node->removeAllEventListeners(); |
210 | } |
211 | |
212 | |
213 | HTMLSlotElement* 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 | |
221 | void ShadowRoot::renameSlotElement(HTMLSlotElement& slot, const AtomicString& oldName, const AtomicString& newName) |
222 | { |
223 | ASSERT(m_slotAssignment); |
224 | return m_slotAssignment->renameSlotElement(slot, oldName, newName, *this); |
225 | } |
226 | |
227 | void ShadowRoot::addSlotElementByName(const AtomicString& 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 | |
236 | void ShadowRoot::removeSlotElementByName(const AtomicString& name, HTMLSlotElement& slot, ContainerNode& oldParentOfRemovedTree) |
237 | { |
238 | ASSERT(m_slotAssignment); |
239 | return m_slotAssignment->removeSlotElementByName(name, slot, &oldParentOfRemovedTree, *this); |
240 | } |
241 | |
242 | void ShadowRoot::slotFallbackDidChange(HTMLSlotElement& slot) |
243 | { |
244 | ASSERT(&slot.rootNode() == this); |
245 | return m_slotAssignment->slotFallbackDidChange(slot, *this); |
246 | } |
247 | |
248 | const Vector<Node*>* ShadowRoot::assignedNodesForSlot(const HTMLSlotElement& slot) |
249 | { |
250 | if (!m_slotAssignment) |
251 | return nullptr; |
252 | return m_slotAssignment->assignedNodesForSlot(slot, *this); |
253 | } |
254 | |
255 | Vector<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 | |