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 "core/dom/shadow/ShadowRoot.h"
29
30 #include "bindings/v8/ExceptionState.h"
31 #include "core/css/StyleSheetList.h"
32 #include "core/css/resolver/StyleResolver.h"
33 #include "core/dom/ElementTraversal.h"
34 #include "core/dom/StyleEngine.h"
35 #include "core/dom/Text.h"
36 #include "core/dom/shadow/ElementShadow.h"
37 #include "core/dom/shadow/InsertionPoint.h"
38 #include "core/dom/shadow/ShadowRootRareData.h"
39 #include "core/editing/markup.h"
40 #include "public/platform/Platform.h"
41
42 namespace WebCore {
43
44 struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope, public DoublyLinkedListNode<ShadowRoot> {
45 void* pointers[3];
46 unsigned countersAndFlags[1];
47 };
48
49 COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small);
50
51 enum ShadowRootUsageOriginType {
52 ShadowRootUsageOriginWeb = 0,
53 ShadowRootUsageOriginNotWeb,
54 ShadowRootUsageOriginMax
55 };
56
ShadowRoot(Document * document,ShadowRootType type)57 ShadowRoot::ShadowRoot(Document* document, ShadowRootType type)
58 : DocumentFragment(0, CreateShadowRoot)
59 , TreeScope(this, document)
60 , m_prev(0)
61 , m_next(0)
62 , m_numberOfStyles(0)
63 , m_applyAuthorStyles(false)
64 , m_resetStyleInheritance(false)
65 , m_type(type)
66 , m_registeredWithParentShadowRoot(false)
67 , m_descendantInsertionPointsIsValid(false)
68 {
69 ASSERT(document);
70 ScriptWrappable::init(this);
71
72 if (type == ShadowRoot::AuthorShadowRoot) {
73 ShadowRootUsageOriginType usageType = document->url().protocolIsInHTTPFamily() ? ShadowRootUsageOriginWeb : ShadowRootUsageOriginNotWeb;
74 blink::Platform::current()->histogramEnumeration("WebCore.ShadowRoot.constructor", usageType, ShadowRootUsageOriginMax);
75 }
76 }
77
~ShadowRoot()78 ShadowRoot::~ShadowRoot()
79 {
80 ASSERT(!m_prev);
81 ASSERT(!m_next);
82
83 if (m_shadowRootRareData && m_shadowRootRareData->styleSheets())
84 m_shadowRootRareData->styleSheets()->detachFromDocument();
85
86 documentInternal()->styleEngine()->didRemoveShadowRoot(this);
87
88 // We cannot let ContainerNode destructor call willBeDeletedFromDocument()
89 // for this ShadowRoot instance because TreeScope destructor
90 // clears Node::m_treeScope thus ContainerNode is no longer able
91 // to access it Document reference after that.
92 willBeDeletedFromDocument();
93
94 // We must remove all of our children first before the TreeScope destructor
95 // runs so we don't go through TreeScopeAdopter for each child with a
96 // destructed tree scope in each descendant.
97 removeDetachedChildren();
98
99 // We must call clearRareData() here since a ShadowRoot class inherits TreeScope
100 // as well as Node. See a comment on TreeScope.h for the reason.
101 if (hasRareData())
102 clearRareData();
103 }
104
dispose()105 void ShadowRoot::dispose()
106 {
107 removeDetachedChildren();
108 }
109
bindingsOlderShadowRoot() const110 ShadowRoot* ShadowRoot::bindingsOlderShadowRoot() const
111 {
112 ShadowRoot* older = olderShadowRoot();
113 while (older && !older->shouldExposeToBindings())
114 older = older->olderShadowRoot();
115 ASSERT(!older || older->shouldExposeToBindings());
116 return older;
117 }
118
isOldestAuthorShadowRoot() const119 bool ShadowRoot::isOldestAuthorShadowRoot() const
120 {
121 if (type() != AuthorShadowRoot)
122 return false;
123 if (ShadowRoot* older = olderShadowRoot())
124 return older->type() == UserAgentShadowRoot;
125 return true;
126 }
127
cloneNode(bool,ExceptionState & exceptionState)128 PassRefPtr<Node> ShadowRoot::cloneNode(bool, ExceptionState& exceptionState)
129 {
130 exceptionState.throwDOMException(DataCloneError, "ShadowRoot nodes are not clonable.");
131 return 0;
132 }
133
innerHTML() const134 String ShadowRoot::innerHTML() const
135 {
136 return createMarkup(this, ChildrenOnly);
137 }
138
setInnerHTML(const String & markup,ExceptionState & exceptionState)139 void ShadowRoot::setInnerHTML(const String& markup, ExceptionState& exceptionState)
140 {
141 if (isOrphan()) {
142 exceptionState.throwDOMException(InvalidAccessError, "The ShadowRoot does not have a host.");
143 return;
144 }
145
146 if (RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, host(), AllowScriptingContent, "innerHTML", exceptionState))
147 replaceChildrenWithFragment(this, fragment.release(), exceptionState);
148 }
149
childTypeAllowed(NodeType type) const150 bool ShadowRoot::childTypeAllowed(NodeType type) const
151 {
152 switch (type) {
153 case ELEMENT_NODE:
154 case PROCESSING_INSTRUCTION_NODE:
155 case COMMENT_NODE:
156 case TEXT_NODE:
157 case CDATA_SECTION_NODE:
158 return true;
159 default:
160 return false;
161 }
162 }
163
recalcStyle(StyleRecalcChange change)164 void ShadowRoot::recalcStyle(StyleRecalcChange change)
165 {
166 // ShadowRoot doesn't support custom callbacks.
167 ASSERT(!hasCustomStyleCallbacks());
168
169 StyleResolver& styleResolver = document().ensureStyleResolver();
170 styleResolver.pushParentShadowRoot(*this);
171
172 if (styleChangeType() >= SubtreeStyleChange)
173 change = Force;
174
175 // There's no style to update so just calling recalcStyle means we're updated.
176 clearNeedsStyleRecalc();
177
178 // FIXME: This doesn't handle :hover + div properly like Element::recalcStyle does.
179 Text* lastTextNode = 0;
180 for (Node* child = lastChild(); child; child = child->previousSibling()) {
181 if (child->isTextNode()) {
182 toText(child)->recalcTextStyle(change, lastTextNode);
183 lastTextNode = toText(child);
184 } else if (child->isElementNode()) {
185 if (shouldRecalcStyle(change, child))
186 toElement(child)->recalcStyle(change, lastTextNode);
187 if (child->renderer())
188 lastTextNode = 0;
189 }
190 }
191
192 styleResolver.popParentShadowRoot(*this);
193
194 clearChildNeedsStyleRecalc();
195 }
196
isActive() const197 bool ShadowRoot::isActive() const
198 {
199 for (ShadowRoot* shadowRoot = youngerShadowRoot(); shadowRoot; shadowRoot = shadowRoot->youngerShadowRoot())
200 if (!shadowRoot->containsShadowElements())
201 return false;
202 return true;
203 }
204
setApplyAuthorStyles(bool value)205 void ShadowRoot::setApplyAuthorStyles(bool value)
206 {
207 if (isOrphan())
208 return;
209
210 if (applyAuthorStyles() == value)
211 return;
212
213 m_applyAuthorStyles = value;
214 if (!isActive())
215 return;
216
217 ASSERT(host());
218 ASSERT(host()->shadow());
219 if (host()->shadow()->didAffectApplyAuthorStyles())
220 host()->setNeedsStyleRecalc();
221
222 // Since styles in shadow trees can select shadow hosts, set shadow host's needs-recalc flag true.
223 // FIXME: host->setNeedsStyleRecalc() should take care of all elements in its shadow tree.
224 // However, when host's recalcStyle is skipped (i.e. host's parent has no renderer),
225 // no recalc style is invoked for any elements in its shadow tree.
226 // This problem occurs when using getComputedStyle() API.
227 // So currently host and shadow root's needsStyleRecalc flags are set to be true.
228 setNeedsStyleRecalc();
229 }
230
setResetStyleInheritance(bool value)231 void ShadowRoot::setResetStyleInheritance(bool value)
232 {
233 if (isOrphan())
234 return;
235
236 if (value == resetStyleInheritance())
237 return;
238
239 m_resetStyleInheritance = value;
240 if (!isActive())
241 return;
242
243 setNeedsStyleRecalc();
244 }
245
attach(const AttachContext & context)246 void ShadowRoot::attach(const AttachContext& context)
247 {
248 StyleResolver& styleResolver = document().ensureStyleResolver();
249 styleResolver.pushParentShadowRoot(*this);
250 DocumentFragment::attach(context);
251 styleResolver.popParentShadowRoot(*this);
252 }
253
insertedInto(ContainerNode * insertionPoint)254 Node::InsertionNotificationRequest ShadowRoot::insertedInto(ContainerNode* insertionPoint)
255 {
256 DocumentFragment::insertedInto(insertionPoint);
257
258 if (!insertionPoint->inDocument() || !isOldest())
259 return InsertionDone;
260
261 // FIXME: When parsing <video controls>, insertedInto() is called many times without invoking removedFrom.
262 // For now, we check m_registeredWithParentShadowroot. We would like to ASSERT(!m_registeredShadowRoot) here.
263 // https://bugs.webkit.org/show_bug.cig?id=101316
264 if (m_registeredWithParentShadowRoot)
265 return InsertionDone;
266
267 if (ShadowRoot* root = host()->containingShadowRoot()) {
268 root->addChildShadowRoot();
269 m_registeredWithParentShadowRoot = true;
270 }
271
272 return InsertionDone;
273 }
274
removedFrom(ContainerNode * insertionPoint)275 void ShadowRoot::removedFrom(ContainerNode* insertionPoint)
276 {
277 if (insertionPoint->inDocument() && m_registeredWithParentShadowRoot) {
278 ShadowRoot* root = host()->containingShadowRoot();
279 if (!root)
280 root = insertionPoint->containingShadowRoot();
281 if (root)
282 root->removeChildShadowRoot();
283 m_registeredWithParentShadowRoot = false;
284 }
285
286 DocumentFragment::removedFrom(insertionPoint);
287 }
288
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)289 void ShadowRoot::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
290 {
291 ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
292 if (InsertionPoint* point = shadowInsertionPointOfYoungerShadowRoot()) {
293 if (ShadowRoot* root = point->containingShadowRoot())
294 root->owner()->setNeedsDistributionRecalc();
295 }
296 }
297
registerScopedHTMLStyleChild()298 void ShadowRoot::registerScopedHTMLStyleChild()
299 {
300 ++m_numberOfStyles;
301 setHasScopedHTMLStyleChild(true);
302 }
303
unregisterScopedHTMLStyleChild()304 void ShadowRoot::unregisterScopedHTMLStyleChild()
305 {
306 ASSERT(hasScopedHTMLStyleChild() && m_numberOfStyles > 0);
307 --m_numberOfStyles;
308 setHasScopedHTMLStyleChild(m_numberOfStyles > 0);
309 }
310
ensureShadowRootRareData()311 ShadowRootRareData* ShadowRoot::ensureShadowRootRareData()
312 {
313 if (m_shadowRootRareData)
314 return m_shadowRootRareData.get();
315
316 m_shadowRootRareData = adoptPtr(new ShadowRootRareData);
317 return m_shadowRootRareData.get();
318 }
319
containsShadowElements() const320 bool ShadowRoot::containsShadowElements() const
321 {
322 return m_shadowRootRareData ? m_shadowRootRareData->containsShadowElements() : 0;
323 }
324
containsContentElements() const325 bool ShadowRoot::containsContentElements() const
326 {
327 return m_shadowRootRareData ? m_shadowRootRareData->containsContentElements() : 0;
328 }
329
containsShadowRoots() const330 bool ShadowRoot::containsShadowRoots() const
331 {
332 return m_shadowRootRareData ? m_shadowRootRareData->containsShadowRoots() : 0;
333 }
334
descendantShadowElementCount() const335 unsigned ShadowRoot::descendantShadowElementCount() const
336 {
337 return m_shadowRootRareData ? m_shadowRootRareData->descendantShadowElementCount() : 0;
338 }
339
shadowInsertionPointOfYoungerShadowRoot() const340 HTMLShadowElement* ShadowRoot::shadowInsertionPointOfYoungerShadowRoot() const
341 {
342 return m_shadowRootRareData ? m_shadowRootRareData->shadowInsertionPointOfYoungerShadowRoot() : 0;
343 }
344
setShadowInsertionPointOfYoungerShadowRoot(PassRefPtr<HTMLShadowElement> shadowInsertionPoint)345 void ShadowRoot::setShadowInsertionPointOfYoungerShadowRoot(PassRefPtr<HTMLShadowElement> shadowInsertionPoint)
346 {
347 if (!m_shadowRootRareData && !shadowInsertionPoint)
348 return;
349 ensureShadowRootRareData()->setShadowInsertionPointOfYoungerShadowRoot(shadowInsertionPoint);
350 }
351
didAddInsertionPoint(InsertionPoint * insertionPoint)352 void ShadowRoot::didAddInsertionPoint(InsertionPoint* insertionPoint)
353 {
354 ensureShadowRootRareData()->didAddInsertionPoint(insertionPoint);
355 invalidateDescendantInsertionPoints();
356 }
357
didRemoveInsertionPoint(InsertionPoint * insertionPoint)358 void ShadowRoot::didRemoveInsertionPoint(InsertionPoint* insertionPoint)
359 {
360 m_shadowRootRareData->didRemoveInsertionPoint(insertionPoint);
361 invalidateDescendantInsertionPoints();
362 }
363
addChildShadowRoot()364 void ShadowRoot::addChildShadowRoot()
365 {
366 ensureShadowRootRareData()->didAddChildShadowRoot();
367 }
368
removeChildShadowRoot()369 void ShadowRoot::removeChildShadowRoot()
370 {
371 // FIXME: Why isn't this an ASSERT?
372 if (!m_shadowRootRareData)
373 return;
374 m_shadowRootRareData->didRemoveChildShadowRoot();
375 }
376
childShadowRootCount() const377 unsigned ShadowRoot::childShadowRootCount() const
378 {
379 return m_shadowRootRareData ? m_shadowRootRareData->childShadowRootCount() : 0;
380 }
381
invalidateDescendantInsertionPoints()382 void ShadowRoot::invalidateDescendantInsertionPoints()
383 {
384 m_descendantInsertionPointsIsValid = false;
385 m_shadowRootRareData->clearDescendantInsertionPoints();
386 }
387
descendantInsertionPoints()388 const Vector<RefPtr<InsertionPoint> >& ShadowRoot::descendantInsertionPoints()
389 {
390 DEFINE_STATIC_LOCAL(const Vector<RefPtr<InsertionPoint> >, emptyList, ());
391
392 if (m_shadowRootRareData && m_descendantInsertionPointsIsValid)
393 return m_shadowRootRareData->descendantInsertionPoints();
394
395 m_descendantInsertionPointsIsValid = true;
396
397 if (!containsInsertionPoints())
398 return emptyList;
399
400 Vector<RefPtr<InsertionPoint> > insertionPoints;
401 for (Element* element = ElementTraversal::firstWithin(*this); element; element = ElementTraversal::next(*element, this)) {
402 if (element->isInsertionPoint())
403 insertionPoints.append(toInsertionPoint(element));
404 }
405
406 ensureShadowRootRareData()->setDescendantInsertionPoints(insertionPoints);
407
408 return m_shadowRootRareData->descendantInsertionPoints();
409 }
410
styleSheets()411 StyleSheetList* ShadowRoot::styleSheets()
412 {
413 if (!ensureShadowRootRareData()->styleSheets())
414 m_shadowRootRareData->setStyleSheets(StyleSheetList::create(this));
415
416 return m_shadowRootRareData->styleSheets();
417 }
418
419 }
420