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