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 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 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 105 void ShadowRoot::dispose() 106 { 107 removeDetachedChildren(); 108 } 109 110 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 119 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 128 PassRefPtr<Node> ShadowRoot::cloneNode(bool, ExceptionState& exceptionState) 129 { 130 exceptionState.throwDOMException(DataCloneError, "ShadowRoot nodes are not clonable."); 131 return 0; 132 } 133 134 String ShadowRoot::innerHTML() const 135 { 136 return createMarkup(this, ChildrenOnly); 137 } 138 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 150 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 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 197 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 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 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 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 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 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 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 298 void ShadowRoot::registerScopedHTMLStyleChild() 299 { 300 ++m_numberOfStyles; 301 setHasScopedHTMLStyleChild(true); 302 } 303 304 void ShadowRoot::unregisterScopedHTMLStyleChild() 305 { 306 ASSERT(hasScopedHTMLStyleChild() && m_numberOfStyles > 0); 307 --m_numberOfStyles; 308 setHasScopedHTMLStyleChild(m_numberOfStyles > 0); 309 } 310 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 320 bool ShadowRoot::containsShadowElements() const 321 { 322 return m_shadowRootRareData ? m_shadowRootRareData->containsShadowElements() : 0; 323 } 324 325 bool ShadowRoot::containsContentElements() const 326 { 327 return m_shadowRootRareData ? m_shadowRootRareData->containsContentElements() : 0; 328 } 329 330 bool ShadowRoot::containsShadowRoots() const 331 { 332 return m_shadowRootRareData ? m_shadowRootRareData->containsShadowRoots() : 0; 333 } 334 335 unsigned ShadowRoot::descendantShadowElementCount() const 336 { 337 return m_shadowRootRareData ? m_shadowRootRareData->descendantShadowElementCount() : 0; 338 } 339 340 HTMLShadowElement* ShadowRoot::shadowInsertionPointOfYoungerShadowRoot() const 341 { 342 return m_shadowRootRareData ? m_shadowRootRareData->shadowInsertionPointOfYoungerShadowRoot() : 0; 343 } 344 345 void ShadowRoot::setShadowInsertionPointOfYoungerShadowRoot(PassRefPtr<HTMLShadowElement> shadowInsertionPoint) 346 { 347 if (!m_shadowRootRareData && !shadowInsertionPoint) 348 return; 349 ensureShadowRootRareData()->setShadowInsertionPointOfYoungerShadowRoot(shadowInsertionPoint); 350 } 351 352 void ShadowRoot::didAddInsertionPoint(InsertionPoint* insertionPoint) 353 { 354 ensureShadowRootRareData()->didAddInsertionPoint(insertionPoint); 355 invalidateDescendantInsertionPoints(); 356 } 357 358 void ShadowRoot::didRemoveInsertionPoint(InsertionPoint* insertionPoint) 359 { 360 m_shadowRootRareData->didRemoveInsertionPoint(insertionPoint); 361 invalidateDescendantInsertionPoints(); 362 } 363 364 void ShadowRoot::addChildShadowRoot() 365 { 366 ensureShadowRootRareData()->didAddChildShadowRoot(); 367 } 368 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 377 unsigned ShadowRoot::childShadowRootCount() const 378 { 379 return m_shadowRootRareData ? m_shadowRootRareData->childShadowRootCount() : 0; 380 } 381 382 void ShadowRoot::invalidateDescendantInsertionPoints() 383 { 384 m_descendantInsertionPointsIsValid = false; 385 m_shadowRootRareData->clearDescendantInsertionPoints(); 386 } 387 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 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