a2f96e61cb56f9fea40599b2aaee63ea35491b88
[WebKit-https.git] / Source / WebCore / dom / ShadowRoot.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Neither the name of Google Inc. nor the names of its
11  * contributors may be used to endorse or promote products derived from
12  * this software without specific prior written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "ShadowRoot.h"
29
30 #include "ContentDistributor.h"
31 #include "DOMSelection.h"
32 #include "DOMWindow.h"
33 #include "Document.h"
34 #include "DocumentFragment.h"
35 #include "Element.h"
36 #include "ElementShadow.h"
37 #include "HTMLContentElement.h"
38 #include "HTMLInputElement.h"
39 #include "HTMLNames.h"
40 #include "HTMLTextAreaElement.h"
41 #include "HistogramSupport.h"
42 #include "InsertionPoint.h"
43 #include "NodeRareData.h"
44 #include "RuntimeEnabledFeatures.h"
45 #include "SVGNames.h"
46 #include "StyleResolver.h"
47 #include "Text.h"
48 #include "markup.h"
49
50 // FIXME: This shouldn't happen. https://bugs.webkit.org/show_bug.cgi?id=88834
51 #define GuardOrphanShadowRoot(rejectStatement) \
52     if (!this->host()) {                       \
53         rejectStatement;                       \
54         return;                                \
55     }
56
57 namespace WebCore {
58
59 struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope, public DoublyLinkedListNode<ShadowRoot> {
60     void* pointers[3];
61     unsigned countersAndFlags[1];
62 };
63
64 COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small);
65
66 ShadowRoot::ShadowRoot(Document* document, ShadowRootType type)
67     : DocumentFragment(document, CreateShadowRoot)
68     , TreeScope(this, document)
69     , m_prev(0)
70     , m_next(0)
71     , m_numberOfStyles(0)
72     , m_applyAuthorStyles(false)
73     , m_resetStyleInheritance(false)
74     , m_type(type)
75     , m_registeredWithParentShadowRoot(false)
76 {
77     ASSERT(document);
78     setTreeScope(this);
79 }
80
81 ShadowRoot::~ShadowRoot()
82 {
83     ASSERT(!m_prev);
84     ASSERT(!m_next);
85
86     // We must remove all of our children first before the TreeScope destructor
87     // runs so we don't go through TreeScopeAdopter for each child with a
88     // destructed tree scope in each descendant.
89     removeDetachedChildren();
90
91     // We must call clearRareData() here since a ShadowRoot class inherits TreeScope
92     // as well as Node. See a comment on TreeScope.h for the reason.
93     if (hasRareData())
94         clearRareData();
95 }
96
97 static bool allowsAuthorShadowRoot(Element* element)
98 {
99 #if ENABLE(SHADOW_DOM)
100     if (RuntimeEnabledFeatures::authorShadowDOMForAnyElementEnabled())
101         return true;
102 #endif
103     return element->areAuthorShadowsAllowed();
104 }
105
106 enum ShadowRootUsageOriginType {
107     ShadowRootUsageOriginWeb = 0,
108     ShadowRootUsageOriginNotWeb,
109     ShadowRootUsageOriginTypes
110 };
111
112 static inline ShadowRootUsageOriginType determineUsageType(Element* host)
113 {
114     // Enables only on CHROMIUM since this cost won't worth paying for platforms which don't collect this metrics.
115 #if PLATFORM(CHROMIUM)
116     if (!host)
117         return ShadowRootUsageOriginWeb;
118     return host->document()->url().string().startsWith("http") ? ShadowRootUsageOriginWeb : ShadowRootUsageOriginNotWeb;
119 #else
120     UNUSED_PARAM(host);
121     return ShadowRootUsageOriginWeb;
122 #endif
123 }
124
125 PassRefPtr<ShadowRoot> ShadowRoot::create(Element* element, ExceptionCode& ec)
126 {
127     HistogramSupport::histogramEnumeration("WebCore.ShadowRoot.constructor", determineUsageType(element), ShadowRootUsageOriginTypes);
128     return create(element, AuthorShadowRoot, ec);
129 }
130
131 PassRefPtr<ShadowRoot> ShadowRoot::create(Element* element, ShadowRootType type, ExceptionCode& ec)
132 {
133     if (!element) {
134         ec = HIERARCHY_REQUEST_ERR;
135         return 0;
136     }
137
138     // Since some elements recreates shadow root dynamically, multiple shadow subtrees won't work well in that element.
139     // Until they are fixed, we disable adding author shadow root for them.
140     if (type == AuthorShadowRoot && !allowsAuthorShadowRoot(element)) {
141         ec = HIERARCHY_REQUEST_ERR;
142         return 0;
143     }
144
145     RefPtr<ShadowRoot> shadowRoot = adoptRef(new ShadowRoot(element->document(), type));
146
147     ec = 0;
148     element->ensureShadow()->addShadowRoot(element, shadowRoot, type, ec);
149     if (ec)
150         return 0;
151     ASSERT(element == shadowRoot->host());
152     ASSERT(element->shadow());
153     return shadowRoot.release();
154 }
155
156 PassRefPtr<Node> ShadowRoot::cloneNode(bool)
157 {
158     // ShadowRoot should not be arbitrarily cloned.
159     return 0;
160 }
161
162 PassRefPtr<Node> ShadowRoot::cloneNode(bool, ExceptionCode& ec)
163 {
164     ec = DATA_CLONE_ERR;
165     return 0;
166 }
167
168 String ShadowRoot::innerHTML() const
169 {
170     return createMarkup(this, ChildrenOnly);
171 }
172
173 void ShadowRoot::setInnerHTML(const String& markup, ExceptionCode& ec)
174 {
175     GuardOrphanShadowRoot(ec = INVALID_ACCESS_ERR);
176
177     if (RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, host(), AllowScriptingContent, ec))
178         replaceChildrenWithFragment(this, fragment.release(), ec);
179 }
180
181 bool ShadowRoot::childTypeAllowed(NodeType type) const
182 {
183     switch (type) {
184     case ELEMENT_NODE:
185     case PROCESSING_INSTRUCTION_NODE:
186     case COMMENT_NODE:
187     case TEXT_NODE:
188     case CDATA_SECTION_NODE:
189     case ENTITY_REFERENCE_NODE:
190         return true;
191     default:
192         return false;
193     }
194 }
195
196 void ShadowRoot::recalcStyle(StyleChange change)
197 {
198     // ShadowRoot doesn't support custom callbacks.
199     ASSERT(!hasCustomCallbacks());
200
201     StyleResolver* styleResolver = document()->styleResolver();
202     styleResolver->pushParentShadowRoot(this);
203
204     for (Node* child = firstChild(); child; child = child->nextSibling()) {
205         if (child->isElementNode())
206             static_cast<Element*>(child)->recalcStyle(change);
207         else if (child->isTextNode())
208             toText(child)->recalcTextStyle(change);
209     }
210
211     styleResolver->popParentShadowRoot(this);
212     clearNeedsStyleRecalc();
213     clearChildNeedsStyleRecalc();
214 }
215
216 ElementShadow* ShadowRoot::owner() const
217 {
218     if (host())
219         return host()->shadow();
220     return 0;
221 }
222
223 bool ShadowRoot::applyAuthorStyles() const
224 {
225     return m_applyAuthorStyles;
226 }
227
228 void ShadowRoot::setApplyAuthorStyles(bool value)
229 {
230     GuardOrphanShadowRoot({ });
231
232     if (m_applyAuthorStyles != value) {
233         m_applyAuthorStyles = value;
234         host()->setNeedsStyleRecalc();
235     }
236 }
237
238 bool ShadowRoot::resetStyleInheritance() const
239 {
240     return m_resetStyleInheritance;
241 }
242
243 void ShadowRoot::setResetStyleInheritance(bool value)
244 {
245     GuardOrphanShadowRoot({ });
246
247     if (value != m_resetStyleInheritance) {
248         m_resetStyleInheritance = value;
249         if (attached() && owner())
250             owner()->recalcStyle(Force);
251     }
252 }
253
254 void ShadowRoot::attach()
255 {
256     StyleResolver* styleResolver = document()->styleResolver();
257     styleResolver->pushParentShadowRoot(this);
258     DocumentFragment::attach();
259     styleResolver->popParentShadowRoot(this);
260 }
261
262 Node::InsertionNotificationRequest ShadowRoot::insertedInto(ContainerNode* insertionPoint)
263 {
264     DocumentFragment::insertedInto(insertionPoint);
265
266     if (!insertionPoint->inDocument() || !isOldest())
267         return InsertionDone;
268
269     // FIXME: When parsing <video controls>, insertedInto() is called many times without invoking removedFrom.
270     // For now, we check m_registeredWithParentShadowroot. We would like to ASSERT(!m_registeredShadowRoot) here.
271     // https://bugs.webkit.org/show_bug.cig?id=101316
272     if (m_registeredWithParentShadowRoot)
273         return InsertionDone;
274
275     if (ShadowRoot* root = host()->containingShadowRoot()) {
276         root->ensureScopeDistribution()->registerElementShadow();
277         m_registeredWithParentShadowRoot = true;
278     }
279
280     return InsertionDone;
281 }
282
283 void ShadowRoot::removedFrom(ContainerNode* insertionPoint)
284 {
285     if (insertionPoint->inDocument() && m_registeredWithParentShadowRoot) {
286         ShadowRoot* root = host()->containingShadowRoot();
287         if (!root)
288             root = insertionPoint->containingShadowRoot();
289
290         if (root && root->scopeDistribution())
291             root->scopeDistribution()->unregisterElementShadow();
292         m_registeredWithParentShadowRoot = false;
293     }
294
295     DocumentFragment::removedFrom(insertionPoint);
296 }
297
298 void ShadowRoot::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
299 {
300     GuardOrphanShadowRoot({ });
301
302     ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
303     owner()->invalidateDistribution();
304 }
305
306 void ShadowRoot::registerScopedHTMLStyleChild()
307 {
308     ++m_numberOfStyles;
309     setHasScopedHTMLStyleChild(true);
310 }
311
312 void ShadowRoot::unregisterScopedHTMLStyleChild()
313 {
314     ASSERT(hasScopedHTMLStyleChild() && m_numberOfStyles > 0);
315     --m_numberOfStyles;
316     setHasScopedHTMLStyleChild(m_numberOfStyles > 0);
317 }
318
319 ScopeContentDistribution* ShadowRoot::ensureScopeDistribution()
320 {
321     if (m_scopeDistribution)
322         return m_scopeDistribution.get();
323
324     m_scopeDistribution = adoptPtr(new ScopeContentDistribution);
325     return m_scopeDistribution.get();
326 }   
327
328 void ShadowRoot::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
329 {
330     MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM);
331     DocumentFragment::reportMemoryUsage(memoryObjectInfo);
332     TreeScope::reportMemoryUsage(memoryObjectInfo);
333     info.addMember(m_prev);
334     info.addMember(m_next);
335     info.addMember(m_scopeDistribution);
336 }
337
338 }