| 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 | #pragma once |
| 31 | |
| 32 | #include "LayerFragment.h" |
| 33 | #include "RenderBlockFlow.h" |
| 34 | #include "RenderFragmentContainer.h" |
| 35 | #include <wtf/ListHashSet.h> |
| 36 | |
| 37 | namespace WebCore { |
| 38 | |
| 39 | class CurrentRenderFragmentContainerMaintainer; |
| 40 | class RenderFragmentedFlow; |
| 41 | class RenderStyle; |
| 42 | class RenderFragmentContainer; |
| 43 | class RootInlineBox; |
| 44 | |
| 45 | typedef ListHashSet<RenderFragmentContainer*> RenderFragmentContainerList; |
| 46 | typedef Vector<RenderLayer*> RenderLayerList; |
| 47 | typedef HashMap<const RootInlineBox*, RenderFragmentContainer*> ContainingFragmentMap; |
| 48 | |
| 49 | // RenderFragmentedFlow is used to collect all the render objects that participate in a |
| 50 | // flow thread. It will also help in doing the layout. However, it will not render |
| 51 | // directly to screen. Instead, RenderFragmentContainer objects will redirect their paint |
| 52 | // and nodeAtPoint methods to this object. Each RenderFragmentContainer will actually be a viewPort |
| 53 | // of the RenderFragmentedFlow. |
| 54 | |
| 55 | class RenderFragmentedFlow: public RenderBlockFlow { |
| 56 | WTF_MAKE_ISO_ALLOCATED(RenderFragmentedFlow); |
| 57 | public: |
| 58 | virtual ~RenderFragmentedFlow() = default; |
| 59 | |
| 60 | virtual void removeFlowChildInfo(RenderElement&); |
| 61 | #ifndef NDEBUG |
| 62 | bool hasChildInfo(RenderObject* child) const { return is<RenderBox>(child) && m_fragmentRangeMap.contains(downcast<RenderBox>(child)); } |
| 63 | #endif |
| 64 | |
| 65 | #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED |
| 66 | bool checkLinesConsistency(const RenderBlockFlow&) const; |
| 67 | #endif |
| 68 | |
| 69 | void deleteLines() override; |
| 70 | |
| 71 | virtual void addFragmentToThread(RenderFragmentContainer*) = 0; |
| 72 | virtual void removeFragmentFromThread(RenderFragmentContainer*); |
| 73 | const RenderFragmentContainerList& renderFragmentContainerList() const { return m_fragmentList; } |
| 74 | |
| 75 | void updateLogicalWidth() final; |
| 76 | LogicalExtentComputedValues computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop) const override; |
| 77 | |
| 78 | bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction) override; |
| 79 | |
| 80 | bool hasFragments() const { return m_fragmentList.size(); } |
| 81 | virtual void fragmentChangedWritingMode(RenderFragmentContainer*) { } |
| 82 | |
| 83 | void validateFragments(); |
| 84 | void invalidateFragments(MarkingBehavior = MarkContainingBlockChain); |
| 85 | bool hasValidFragmentInfo() const { return !m_fragmentsInvalidated && !m_fragmentList.isEmpty(); } |
| 86 | |
| 87 | // Called when a descendant box's layout is finished and it has been positioned within its container. |
| 88 | virtual void fragmentedFlowDescendantBoxLaidOut(RenderBox*) { } |
| 89 | |
| 90 | void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override; |
| 91 | |
| 92 | void repaintRectangleInFragments(const LayoutRect&) const; |
| 93 | |
| 94 | LayoutPoint adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject&, const LayoutPoint&) const; |
| 95 | |
| 96 | LayoutUnit pageLogicalTopForOffset(LayoutUnit) const; |
| 97 | LayoutUnit pageLogicalWidthForOffset(LayoutUnit) const; |
| 98 | LayoutUnit pageLogicalHeightForOffset(LayoutUnit) const; |
| 99 | LayoutUnit pageRemainingLogicalHeightForOffset(LayoutUnit, PageBoundaryRule = IncludePageBoundary) const; |
| 100 | |
| 101 | virtual void setPageBreak(const RenderBlock*, LayoutUnit /*offset*/, LayoutUnit /*spaceShortage*/) { } |
| 102 | virtual void updateMinimumPageHeight(const RenderBlock*, LayoutUnit /*offset*/, LayoutUnit /*minHeight*/) { } |
| 103 | |
| 104 | virtual RenderFragmentContainer* fragmentAtBlockOffset(const RenderBox*, LayoutUnit, bool extendLastFragment = false) const; |
| 105 | |
| 106 | bool fragmentsHaveUniformLogicalWidth() const { return m_fragmentsHaveUniformLogicalWidth; } |
| 107 | bool fragmentsHaveUniformLogicalHeight() const { return m_fragmentsHaveUniformLogicalHeight; } |
| 108 | |
| 109 | virtual RenderFragmentContainer* mapFromFlowToFragment(TransformState&) const; |
| 110 | |
| 111 | void logicalWidthChangedInFragmentsForBlock(const RenderBlock*, bool&); |
| 112 | |
| 113 | LayoutUnit contentLogicalWidthOfFirstFragment() const; |
| 114 | LayoutUnit contentLogicalHeightOfFirstFragment() const; |
| 115 | LayoutUnit contentLogicalLeftOfFirstFragment() const; |
| 116 | |
| 117 | RenderFragmentContainer* firstFragment() const; |
| 118 | RenderFragmentContainer* lastFragment() const; |
| 119 | |
| 120 | virtual void setFragmentRangeForBox(const RenderBox&, RenderFragmentContainer*, RenderFragmentContainer*); |
| 121 | bool getFragmentRangeForBox(const RenderBox*, RenderFragmentContainer*& startFragment, RenderFragmentContainer*& endFragment) const; |
| 122 | bool computedFragmentRangeForBox(const RenderBox*, RenderFragmentContainer*& startFragment, RenderFragmentContainer*& endFragment) const; |
| 123 | bool hasCachedFragmentRangeForBox(const RenderBox&) const; |
| 124 | |
| 125 | // Check if the object is in fragment and the fragment is part of this flow thread. |
| 126 | bool objectInFlowFragment(const RenderObject*, const RenderFragmentContainer*) const; |
| 127 | |
| 128 | // Check if the object should be painted in this fragment and if the fragment is part of this flow thread. |
| 129 | bool objectShouldFragmentInFlowFragment(const RenderObject*, const RenderFragmentContainer*) const; |
| 130 | |
| 131 | void markFragmentsForOverflowLayoutIfNeeded(); |
| 132 | |
| 133 | virtual bool addForcedFragmentBreak(const RenderBlock*, LayoutUnit, RenderBox* breakChild, bool isBefore, LayoutUnit* offsetBreakAdjustment = 0); |
| 134 | virtual void applyBreakAfterContent(LayoutUnit) { } |
| 135 | |
| 136 | virtual bool isPageLogicalHeightKnown() const { return true; } |
| 137 | bool pageLogicalSizeChanged() const { return m_pageLogicalSizeChanged; } |
| 138 | |
| 139 | void collectLayerFragments(LayerFragments&, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect); |
| 140 | LayoutRect fragmentsBoundingBox(const LayoutRect& layerBoundingBox); |
| 141 | |
| 142 | LayoutUnit offsetFromLogicalTopOfFirstFragment(const RenderBlock*) const; |
| 143 | void clearRenderBoxFragmentInfoAndCustomStyle(const RenderBox&, const RenderFragmentContainer*, const RenderFragmentContainer*, const RenderFragmentContainer*, const RenderFragmentContainer*); |
| 144 | |
| 145 | void addFragmentsVisualEffectOverflow(const RenderBox*); |
| 146 | void addFragmentsVisualOverflowFromTheme(const RenderBlock*); |
| 147 | void addFragmentsOverflowFromChild(const RenderBox*, const RenderBox*, const LayoutSize&); |
| 148 | void addFragmentsLayoutOverflow(const RenderBox*, const LayoutRect&); |
| 149 | void addFragmentsVisualOverflow(const RenderBox*, const LayoutRect&); |
| 150 | void clearFragmentsOverflow(const RenderBox*); |
| 151 | |
| 152 | LayoutRect mapFromFragmentedFlowToLocal(const RenderBox*, const LayoutRect&) const; |
| 153 | LayoutRect mapFromLocalToFragmentedFlow(const RenderBox*, const LayoutRect&) const; |
| 154 | |
| 155 | void flipForWritingModeLocalCoordinates(LayoutRect&) const; |
| 156 | |
| 157 | // Used to estimate the maximum height of the flow thread. |
| 158 | static LayoutUnit maxLogicalHeight() { return LayoutUnit::max() / 2; } |
| 159 | |
| 160 | bool fragmentInRange(const RenderFragmentContainer* targetFragment, const RenderFragmentContainer* startFragment, const RenderFragmentContainer* endFragment) const; |
| 161 | |
| 162 | virtual bool absoluteQuadsForBox(Vector<FloatQuad>&, bool*, const RenderBox*, float, float) const { return false; } |
| 163 | |
| 164 | void layout() override; |
| 165 | |
| 166 | void setCurrentFragmentMaintainer(CurrentRenderFragmentContainerMaintainer* currentFragmentMaintainer) { m_currentFragmentMaintainer = currentFragmentMaintainer; } |
| 167 | RenderFragmentContainer* currentFragment() const; |
| 168 | |
| 169 | ContainingFragmentMap& containingFragmentMap(); |
| 170 | |
| 171 | bool cachedEnclosingFragmentedFlowNeedsUpdate() const override { return false; } |
| 172 | |
| 173 | // FIXME: Eventually as column and fragment flow threads start nesting, this may end up changing. |
| 174 | virtual bool shouldCheckColumnBreaks() const { return false; } |
| 175 | |
| 176 | private: |
| 177 | // Always create a RenderLayer for the RenderFragmentedFlow so that we |
| 178 | // can easily avoid drawing the children directly. |
| 179 | bool requiresLayer() const final { return true; } |
| 180 | |
| 181 | protected: |
| 182 | RenderFragmentedFlow(Document&, RenderStyle&&); |
| 183 | |
| 184 | RenderFragmentedFlow* locateEnclosingFragmentedFlow() const override { return const_cast<RenderFragmentedFlow*>(this); } |
| 185 | |
| 186 | const char* renderName() const override = 0; |
| 187 | |
| 188 | // Overridden by columns/pages to set up an initial logical width of the page width even when |
| 189 | // no fragments have been generated yet. |
| 190 | virtual LayoutUnit initialLogicalWidth() const { return 0; }; |
| 191 | |
| 192 | void clearLinesToFragmentMap(); |
| 193 | void willBeDestroyed() override; |
| 194 | |
| 195 | void mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState&, MapCoordinatesFlags, bool* wasFixed) const override; |
| 196 | |
| 197 | void updateFragmentsFragmentedFlowPortionRect(); |
| 198 | bool shouldRepaint(const LayoutRect&) const; |
| 199 | |
| 200 | bool getFragmentRangeForBoxFromCachedInfo(const RenderBox*, RenderFragmentContainer*& startFragment, RenderFragmentContainer*& endFragment) const; |
| 201 | |
| 202 | void removeRenderBoxFragmentInfo(RenderBox&); |
| 203 | void removeLineFragmentInfo(const RenderBlockFlow&); |
| 204 | |
| 205 | RenderFragmentContainerList m_fragmentList; |
| 206 | |
| 207 | class RenderFragmentContainerRange { |
| 208 | public: |
| 209 | RenderFragmentContainerRange() = default; |
| 210 | RenderFragmentContainerRange(RenderFragmentContainer* start, RenderFragmentContainer* end) |
| 211 | { |
| 212 | setRange(start, end); |
| 213 | } |
| 214 | |
| 215 | void setRange(RenderFragmentContainer* start, RenderFragmentContainer* end) |
| 216 | { |
| 217 | m_startFragment = makeWeakPtr(start); |
| 218 | m_endFragment = makeWeakPtr(end); |
| 219 | m_rangeInvalidated = true; |
| 220 | } |
| 221 | |
| 222 | RenderFragmentContainer* startFragment() const { return m_startFragment.get(); } |
| 223 | RenderFragmentContainer* endFragment() const { return m_endFragment.get(); } |
| 224 | bool rangeInvalidated() const { return m_rangeInvalidated; } |
| 225 | void clearRangeInvalidated() { m_rangeInvalidated = false; } |
| 226 | |
| 227 | private: |
| 228 | WeakPtr<RenderFragmentContainer> m_startFragment; |
| 229 | WeakPtr<RenderFragmentContainer> m_endFragment; |
| 230 | bool m_rangeInvalidated; |
| 231 | }; |
| 232 | |
| 233 | typedef PODInterval<LayoutUnit, WeakPtr<RenderFragmentContainer>> FragmentInterval; |
| 234 | typedef PODIntervalTree<LayoutUnit, WeakPtr<RenderFragmentContainer>> FragmentIntervalTree; |
| 235 | |
| 236 | class FragmentSearchAdapter { |
| 237 | public: |
| 238 | FragmentSearchAdapter(LayoutUnit offset) |
| 239 | : m_offset(offset) |
| 240 | { |
| 241 | } |
| 242 | |
| 243 | const LayoutUnit& lowValue() const { return m_offset; } |
| 244 | const LayoutUnit& highValue() const { return m_offset; } |
| 245 | void collectIfNeeded(const FragmentInterval&); |
| 246 | |
| 247 | RenderFragmentContainer* result() const { return m_result.get(); } |
| 248 | |
| 249 | private: |
| 250 | LayoutUnit m_offset; |
| 251 | WeakPtr<RenderFragmentContainer> m_result; |
| 252 | }; |
| 253 | |
| 254 | // Map a line to its containing fragment. |
| 255 | std::unique_ptr<ContainingFragmentMap> m_lineToFragmentMap; |
| 256 | |
| 257 | // Map a box to the list of fragments in which the box is rendered. |
| 258 | typedef HashMap<const RenderBox*, RenderFragmentContainerRange> RenderFragmentContainerRangeMap; |
| 259 | RenderFragmentContainerRangeMap m_fragmentRangeMap; |
| 260 | |
| 261 | // Map a box with a fragment break to the auto height fragment affected by that break. |
| 262 | typedef HashMap<RenderBox*, RenderFragmentContainer*> RenderBoxToFragmentMap; |
| 263 | RenderBoxToFragmentMap m_breakBeforeToFragmentMap; |
| 264 | RenderBoxToFragmentMap m_breakAfterToFragmentMap; |
| 265 | |
| 266 | FragmentIntervalTree m_fragmentIntervalTree; |
| 267 | |
| 268 | CurrentRenderFragmentContainerMaintainer* m_currentFragmentMaintainer; |
| 269 | |
| 270 | bool m_fragmentsInvalidated : 1; |
| 271 | bool m_fragmentsHaveUniformLogicalWidth : 1; |
| 272 | bool m_fragmentsHaveUniformLogicalHeight : 1; |
| 273 | bool m_pageLogicalSizeChanged : 1; |
| 274 | }; |
| 275 | |
| 276 | } // namespace WebCore |
| 277 | |
| 278 | #ifndef NDEBUG |
| 279 | |
| 280 | namespace WTF { |
| 281 | |
| 282 | // This structure is used by PODIntervalTree for debugging. |
| 283 | template <> struct ValueToString<WebCore::RenderFragmentContainer*> { |
| 284 | static String string(const WebCore::RenderFragmentContainer* value) { return value->debugString(); } |
| 285 | }; |
| 286 | template <> struct ValueToString<WeakPtr<WebCore::RenderFragmentContainer>> { |
| 287 | static String string(const WeakPtr<WebCore::RenderFragmentContainer>& value) { return value ? value->debugString() : String { }; } |
| 288 | }; |
| 289 | |
| 290 | } // namespace WTF |
| 291 | |
| 292 | #endif |
| 293 | |
| 294 | SPECIALIZE_TYPE_TRAITS_RENDER_OBJECT(RenderFragmentedFlow, isRenderFragmentedFlow()) |
| 295 | |