1/*
2 * Copyright (C) 2011 Adobe Systems Incorporated. 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
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above
9 *    copyright notice, this list of conditions and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 *    copyright notice, this list of conditions and the following
13 *    disclaimer in the documentation and/or other materials
14 *    provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "WebKitNamedFlow.h"
32
33#include "EventNames.h"
34#include "NamedFlowCollection.h"
35#include "RenderNamedFlowThread.h"
36#include "RenderRegion.h"
37#include "ScriptExecutionContext.h"
38#include "StaticNodeList.h"
39#include "UIEvent.h"
40
41namespace WebCore {
42
43WebKitNamedFlow::WebKitNamedFlow(PassRefPtr<NamedFlowCollection> manager, const AtomicString& flowThreadName)
44    : m_flowThreadName(flowThreadName)
45    , m_flowManager(manager)
46    , m_parentFlowThread(0)
47{
48}
49
50WebKitNamedFlow::~WebKitNamedFlow()
51{
52    // The named flow is not "strong" referenced from anywhere at this time so it shouldn't be reused if the named flow is recreated.
53    m_flowManager->discardNamedFlow(this);
54}
55
56PassRefPtr<WebKitNamedFlow> WebKitNamedFlow::create(PassRefPtr<NamedFlowCollection> manager, const AtomicString& flowThreadName)
57{
58    return adoptRef(new WebKitNamedFlow(manager, flowThreadName));
59}
60
61const AtomicString& WebKitNamedFlow::name() const
62{
63    return m_flowThreadName;
64}
65
66bool WebKitNamedFlow::overset() const
67{
68    if (m_flowManager->document())
69        m_flowManager->document()->updateLayoutIgnorePendingStylesheets();
70
71    // The renderer may be destroyed or created after the style update.
72    // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary.
73    return m_parentFlowThread ? m_parentFlowThread->overset() : true;
74}
75
76static inline bool inFlowThread(RenderObject* renderer, RenderNamedFlowThread* flowThread)
77{
78    if (!renderer)
79        return false;
80    RenderFlowThread* currentFlowThread = renderer->flowThreadContainingBlock();
81    if (flowThread == currentFlowThread)
82        return true;
83    if (renderer->flowThreadState() != RenderObject::InsideInFlowThread)
84        return false;
85
86    // An in-flow flow thread can be nested inside an out-of-flow one, so we have to recur up to check.
87    return inFlowThread(currentFlowThread->containingBlock(), flowThread);
88}
89
90int WebKitNamedFlow::firstEmptyRegionIndex() const
91{
92    if (m_flowManager->document())
93        m_flowManager->document()->updateLayoutIgnorePendingStylesheets();
94
95    if (!m_parentFlowThread)
96        return -1;
97
98    const RenderRegionList& regionList = m_parentFlowThread->renderRegionList();
99    if (regionList.isEmpty())
100        return -1;
101    RenderRegionList::const_iterator iter = regionList.begin();
102    for (int index = 0; iter != regionList.end(); ++index, ++iter) {
103        const RenderRegion* renderRegion = *iter;
104        if (renderRegion->regionState() == RenderRegion::RegionEmpty)
105            return index;
106    }
107    return -1;
108}
109
110PassRefPtr<NodeList> WebKitNamedFlow::getRegionsByContent(Node* contentNode)
111{
112    Vector<RefPtr<Node> > regionNodes;
113
114    if (!contentNode)
115        return StaticNodeList::adopt(regionNodes);
116
117    if (m_flowManager->document())
118        m_flowManager->document()->updateLayoutIgnorePendingStylesheets();
119
120    // The renderer may be destroyed or created after the style update.
121    // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary.
122    if (!m_parentFlowThread)
123        return StaticNodeList::adopt(regionNodes);
124
125    if (inFlowThread(contentNode->renderer(), m_parentFlowThread)) {
126        const RenderRegionList& regionList = m_parentFlowThread->renderRegionList();
127        for (RenderRegionList::const_iterator iter = regionList.begin(); iter != regionList.end(); ++iter) {
128            const RenderRegion* renderRegion = *iter;
129            // FIXME: Pseudo-elements are not included in the list.
130            if (!renderRegion->node())
131                continue;
132            if (m_parentFlowThread->objectInFlowRegion(contentNode->renderer(), renderRegion))
133                regionNodes.append(renderRegion->node());
134        }
135    }
136
137    return StaticNodeList::adopt(regionNodes);
138}
139
140PassRefPtr<NodeList> WebKitNamedFlow::getRegions()
141{
142    Vector<RefPtr<Node> > regionNodes;
143
144    if (m_flowManager->document())
145        m_flowManager->document()->updateLayoutIgnorePendingStylesheets();
146
147    // The renderer may be destroyed or created after the style update.
148    // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary.
149    if (!m_parentFlowThread)
150        return StaticNodeList::adopt(regionNodes);
151
152    const RenderRegionList& regionList = m_parentFlowThread->renderRegionList();
153    for (RenderRegionList::const_iterator iter = regionList.begin(); iter != regionList.end(); ++iter) {
154        const RenderRegion* renderRegion = *iter;
155        // FIXME: Pseudo-elements are not included in the list.
156        if (!renderRegion->node())
157            continue;
158        regionNodes.append(renderRegion->node());
159    }
160
161    return StaticNodeList::adopt(regionNodes);
162}
163
164PassRefPtr<NodeList> WebKitNamedFlow::getContent()
165{
166    Vector<RefPtr<Node> > contentNodes;
167
168    if (m_flowManager->document())
169        m_flowManager->document()->updateLayoutIgnorePendingStylesheets();
170
171    // The renderer may be destroyed or created after the style update.
172    // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary.
173    if (!m_parentFlowThread)
174        return StaticNodeList::adopt(contentNodes);
175
176    const NamedFlowContentNodes& contentNodesList = m_parentFlowThread->contentNodes();
177    for (NamedFlowContentNodes::const_iterator it = contentNodesList.begin(); it != contentNodesList.end(); ++it) {
178        Node* node = *it;
179        ASSERT(node->computedStyle()->flowThread() == m_parentFlowThread->flowThreadName());
180        contentNodes.append(node);
181    }
182
183    return StaticNodeList::adopt(contentNodes);
184}
185
186void WebKitNamedFlow::setRenderer(RenderNamedFlowThread* parentFlowThread)
187{
188    // The named flow can either go from a no_renderer->renderer or renderer->no_renderer state; anything else could indicate a bug.
189    ASSERT((!m_parentFlowThread && parentFlowThread) || (m_parentFlowThread && !parentFlowThread));
190
191    // If parentFlowThread is 0, the flow thread will move in the "NULL" state.
192    m_parentFlowThread = parentFlowThread;
193}
194
195EventTargetData* WebKitNamedFlow::eventTargetData()
196{
197    return &m_eventTargetData;
198}
199
200EventTargetData* WebKitNamedFlow::ensureEventTargetData()
201{
202    return &m_eventTargetData;
203}
204
205void WebKitNamedFlow::dispatchRegionLayoutUpdateEvent()
206{
207    ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
208
209    // If the flow is in the "NULL" state the event should not be dispatched any more.
210    if (flowState() == FlowStateNull)
211        return;
212
213    RefPtr<Event> event = UIEvent::create(eventNames().webkitregionlayoutupdateEvent, false, false, m_flowManager->document()->defaultView(), 0);
214
215    dispatchEvent(event);
216}
217
218const AtomicString& WebKitNamedFlow::interfaceName() const
219{
220    return eventNames().interfaceForWebKitNamedFlow;
221}
222
223ScriptExecutionContext* WebKitNamedFlow::scriptExecutionContext() const
224{
225    return m_flowManager->document();
226}
227
228Node* WebKitNamedFlow::ownerNode() const
229{
230    return m_flowManager->document();
231}
232
233} // namespace WebCore
234
235