Refactor ShadowRoot exception handling
[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 enum ShadowRootUsageOriginType {
67     ShadowRootUsageOriginWeb = 0,
68     ShadowRootUsageOriginNotWeb,
69     ShadowRootUsageOriginTypes
70 };
71
72 static inline ShadowRootUsageOriginType determineUsageType(Document* document)
73 {
74     // Enables only on CHROMIUM since this cost won't worth paying for platforms which don't collect this metrics.
75 #if PLATFORM(CHROMIUM)
76     return document->url().string().startsWith("http") ? ShadowRootUsageOriginWeb : ShadowRootUsageOriginNotWeb;
77 #else
78     UNUSED_PARAM(document);
79     return ShadowRootUsageOriginWeb;
80 #endif
81 }
82
83 ShadowRoot::ShadowRoot(Document* document, ShadowRootType type)
84     : DocumentFragment(document, CreateShadowRoot)
85     , TreeScope(this, document)
86     , m_prev(0)
87     , m_next(0)
88     , m_numberOfStyles(0)
89     , m_applyAuthorStyles(false)
90     , m_resetStyleInheritance(false)
91     , m_type(type)
92     , m_registeredWithParentShadowRoot(false)
93 {
94     ASSERT(document);
95     setTreeScope(this);
96
97     if (type == ShadowRoot::AuthorShadowRoot)
98         HistogramSupport::histogramEnumeration("WebCore.ShadowRoot.constructor", determineUsageType(document), ShadowRootUsageOriginTypes);
99 }
100
101 ShadowRoot::~ShadowRoot()
102 {
103     ASSERT(!m_prev);
104     ASSERT(!m_next);
105
106     // We must remove all of our children first before the TreeScope destructor
107     // runs so we don't go through TreeScopeAdopter for each child with a
108     // destructed tree scope in each descendant.
109     removeDetachedChildren();
110
111     // We must call clearRareData() here since a ShadowRoot class inherits TreeScope
112     // as well as Node. See a comment on TreeScope.h for the reason.
113     if (hasRareData())
114         clearRareData();
115 }
116
117 PassRefPtr<ShadowRoot> ShadowRoot::create(Element* element, ShadowRootType type)
118 {
119     ASSERT(element);
120
121     RefPtr<ShadowRoot> shadowRoot = adoptRef(new ShadowRoot(element->document(), type));
122
123     element->ensureShadow()->addShadowRoot(element, shadowRoot, type);
124     ASSERT(element == shadowRoot->host());
125     ASSERT(element->shadow());
126
127     return shadowRoot.release();
128 }
129
130 PassRefPtr<Node> ShadowRoot::cloneNode(bool)
131 {
132     // ShadowRoot should not be arbitrarily cloned.
133     return 0;
134 }
135
136 PassRefPtr<Node> ShadowRoot::cloneNode(bool, ExceptionCode& ec)
137 {
138     ec = DATA_CLONE_ERR;
139     return 0;
140 }
141
142 String ShadowRoot::innerHTML() const
143 {
144     return createMarkup(this, ChildrenOnly);
145 }
146
147 void ShadowRoot::setInnerHTML(const String& markup, ExceptionCode& ec)
148 {
149     GuardOrphanShadowRoot(ec = INVALID_ACCESS_ERR);
150
151     if (RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, host(), AllowScriptingContent, ec))
152         replaceChildrenWithFragment(this, fragment.release(), ec);
153 }
154
155 bool ShadowRoot::childTypeAllowed(NodeType type) const
156 {
157     switch (type) {
158     case ELEMENT_NODE:
159     case PROCESSING_INSTRUCTION_NODE:
160     case COMMENT_NODE:
161     case TEXT_NODE:
162     case CDATA_SECTION_NODE:
163     case ENTITY_REFERENCE_NODE:
164         return true;
165     default:
166         return false;
167     }
168 }
169
170 void ShadowRoot::recalcStyle(StyleChange change)
171 {
172     // ShadowRoot doesn't support custom callbacks.
173     ASSERT(!hasCustomCallbacks());
174
175     StyleResolver* styleResolver = document()->styleResolver();
176     styleResolver->pushParentShadowRoot(this);
177
178     for (Node* child = firstChild(); child; child = child->nextSibling()) {
179         if (child->isElementNode())
180             static_cast<Element*>(child)->recalcStyle(change);
181         else if (child->isTextNode())
182             toText(child)->recalcTextStyle(change);
183     }
184
185     styleResolver->popParentShadowRoot(this);
186     clearNeedsStyleRecalc();
187     clearChildNeedsStyleRecalc();
188 }
189
190 ElementShadow* ShadowRoot::owner() const
191 {
192     if (host())
193         return host()->shadow();
194     return 0;
195 }
196
197 bool ShadowRoot::applyAuthorStyles() const
198 {
199     return m_applyAuthorStyles;
200 }
201
202 void ShadowRoot::setApplyAuthorStyles(bool value)
203 {
204     GuardOrphanShadowRoot({ });
205
206     if (m_applyAuthorStyles != value) {
207         m_applyAuthorStyles = value;
208         host()->setNeedsStyleRecalc();
209     }
210 }
211
212 bool ShadowRoot::resetStyleInheritance() const
213 {
214     return m_resetStyleInheritance;
215 }
216
217 void ShadowRoot::setResetStyleInheritance(bool value)
218 {
219     GuardOrphanShadowRoot({ });
220
221     if (value != m_resetStyleInheritance) {
222         m_resetStyleInheritance = value;
223         if (attached() && owner())
224             owner()->recalcStyle(Force);
225     }
226 }
227
228 void ShadowRoot::attach()
229 {
230     StyleResolver* styleResolver = document()->styleResolver();
231     styleResolver->pushParentShadowRoot(this);
232     DocumentFragment::attach();
233     styleResolver->popParentShadowRoot(this);
234 }
235
236 Node::InsertionNotificationRequest ShadowRoot::insertedInto(ContainerNode* insertionPoint)
237 {
238     DocumentFragment::insertedInto(insertionPoint);
239
240     if (!insertionPoint->inDocument() || !isOldest())
241         return InsertionDone;
242
243     // FIXME: When parsing <video controls>, insertedInto() is called many times without invoking removedFrom.
244     // For now, we check m_registeredWithParentShadowroot. We would like to ASSERT(!m_registeredShadowRoot) here.
245     // https://bugs.webkit.org/show_bug.cig?id=101316
246     if (m_registeredWithParentShadowRoot)
247         return InsertionDone;
248
249     if (ShadowRoot* root = host()->containingShadowRoot()) {
250         root->ensureScopeDistribution()->registerElementShadow();
251         m_registeredWithParentShadowRoot = true;
252     }
253
254     return InsertionDone;
255 }
256
257 void ShadowRoot::removedFrom(ContainerNode* insertionPoint)
258 {
259     if (insertionPoint->inDocument() && m_registeredWithParentShadowRoot) {
260         ShadowRoot* root = host()->containingShadowRoot();
261         if (!root)
262             root = insertionPoint->containingShadowRoot();
263
264         if (root && root->scopeDistribution())
265             root->scopeDistribution()->unregisterElementShadow();
266         m_registeredWithParentShadowRoot = false;
267     }
268
269     DocumentFragment::removedFrom(insertionPoint);
270 }
271
272 void ShadowRoot::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
273 {
274     GuardOrphanShadowRoot({ });
275
276     ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
277     owner()->invalidateDistribution();
278 }
279
280 void ShadowRoot::registerScopedHTMLStyleChild()
281 {
282     ++m_numberOfStyles;
283     setHasScopedHTMLStyleChild(true);
284 }
285
286 void ShadowRoot::unregisterScopedHTMLStyleChild()
287 {
288     ASSERT(hasScopedHTMLStyleChild() && m_numberOfStyles > 0);
289     --m_numberOfStyles;
290     setHasScopedHTMLStyleChild(m_numberOfStyles > 0);
291 }
292
293 ScopeContentDistribution* ShadowRoot::ensureScopeDistribution()
294 {
295     if (m_scopeDistribution)
296         return m_scopeDistribution.get();
297
298     m_scopeDistribution = adoptPtr(new ScopeContentDistribution);
299     return m_scopeDistribution.get();
300 }   
301
302 void ShadowRoot::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
303 {
304     MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::DOM);
305     DocumentFragment::reportMemoryUsage(memoryObjectInfo);
306     TreeScope::reportMemoryUsage(memoryObjectInfo);
307     info.addMember(m_prev);
308     info.addMember(m_next);
309     info.addMember(m_scopeDistribution);
310 }
311
312 }