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 "NamedFlowCollection.h" 34#include "RenderNamedFlowFragment.h" 35#include "RenderNamedFlowThread.h" 36#include "RenderRegion.h" 37#include "StaticNodeList.h" 38#include "UIEvent.h" 39 40namespace WebCore { 41 42WebKitNamedFlow::WebKitNamedFlow(PassRefPtr<NamedFlowCollection> manager, const AtomicString& flowThreadName) 43 : m_flowThreadName(flowThreadName) 44 , m_flowManager(manager) 45 , m_parentFlowThread(0) 46{ 47} 48 49WebKitNamedFlow::~WebKitNamedFlow() 50{ 51 // The named flow is not "strong" referenced from anywhere at this time so it shouldn't be reused if the named flow is recreated. 52 m_flowManager->discardNamedFlow(this); 53} 54 55PassRefPtr<WebKitNamedFlow> WebKitNamedFlow::create(PassRefPtr<NamedFlowCollection> manager, const AtomicString& flowThreadName) 56{ 57 return adoptRef(new WebKitNamedFlow(manager, flowThreadName)); 58} 59 60const AtomicString& WebKitNamedFlow::name() const 61{ 62 return m_flowThreadName; 63} 64 65bool WebKitNamedFlow::overset() const 66{ 67 if (m_flowManager->document()) 68 m_flowManager->document()->updateLayoutIgnorePendingStylesheets(); 69 70 // The renderer may be destroyed or created after the style update. 71 // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary. 72 if (!m_parentFlowThread || !m_parentFlowThread->hasRegions()) 73 return true; 74 75 const RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(m_parentFlowThread->lastRegion()); 76 return namedFlowFragment->regionOversetState() == RegionOverset; 77} 78 79static inline bool inFlowThread(RenderObject* renderer, RenderNamedFlowThread* flowThread) 80{ 81 if (!renderer) 82 return false; 83 RenderFlowThread* currentFlowThread = renderer->flowThreadContainingBlock(); 84 if (flowThread == currentFlowThread) 85 return true; 86 if (renderer->flowThreadState() != RenderObject::InsideInFlowThread) 87 return false; 88 89 // An in-flow flow thread can be nested inside an out-of-flow one, so we have to recur up to check. 90 return inFlowThread(currentFlowThread->containingBlock(), flowThread); 91} 92 93int WebKitNamedFlow::firstEmptyRegionIndex() const 94{ 95 if (m_flowManager->document()) 96 m_flowManager->document()->updateLayoutIgnorePendingStylesheets(); 97 98 if (!m_parentFlowThread) 99 return -1; 100 101 const RenderRegionList& regionList = m_parentFlowThread->renderRegionList(); 102 if (regionList.isEmpty()) 103 return -1; 104 105 int countNonPseudoRegions = -1; 106 for (const auto& renderRegion : regionList) { 107 const RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(renderRegion); 108 // FIXME: Pseudo-elements are not included in the list. 109 // They will be included when we will properly support the Region interface 110 // http://dev.w3.org/csswg/css-regions/#the-region-interface 111 if (namedFlowFragment->isPseudoElementRegion()) 112 continue; 113 countNonPseudoRegions++; 114 if (namedFlowFragment->regionOversetState() == RegionEmpty) 115 return countNonPseudoRegions; 116 } 117 return -1; 118} 119 120PassRefPtr<NodeList> WebKitNamedFlow::getRegionsByContent(Node* contentNode) 121{ 122 if (!contentNode) 123 return StaticElementList::createEmpty(); 124 125 if (m_flowManager->document()) 126 m_flowManager->document()->updateLayoutIgnorePendingStylesheets(); 127 128 // The renderer may be destroyed or created after the style update. 129 // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary. 130 if (!m_parentFlowThread) 131 return StaticElementList::createEmpty(); 132 133 Vector<Ref<Element>> regionElements; 134 135 if (inFlowThread(contentNode->renderer(), m_parentFlowThread)) { 136 const RenderRegionList& regionList = m_parentFlowThread->renderRegionList(); 137 for (const auto& renderRegion : regionList) { 138 const RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(renderRegion); 139 // FIXME: Pseudo-elements are not included in the list. 140 // They will be included when we will properly support the Region interface 141 // http://dev.w3.org/csswg/css-regions/#the-region-interface 142 if (namedFlowFragment->isPseudoElementRegion()) 143 continue; 144 if (m_parentFlowThread->objectInFlowRegion(contentNode->renderer(), namedFlowFragment)) { 145 ASSERT(namedFlowFragment->generatingElement()); 146 regionElements.append(*namedFlowFragment->generatingElement()); 147 } 148 } 149 } 150 151 return StaticElementList::adopt(regionElements); 152} 153 154PassRefPtr<NodeList> WebKitNamedFlow::getRegions() 155{ 156 if (m_flowManager->document()) 157 m_flowManager->document()->updateLayoutIgnorePendingStylesheets(); 158 159 // The renderer may be destroyed or created after the style update. 160 // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary. 161 if (!m_parentFlowThread) 162 return StaticElementList::createEmpty(); 163 164 Vector<Ref<Element>> regionElements; 165 166 const RenderRegionList& regionList = m_parentFlowThread->renderRegionList(); 167 for (const auto& renderRegion : regionList) { 168 const RenderNamedFlowFragment* namedFlowFragment = toRenderNamedFlowFragment(renderRegion); 169 // FIXME: Pseudo-elements are not included in the list. 170 // They will be included when we will properly support the Region interface 171 // http://dev.w3.org/csswg/css-regions/#the-region-interface 172 if (namedFlowFragment->isPseudoElementRegion()) 173 continue; 174 ASSERT(namedFlowFragment->generatingElement()); 175 regionElements.append(*namedFlowFragment->generatingElement()); 176 } 177 178 return StaticElementList::adopt(regionElements); 179} 180 181PassRefPtr<NodeList> WebKitNamedFlow::getContent() 182{ 183 if (m_flowManager->document()) 184 m_flowManager->document()->updateLayoutIgnorePendingStylesheets(); 185 186 // The renderer may be destroyed or created after the style update. 187 // Because this is called from JS, where the wrapper keeps a reference to the NamedFlow, no guard is necessary. 188 if (!m_parentFlowThread) 189 return StaticElementList::createEmpty(); 190 191 Vector<Ref<Element>> contentElements; 192 193 const NamedFlowContentElements& contentElementsList = m_parentFlowThread->contentElements(); 194 for (auto& element : contentElementsList) { 195 ASSERT(element->computedStyle()->flowThread() == m_parentFlowThread->flowThreadName()); 196 contentElements.append(*element); 197 } 198 199 return StaticElementList::adopt(contentElements); 200} 201 202void WebKitNamedFlow::setRenderer(RenderNamedFlowThread* parentFlowThread) 203{ 204 // The named flow can either go from a no_renderer->renderer or renderer->no_renderer state; anything else could indicate a bug. 205 ASSERT((!m_parentFlowThread && parentFlowThread) || (m_parentFlowThread && !parentFlowThread)); 206 207 // If parentFlowThread is 0, the flow thread will move in the "NULL" state. 208 m_parentFlowThread = parentFlowThread; 209} 210 211void WebKitNamedFlow::dispatchRegionOversetChangeEvent() 212{ 213 ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden()); 214 215 // If the flow is in the "NULL" state the event should not be dispatched any more. 216 if (flowState() == FlowStateNull) 217 return; 218 219 dispatchEvent(UIEvent::create(eventNames().webkitregionoversetchangeEvent, false, false, m_flowManager->document()->defaultView(), 0)); 220} 221 222ScriptExecutionContext* WebKitNamedFlow::scriptExecutionContext() const 223{ 224 return m_flowManager->document(); 225} 226 227Node* WebKitNamedFlow::ownerNode() const 228{ 229 return m_flowManager->document(); 230} 231 232} // namespace WebCore 233 234