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 "ContentDistributor.h"
29
30#include "ElementShadow.h"
31#include "HTMLContentElement.h"
32#include "NodeTraversal.h"
33#include "ShadowRoot.h"
34
35
36namespace WebCore {
37
38ContentDistributor::ContentDistributor()
39    : m_insertionPointListIsValid(true)
40    , m_validity(Undetermined)
41{
42}
43
44ContentDistributor::~ContentDistributor()
45{
46}
47
48void ContentDistributor::invalidateInsertionPointList()
49{
50    m_insertionPointListIsValid = false;
51    m_insertionPointList.clear();
52}
53
54const Vector<RefPtr<InsertionPoint> >& ContentDistributor::ensureInsertionPointList(ShadowRoot* shadowRoot)
55{
56    if (m_insertionPointListIsValid)
57        return m_insertionPointList;
58
59    m_insertionPointListIsValid = true;
60    ASSERT(m_insertionPointList.isEmpty());
61
62    for (Element* element = ElementTraversal::firstWithin(shadowRoot); element; element = ElementTraversal::next(element, shadowRoot)) {
63        if (element->isInsertionPoint())
64            m_insertionPointList.append(toInsertionPoint(element));
65    }
66
67    return m_insertionPointList;
68}
69
70InsertionPoint* ContentDistributor::findInsertionPointFor(const Node* key) const
71{
72    return m_nodeToInsertionPoint.get(key);
73}
74
75void ContentDistributor::distribute(Element* host)
76{
77    ASSERT(needsDistribution());
78    ASSERT(m_nodeToInsertionPoint.isEmpty());
79    ASSERT(!host->containingShadowRoot() || host->containingShadowRoot()->owner()->distributor().isValid());
80
81    m_validity = Valid;
82
83    if (ShadowRoot* root = host->shadowRoot()) {
84        const Vector<RefPtr<InsertionPoint> >& insertionPoints = ensureInsertionPointList(root);
85        for (size_t i = 0; i < insertionPoints.size(); ++i) {
86            InsertionPoint* point = insertionPoints[i].get();
87            if (!point->isActive())
88                continue;
89
90            distributeSelectionsTo(point, host);
91        }
92    }
93}
94
95bool ContentDistributor::invalidate(Element* host)
96{
97    ASSERT(needsInvalidation());
98    bool needsReattach = (m_validity == Undetermined) || !m_nodeToInsertionPoint.isEmpty();
99
100    if (ShadowRoot* root = host->shadowRoot()) {
101        const Vector<RefPtr<InsertionPoint> >& insertionPoints = ensureInsertionPointList(root);
102        for (size_t i = 0; i < insertionPoints.size(); ++i) {
103            needsReattach = true;
104            insertionPoints[i]->clearDistribution();
105        }
106    }
107
108    m_validity = Invalidating;
109    m_nodeToInsertionPoint.clear();
110    return needsReattach;
111}
112
113void ContentDistributor::distributeSelectionsTo(InsertionPoint* insertionPoint, Element* host)
114{
115    for (Node* child = host->firstChild(); child; child = child->nextSibling()) {
116        ASSERT(!child->isInsertionPoint());
117
118        if (insertionPoint->matchTypeFor(child) != InsertionPoint::AlwaysMatches)
119            continue;
120
121        m_nodeToInsertionPoint.add(child, insertionPoint);
122    }
123
124    if (m_nodeToInsertionPoint.isEmpty())
125        return;
126    insertionPoint->setHasDistribution();
127}
128
129void ContentDistributor::ensureDistribution(ShadowRoot* shadowRoot)
130{
131    ASSERT(shadowRoot);
132
133    Vector<ElementShadow*, 8> elementShadows;
134    for (Element* current = shadowRoot->host(); current; current = current->shadowHost()) {
135        ElementShadow* elementShadow = current->shadow();
136        if (!elementShadow->distributor().needsDistribution())
137            break;
138
139        elementShadows.append(elementShadow);
140    }
141
142    for (size_t i = elementShadows.size(); i > 0; --i)
143        elementShadows[i - 1]->distributor().distribute(elementShadows[i - 1]->host());
144}
145
146
147void ContentDistributor::invalidateDistribution(Element* host)
148{
149    bool didNeedInvalidation = needsInvalidation();
150    bool needsReattach = didNeedInvalidation ? invalidate(host) : false;
151
152    if (needsReattach && host->attached()) {
153        for (Node* n = host->firstChild(); n; n = n->nextSibling())
154            n->lazyReattach();
155        host->setNeedsStyleRecalc();
156    }
157
158    if (didNeedInvalidation) {
159        ASSERT(m_validity == Invalidating);
160        m_validity = Invalidated;
161    }
162}
163
164void ContentDistributor::didShadowBoundaryChange(Element* host)
165{
166    setValidity(Undetermined);
167    invalidateDistribution(host);
168}
169
170}
171