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/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