Home | History | Annotate | Download | only in shadow
      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