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/resolver/StyleResolver.h" 32 #include "core/dom/Text.h" 33 #include "core/dom/shadow/ContentDistributor.h" 34 #include "core/dom/shadow/ElementShadow.h" 35 #include "core/dom/shadow/InsertionPoint.h" 36 #include "core/editing/markup.h" 37 #include "core/platform/HistogramSupport.h" 38 39 namespace WebCore { 40 41 struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope, public DoublyLinkedListNode<ShadowRoot> { 42 void* pointers[3]; 43 unsigned countersAndFlags[1]; 44 }; 45 46 COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small); 47 48 enum ShadowRootUsageOriginType { 49 ShadowRootUsageOriginWeb = 0, 50 ShadowRootUsageOriginNotWeb, 51 ShadowRootUsageOriginMax 52 }; 53 54 ShadowRoot::ShadowRoot(Document* document, ShadowRootType type) 55 : DocumentFragment(0, CreateShadowRoot) 56 , TreeScope(this, document) 57 , m_prev(0) 58 , m_next(0) 59 , m_numberOfStyles(0) 60 , m_applyAuthorStyles(false) 61 , m_resetStyleInheritance(false) 62 , m_type(type) 63 , m_registeredWithParentShadowRoot(false) 64 { 65 ASSERT(document); 66 ScriptWrappable::init(this); 67 68 if (type == ShadowRoot::AuthorShadowRoot) { 69 ShadowRootUsageOriginType usageType = document->url().protocolIsInHTTPFamily() ? ShadowRootUsageOriginWeb : ShadowRootUsageOriginNotWeb; 70 HistogramSupport::histogramEnumeration("WebCore.ShadowRoot.constructor", usageType, ShadowRootUsageOriginMax); 71 } 72 } 73 74 ShadowRoot::~ShadowRoot() 75 { 76 ASSERT(!m_prev); 77 ASSERT(!m_next); 78 79 // We cannot let ContainerNode destructor call willBeDeletedFrom() 80 // for this ShadowRoot instance because TreeScope destructor 81 // clears Node::m_treeScope thus ContainerNode is no longer able 82 // to access it Document reference after that. 83 willBeDeletedFrom(documentInternal()); 84 85 // We must remove all of our children first before the TreeScope destructor 86 // runs so we don't go through TreeScopeAdopter for each child with a 87 // destructed tree scope in each descendant. 88 removeDetachedChildren(); 89 90 // We must call clearRareData() here since a ShadowRoot class inherits TreeScope 91 // as well as Node. See a comment on TreeScope.h for the reason. 92 if (hasRareData()) 93 clearRareData(); 94 } 95 96 void ShadowRoot::dispose() 97 { 98 removeDetachedChildren(); 99 } 100 101 ShadowRoot* ShadowRoot::bindingsOlderShadowRoot() const 102 { 103 ShadowRoot* older = olderShadowRoot(); 104 while (older && !older->shouldExposeToBindings()) 105 older = older->olderShadowRoot(); 106 ASSERT(!older || older->shouldExposeToBindings()); 107 return older; 108 } 109 110 bool ShadowRoot::isOldestAuthorShadowRoot() const 111 { 112 if (type() != AuthorShadowRoot) 113 return false; 114 if (ShadowRoot* older = olderShadowRoot()) 115 return older->type() == UserAgentShadowRoot; 116 return true; 117 } 118 119 PassRefPtr<Node> ShadowRoot::cloneNode(bool, ExceptionState& es) 120 { 121 es.throwDOMException(DataCloneError); 122 return 0; 123 } 124 125 String ShadowRoot::innerHTML() const 126 { 127 return createMarkup(this, ChildrenOnly); 128 } 129 130 void ShadowRoot::setInnerHTML(const String& markup, ExceptionState& es) 131 { 132 if (isOrphan()) { 133 es.throwDOMException(InvalidAccessError); 134 return; 135 } 136 137 if (RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, host(), AllowScriptingContent, es)) 138 replaceChildrenWithFragment(this, fragment.release(), es); 139 } 140 141 bool ShadowRoot::childTypeAllowed(NodeType type) const 142 { 143 switch (type) { 144 case ELEMENT_NODE: 145 case PROCESSING_INSTRUCTION_NODE: 146 case COMMENT_NODE: 147 case TEXT_NODE: 148 case CDATA_SECTION_NODE: 149 return true; 150 default: 151 return false; 152 } 153 } 154 155 void ShadowRoot::recalcStyle(StyleChange change) 156 { 157 // ShadowRoot doesn't support custom callbacks. 158 ASSERT(!hasCustomStyleCallbacks()); 159 160 StyleResolver* styleResolver = document()->styleResolver(); 161 styleResolver->pushParentShadowRoot(this); 162 163 if (!attached()) { 164 attach(); 165 return; 166 } 167 168 // When we're set to lazyAttach we'll have a SubtreeStyleChange and we'll need 169 // to promote the change to a Force for all our descendants so they get a 170 // recalc and will attach. 171 if (styleChangeType() >= SubtreeStyleChange) 172 change = Force; 173 174 // FIXME: This doesn't handle :hover + div properly like Element::recalcStyle does. 175 bool forceReattachOfAnyWhitespaceSibling = false; 176 for (Node* child = firstChild(); child; child = child->nextSibling()) { 177 bool didReattach = false; 178 179 if (child->renderer()) 180 forceReattachOfAnyWhitespaceSibling = false; 181 182 if (child->isTextNode()) { 183 if (forceReattachOfAnyWhitespaceSibling && toText(child)->containsOnlyWhitespace()) 184 child->reattach(); 185 else 186 didReattach = toText(child)->recalcTextStyle(change); 187 } else if (child->isElementNode() && shouldRecalcStyle(change, child)) { 188 didReattach = toElement(child)->recalcStyle(change); 189 } 190 191 forceReattachOfAnyWhitespaceSibling = didReattach || forceReattachOfAnyWhitespaceSibling; 192 } 193 194 styleResolver->popParentShadowRoot(this); 195 clearNeedsStyleRecalc(); 196 clearChildNeedsStyleRecalc(); 197 } 198 199 bool ShadowRoot::isActive() const 200 { 201 for (ShadowRoot* shadowRoot = youngerShadowRoot(); shadowRoot; shadowRoot = shadowRoot->youngerShadowRoot()) 202 if (!shadowRoot->containsShadowElements()) 203 return false; 204 return true; 205 } 206 207 void ShadowRoot::setApplyAuthorStyles(bool value) 208 { 209 if (isOrphan()) 210 return; 211 212 if (m_applyAuthorStyles == value) 213 return; 214 215 m_applyAuthorStyles = value; 216 if (!isActive()) 217 return; 218 219 ASSERT(host()); 220 ASSERT(host()->shadow()); 221 if (host()->shadow()->didAffectApplyAuthorStyles()) 222 host()->setNeedsStyleRecalc(); 223 224 // Since styles in shadow trees can select shadow hosts, set shadow host's needs-recalc flag true. 225 // FIXME: host->setNeedsStyleRecalc() should take care of all elements in its shadow tree. 226 // However, when host's recalcStyle is skipped (i.e. host's parent has no renderer), 227 // no recalc style is invoked for any elements in its shadow tree. 228 // This problem occurs when using getComputedStyle() API. 229 // So currently host and shadow root's needsStyleRecalc flags are set to be true. 230 setNeedsStyleRecalc(); 231 } 232 233 void ShadowRoot::setResetStyleInheritance(bool value) 234 { 235 if (isOrphan()) 236 return; 237 238 if (value == m_resetStyleInheritance) 239 return; 240 241 m_resetStyleInheritance = value; 242 if (!isActive()) 243 return; 244 245 setNeedsStyleRecalc(); 246 } 247 248 void ShadowRoot::attach(const AttachContext& context) 249 { 250 StyleResolver* styleResolver = document()->styleResolver(); 251 styleResolver->pushParentShadowRoot(this); 252 DocumentFragment::attach(context); 253 styleResolver->popParentShadowRoot(this); 254 } 255 256 Node::InsertionNotificationRequest ShadowRoot::insertedInto(ContainerNode* insertionPoint) 257 { 258 DocumentFragment::insertedInto(insertionPoint); 259 260 if (!insertionPoint->inDocument() || !isOldest()) 261 return InsertionDone; 262 263 // FIXME: When parsing <video controls>, insertedInto() is called many times without invoking removedFrom. 264 // For now, we check m_registeredWithParentShadowroot. We would like to ASSERT(!m_registeredShadowRoot) here. 265 // https://bugs.webkit.org/show_bug.cig?id=101316 266 if (m_registeredWithParentShadowRoot) 267 return InsertionDone; 268 269 if (ShadowRoot* root = host()->containingShadowRoot()) { 270 root->ensureScopeDistribution()->registerElementShadow(); 271 m_registeredWithParentShadowRoot = true; 272 } 273 274 return InsertionDone; 275 } 276 277 void ShadowRoot::removedFrom(ContainerNode* insertionPoint) 278 { 279 if (insertionPoint->inDocument() && m_registeredWithParentShadowRoot) { 280 ShadowRoot* root = host()->containingShadowRoot(); 281 if (!root) 282 root = insertionPoint->containingShadowRoot(); 283 284 if (root && root->scopeDistribution()) 285 root->scopeDistribution()->unregisterElementShadow(); 286 m_registeredWithParentShadowRoot = false; 287 } 288 289 DocumentFragment::removedFrom(insertionPoint); 290 } 291 292 void ShadowRoot::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) 293 { 294 ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 295 if (InsertionPoint* point = insertionPoint()) { 296 if (ShadowRoot* root = point->containingShadowRoot()) 297 root->owner()->setNeedsDistributionRecalc(); 298 } 299 } 300 301 void ShadowRoot::registerScopedHTMLStyleChild() 302 { 303 ++m_numberOfStyles; 304 setHasScopedHTMLStyleChild(true); 305 } 306 307 void ShadowRoot::unregisterScopedHTMLStyleChild() 308 { 309 ASSERT(hasScopedHTMLStyleChild() && m_numberOfStyles > 0); 310 --m_numberOfStyles; 311 setHasScopedHTMLStyleChild(m_numberOfStyles > 0); 312 } 313 314 ScopeContentDistribution* ShadowRoot::ensureScopeDistribution() 315 { 316 if (m_scopeDistribution) 317 return m_scopeDistribution.get(); 318 319 m_scopeDistribution = adoptPtr(new ScopeContentDistribution); 320 return m_scopeDistribution.get(); 321 } 322 323 bool ShadowRoot::containsShadowElements() const 324 { 325 return m_scopeDistribution ? m_scopeDistribution->hasShadowElementChildren() : 0; 326 } 327 328 bool ShadowRoot::containsContentElements() const 329 { 330 return m_scopeDistribution ? m_scopeDistribution->hasContentElementChildren() : 0; 331 } 332 333 bool ShadowRoot::containsShadowRoots() const 334 { 335 return m_scopeDistribution ? m_scopeDistribution->numberOfElementShadowChildren() : 0; 336 } 337 338 InsertionPoint* ShadowRoot::insertionPoint() const 339 { 340 return m_scopeDistribution ? m_scopeDistribution->insertionPointAssignedTo() : 0; 341 } 342 343 } 344