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/css/resolver/StyleResolverParentScope.h" 34 #include "core/dom/ElementTraversal.h" 35 #include "core/dom/StyleEngine.h" 36 #include "core/dom/Text.h" 37 #include "core/dom/shadow/ElementShadow.h" 38 #include "core/dom/shadow/InsertionPoint.h" 39 #include "core/dom/shadow/ShadowRootRareData.h" 40 #include "core/editing/markup.h" 41 #include "core/html/HTMLShadowElement.h" 42 #include "public/platform/Platform.h" 43 44 namespace WebCore { 45 46 struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope, public DoublyLinkedListNode<ShadowRoot> { 47 void* pointers[3]; 48 unsigned countersAndFlags[1]; 49 }; 50 51 COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small); 52 53 ShadowRoot::ShadowRoot(Document& document, ShadowRootType type) 54 : DocumentFragment(0, CreateShadowRoot) 55 , TreeScope(*this, document) 56 , m_prev(nullptr) 57 , m_next(nullptr) 58 , m_numberOfStyles(0) 59 , m_type(type) 60 , m_registeredWithParentShadowRoot(false) 61 , m_descendantInsertionPointsIsValid(false) 62 { 63 ScriptWrappable::init(this); 64 } 65 66 ShadowRoot::~ShadowRoot() 67 { 68 #if !ENABLE(OILPAN) 69 ASSERT(!m_prev); 70 ASSERT(!m_next); 71 72 if (m_shadowRootRareData && m_shadowRootRareData->styleSheets()) 73 m_shadowRootRareData->styleSheets()->detachFromDocument(); 74 75 document().styleEngine()->didRemoveShadowRoot(this); 76 77 // We cannot let ContainerNode destructor call willBeDeletedFromDocument() 78 // for this ShadowRoot instance because TreeScope destructor 79 // clears Node::m_treeScope thus ContainerNode is no longer able 80 // to access it Document reference after that. 81 willBeDeletedFromDocument(); 82 83 // We must remove all of our children first before the TreeScope destructor 84 // runs so we don't go through TreeScopeAdopter for each child with a 85 // destructed tree scope in each descendant. 86 removeDetachedChildren(); 87 88 // We must call clearRareData() here since a ShadowRoot class inherits TreeScope 89 // as well as Node. See a comment on TreeScope.h for the reason. 90 if (hasRareData()) 91 clearRareData(); 92 #endif 93 } 94 95 #if !ENABLE(OILPAN) 96 void ShadowRoot::dispose() 97 { 98 removeDetachedChildren(); 99 } 100 #endif 101 102 ShadowRoot* ShadowRoot::olderShadowRootForBindings() const 103 { 104 ShadowRoot* older = olderShadowRoot(); 105 while (older && !older->shouldExposeToBindings()) 106 older = older->olderShadowRoot(); 107 ASSERT(!older || older->shouldExposeToBindings()); 108 return older; 109 } 110 111 PassRefPtrWillBeRawPtr<Node> ShadowRoot::cloneNode(bool, ExceptionState& exceptionState) 112 { 113 exceptionState.throwDOMException(DataCloneError, "ShadowRoot nodes are not clonable."); 114 return nullptr; 115 } 116 117 String ShadowRoot::innerHTML() const 118 { 119 return createMarkup(this, ChildrenOnly); 120 } 121 122 void ShadowRoot::setInnerHTML(const String& markup, ExceptionState& exceptionState) 123 { 124 if (isOrphan()) { 125 exceptionState.throwDOMException(InvalidAccessError, "The ShadowRoot does not have a host."); 126 return; 127 } 128 129 if (RefPtrWillBeRawPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, host(), AllowScriptingContent, "innerHTML", exceptionState)) 130 replaceChildrenWithFragment(this, fragment.release(), exceptionState); 131 } 132 133 void ShadowRoot::recalcStyle(StyleRecalcChange change) 134 { 135 // ShadowRoot doesn't support custom callbacks. 136 ASSERT(!hasCustomStyleCallbacks()); 137 138 StyleResolverParentScope parentScope(*this); 139 140 if (styleChangeType() >= SubtreeStyleChange) 141 change = Force; 142 143 if (change < Force && hasRareData() && childNeedsStyleRecalc()) 144 checkForChildrenAdjacentRuleChanges(); 145 146 // There's no style to update so just calling recalcStyle means we're updated. 147 clearNeedsStyleRecalc(); 148 149 // FIXME: This doesn't handle :hover + div properly like Element::recalcStyle does. 150 Text* lastTextNode = 0; 151 for (Node* child = lastChild(); child; child = child->previousSibling()) { 152 if (child->isTextNode()) { 153 toText(child)->recalcTextStyle(change, lastTextNode); 154 lastTextNode = toText(child); 155 } else if (child->isElementNode()) { 156 if (child->shouldCallRecalcStyle(change)) 157 toElement(child)->recalcStyle(change, lastTextNode); 158 if (child->renderer()) 159 lastTextNode = 0; 160 } 161 } 162 163 clearChildNeedsStyleRecalc(); 164 } 165 166 void ShadowRoot::attach(const AttachContext& context) 167 { 168 StyleResolverParentScope parentScope(*this); 169 DocumentFragment::attach(context); 170 } 171 172 Node::InsertionNotificationRequest ShadowRoot::insertedInto(ContainerNode* insertionPoint) 173 { 174 DocumentFragment::insertedInto(insertionPoint); 175 176 if (!insertionPoint->inDocument() || !isOldest()) 177 return InsertionDone; 178 179 // FIXME: When parsing <video controls>, insertedInto() is called many times without invoking removedFrom. 180 // For now, we check m_registeredWithParentShadowroot. We would like to ASSERT(!m_registeredShadowRoot) here. 181 // https://bugs.webkit.org/show_bug.cig?id=101316 182 if (m_registeredWithParentShadowRoot) 183 return InsertionDone; 184 185 if (ShadowRoot* root = host()->containingShadowRoot()) { 186 root->addChildShadowRoot(); 187 m_registeredWithParentShadowRoot = true; 188 } 189 190 return InsertionDone; 191 } 192 193 void ShadowRoot::removedFrom(ContainerNode* insertionPoint) 194 { 195 if (insertionPoint->inDocument() && m_registeredWithParentShadowRoot) { 196 ShadowRoot* root = host()->containingShadowRoot(); 197 if (!root) 198 root = insertionPoint->containingShadowRoot(); 199 if (root) 200 root->removeChildShadowRoot(); 201 m_registeredWithParentShadowRoot = false; 202 } 203 204 DocumentFragment::removedFrom(insertionPoint); 205 } 206 207 void ShadowRoot::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) 208 { 209 ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 210 211 checkForSiblingStyleChanges(false, beforeChange, afterChange, childCountDelta); 212 213 if (InsertionPoint* point = shadowInsertionPointOfYoungerShadowRoot()) { 214 if (ShadowRoot* root = point->containingShadowRoot()) 215 root->owner()->setNeedsDistributionRecalc(); 216 } 217 } 218 219 void ShadowRoot::registerScopedHTMLStyleChild() 220 { 221 ++m_numberOfStyles; 222 } 223 224 void ShadowRoot::unregisterScopedHTMLStyleChild() 225 { 226 ASSERT(m_numberOfStyles > 0); 227 --m_numberOfStyles; 228 } 229 230 ShadowRootRareData* ShadowRoot::ensureShadowRootRareData() 231 { 232 if (m_shadowRootRareData) 233 return m_shadowRootRareData.get(); 234 235 m_shadowRootRareData = adoptPtrWillBeNoop(new ShadowRootRareData); 236 return m_shadowRootRareData.get(); 237 } 238 239 bool ShadowRoot::containsShadowElements() const 240 { 241 return m_shadowRootRareData ? m_shadowRootRareData->containsShadowElements() : 0; 242 } 243 244 bool ShadowRoot::containsContentElements() const 245 { 246 return m_shadowRootRareData ? m_shadowRootRareData->containsContentElements() : 0; 247 } 248 249 bool ShadowRoot::containsShadowRoots() const 250 { 251 return m_shadowRootRareData ? m_shadowRootRareData->containsShadowRoots() : 0; 252 } 253 254 unsigned ShadowRoot::descendantShadowElementCount() const 255 { 256 return m_shadowRootRareData ? m_shadowRootRareData->descendantShadowElementCount() : 0; 257 } 258 259 HTMLShadowElement* ShadowRoot::shadowInsertionPointOfYoungerShadowRoot() const 260 { 261 return m_shadowRootRareData ? m_shadowRootRareData->shadowInsertionPointOfYoungerShadowRoot() : 0; 262 } 263 264 void ShadowRoot::setShadowInsertionPointOfYoungerShadowRoot(PassRefPtrWillBeRawPtr<HTMLShadowElement> shadowInsertionPoint) 265 { 266 if (!m_shadowRootRareData && !shadowInsertionPoint) 267 return; 268 ensureShadowRootRareData()->setShadowInsertionPointOfYoungerShadowRoot(shadowInsertionPoint); 269 } 270 271 void ShadowRoot::didAddInsertionPoint(InsertionPoint* insertionPoint) 272 { 273 ensureShadowRootRareData()->didAddInsertionPoint(insertionPoint); 274 invalidateDescendantInsertionPoints(); 275 } 276 277 void ShadowRoot::didRemoveInsertionPoint(InsertionPoint* insertionPoint) 278 { 279 m_shadowRootRareData->didRemoveInsertionPoint(insertionPoint); 280 invalidateDescendantInsertionPoints(); 281 } 282 283 void ShadowRoot::addChildShadowRoot() 284 { 285 ensureShadowRootRareData()->didAddChildShadowRoot(); 286 } 287 288 void ShadowRoot::removeChildShadowRoot() 289 { 290 // FIXME: Why isn't this an ASSERT? 291 if (!m_shadowRootRareData) 292 return; 293 m_shadowRootRareData->didRemoveChildShadowRoot(); 294 } 295 296 unsigned ShadowRoot::childShadowRootCount() const 297 { 298 return m_shadowRootRareData ? m_shadowRootRareData->childShadowRootCount() : 0; 299 } 300 301 void ShadowRoot::invalidateDescendantInsertionPoints() 302 { 303 m_descendantInsertionPointsIsValid = false; 304 m_shadowRootRareData->clearDescendantInsertionPoints(); 305 } 306 307 const WillBeHeapVector<RefPtrWillBeMember<InsertionPoint> >& ShadowRoot::descendantInsertionPoints() 308 { 309 DEFINE_STATIC_LOCAL(WillBePersistentHeapVector<RefPtrWillBeMember<InsertionPoint> >, emptyList, ()); 310 if (m_shadowRootRareData && m_descendantInsertionPointsIsValid) 311 return m_shadowRootRareData->descendantInsertionPoints(); 312 313 m_descendantInsertionPointsIsValid = true; 314 315 if (!containsInsertionPoints()) 316 return emptyList; 317 318 WillBeHeapVector<RefPtrWillBeMember<InsertionPoint> > insertionPoints; 319 for (Element* element = ElementTraversal::firstWithin(*this); element; element = ElementTraversal::next(*element, this)) { 320 if (element->isInsertionPoint()) 321 insertionPoints.append(toInsertionPoint(element)); 322 } 323 324 ensureShadowRootRareData()->setDescendantInsertionPoints(insertionPoints); 325 326 return m_shadowRootRareData->descendantInsertionPoints(); 327 } 328 329 StyleSheetList* ShadowRoot::styleSheets() 330 { 331 if (!ensureShadowRootRareData()->styleSheets()) 332 m_shadowRootRareData->setStyleSheets(StyleSheetList::create(this)); 333 334 return m_shadowRootRareData->styleSheets(); 335 } 336 337 void ShadowRoot::trace(Visitor* visitor) 338 { 339 visitor->trace(m_prev); 340 visitor->trace(m_next); 341 visitor->trace(m_shadowRootRareData); 342 TreeScope::trace(visitor); 343 DocumentFragment::trace(visitor); 344 } 345 346 } 347