| 1 | /* |
| 2 | * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 | * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| 4 | * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) |
| 5 | * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) |
| 6 | * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
| 7 | * Copyright (C) 2010, 2012 Google Inc. All rights reserved. |
| 8 | * |
| 9 | * This library is free software; you can redistribute it and/or |
| 10 | * modify it under the terms of the GNU Library General Public |
| 11 | * License as published by the Free Software Foundation; either |
| 12 | * version 2 of the License, or (at your option) any later version. |
| 13 | * |
| 14 | * This library is distributed in the hope that it will be useful, |
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 17 | * Library General Public License for more details. |
| 18 | * |
| 19 | * You should have received a copy of the GNU Library General Public License |
| 20 | * along with this library; see the file COPYING.LIB. If not, write to |
| 21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 22 | * Boston, MA 02110-1301, USA. |
| 23 | */ |
| 24 | |
| 25 | #include "config.h" |
| 26 | #include "RenderLayerModelObject.h" |
| 27 | |
| 28 | #include "RenderLayer.h" |
| 29 | #include "RenderLayerBacking.h" |
| 30 | #include "RenderLayerCompositor.h" |
| 31 | #include "RenderView.h" |
| 32 | #include "Settings.h" |
| 33 | #include "StyleScrollSnapPoints.h" |
| 34 | #include <wtf/IsoMallocInlines.h> |
| 35 | |
| 36 | namespace WebCore { |
| 37 | |
| 38 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderLayerModelObject); |
| 39 | |
| 40 | bool RenderLayerModelObject::s_wasFloating = false; |
| 41 | bool RenderLayerModelObject::s_hadLayer = false; |
| 42 | bool RenderLayerModelObject::s_hadTransform = false; |
| 43 | bool RenderLayerModelObject::s_layerWasSelfPainting = false; |
| 44 | |
| 45 | typedef WTF::HashMap<const RenderLayerModelObject*, RepaintLayoutRects> RepaintLayoutRectsMap; |
| 46 | static RepaintLayoutRectsMap* gRepaintLayoutRectsMap = nullptr; |
| 47 | |
| 48 | RepaintLayoutRects::RepaintLayoutRects(const RenderLayerModelObject& renderer, const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap) |
| 49 | : m_repaintRect(renderer.clippedOverflowRectForRepaint(repaintContainer)) |
| 50 | , m_outlineBox(renderer.outlineBoundsForRepaint(repaintContainer, geometryMap)) |
| 51 | { |
| 52 | } |
| 53 | |
| 54 | RenderLayerModelObject::RenderLayerModelObject(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags) |
| 55 | : RenderElement(element, WTFMove(style), baseTypeFlags | RenderLayerModelObjectFlag) |
| 56 | { |
| 57 | } |
| 58 | |
| 59 | RenderLayerModelObject::RenderLayerModelObject(Document& document, RenderStyle&& style, BaseTypeFlags baseTypeFlags) |
| 60 | : RenderElement(document, WTFMove(style), baseTypeFlags | RenderLayerModelObjectFlag) |
| 61 | { |
| 62 | } |
| 63 | |
| 64 | RenderLayerModelObject::~RenderLayerModelObject() |
| 65 | { |
| 66 | // Do not add any code here. Add it to willBeDestroyed() instead. |
| 67 | } |
| 68 | |
| 69 | void RenderLayerModelObject::willBeDestroyed() |
| 70 | { |
| 71 | if (isPositioned()) { |
| 72 | if (style().hasViewportConstrainedPosition()) |
| 73 | view().frameView().removeViewportConstrainedObject(this); |
| 74 | } |
| 75 | |
| 76 | if (hasLayer()) { |
| 77 | setHasLayer(false); |
| 78 | destroyLayer(); |
| 79 | } |
| 80 | |
| 81 | RenderElement::willBeDestroyed(); |
| 82 | |
| 83 | clearRepaintLayoutRects(); |
| 84 | } |
| 85 | |
| 86 | void RenderLayerModelObject::destroyLayer() |
| 87 | { |
| 88 | ASSERT(!hasLayer()); |
| 89 | ASSERT(m_layer); |
| 90 | if (m_layer->isSelfPaintingLayer()) |
| 91 | clearRepaintLayoutRects(); |
| 92 | m_layer = nullptr; |
| 93 | } |
| 94 | |
| 95 | void RenderLayerModelObject::createLayer() |
| 96 | { |
| 97 | ASSERT(!m_layer); |
| 98 | m_layer = std::make_unique<RenderLayer>(*this); |
| 99 | setHasLayer(true); |
| 100 | m_layer->insertOnlyThisLayer(); |
| 101 | } |
| 102 | |
| 103 | bool RenderLayerModelObject::hasSelfPaintingLayer() const |
| 104 | { |
| 105 | return m_layer && m_layer->isSelfPaintingLayer(); |
| 106 | } |
| 107 | |
| 108 | void RenderLayerModelObject::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) |
| 109 | { |
| 110 | s_wasFloating = isFloating(); |
| 111 | s_hadLayer = hasLayer(); |
| 112 | s_hadTransform = hasTransform(); |
| 113 | if (s_hadLayer) |
| 114 | s_layerWasSelfPainting = layer()->isSelfPaintingLayer(); |
| 115 | |
| 116 | // If our z-index changes value or our visibility changes, |
| 117 | // we need to dirty our stacking context's z-order list. |
| 118 | const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr; |
| 119 | if (oldStyle) { |
| 120 | if (parent()) { |
| 121 | // Do a repaint with the old style first, e.g., for example if we go from |
| 122 | // having an outline to not having an outline. |
| 123 | if (diff == StyleDifference::RepaintLayer) { |
| 124 | layer()->repaintIncludingDescendants(); |
| 125 | if (!(oldStyle->clip() == newStyle.clip())) |
| 126 | layer()->clearClipRectsIncludingDescendants(); |
| 127 | } else if (diff == StyleDifference::Repaint || newStyle.outlineSize() < oldStyle->outlineSize()) |
| 128 | repaint(); |
| 129 | } |
| 130 | |
| 131 | if (diff == StyleDifference::Layout || diff == StyleDifference::SimplifiedLayout) { |
| 132 | // When a layout hint happens, we do a repaint of the layer, since the layer could end up being destroyed. |
| 133 | if (hasLayer()) { |
| 134 | if (oldStyle->position() != newStyle.position() |
| 135 | || oldStyle->zIndex() != newStyle.zIndex() |
| 136 | || oldStyle->hasAutoZIndex() != newStyle.hasAutoZIndex() |
| 137 | || !(oldStyle->clip() == newStyle.clip()) |
| 138 | || oldStyle->hasClip() != newStyle.hasClip() |
| 139 | || oldStyle->opacity() != newStyle.opacity() |
| 140 | || oldStyle->transform() != newStyle.transform() |
| 141 | || oldStyle->filter() != newStyle.filter() |
| 142 | ) |
| 143 | layer()->repaintIncludingDescendants(); |
| 144 | } else if (newStyle.hasTransform() || newStyle.opacity() < 1 || newStyle.hasFilter() || newStyle.hasBackdropFilter()) { |
| 145 | // If we don't have a layer yet, but we are going to get one because of transform or opacity, |
| 146 | // then we need to repaint the old position of the object. |
| 147 | repaint(); |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | RenderElement::styleWillChange(diff, newStyle); |
| 153 | } |
| 154 | |
| 155 | #if ENABLE(CSS_SCROLL_SNAP) |
| 156 | static bool scrollSnapContainerRequiresUpdateForStyleUpdate(const RenderStyle& oldStyle, const RenderStyle& newStyle) |
| 157 | { |
| 158 | return oldStyle.scrollSnapPort() != newStyle.scrollSnapPort(); |
| 159 | } |
| 160 | #endif |
| 161 | |
| 162 | void RenderLayerModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) |
| 163 | { |
| 164 | RenderElement::styleDidChange(diff, oldStyle); |
| 165 | updateFromStyle(); |
| 166 | |
| 167 | if (requiresLayer()) { |
| 168 | if (!layer() && layerCreationAllowedForSubtree()) { |
| 169 | if (s_wasFloating && isFloating()) |
| 170 | setChildNeedsLayout(); |
| 171 | createLayer(); |
| 172 | if (parent() && !needsLayout() && containingBlock()) { |
| 173 | layer()->setRepaintStatus(NeedsFullRepaint); |
| 174 | layer()->updateLayerPositions(); |
| 175 | } |
| 176 | } |
| 177 | } else if (layer() && layer()->parent()) { |
| 178 | #if ENABLE(CSS_COMPOSITING) |
| 179 | if (oldStyle->hasBlendMode()) |
| 180 | layer()->parent()->dirtyAncestorChainHasBlendingDescendants(); |
| 181 | #endif |
| 182 | setHasTransformRelatedProperty(false); // All transform-related propeties force layers, so we know we don't have one or the object doesn't support them. |
| 183 | setHasReflection(false); |
| 184 | // Repaint the about to be destroyed self-painting layer when style change also triggers repaint. |
| 185 | if (layer()->isSelfPaintingLayer() && layer()->repaintStatus() == NeedsFullRepaint && hasRepaintLayoutRects()) |
| 186 | repaintUsingContainer(containerForRepaint(), repaintLayoutRects().m_repaintRect); |
| 187 | layer()->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer |
| 188 | if (s_wasFloating && isFloating()) |
| 189 | setChildNeedsLayout(); |
| 190 | if (s_hadTransform) |
| 191 | setNeedsLayoutAndPrefWidthsRecalc(); |
| 192 | } |
| 193 | |
| 194 | if (layer()) { |
| 195 | layer()->styleChanged(diff, oldStyle); |
| 196 | if (s_hadLayer && layer()->isSelfPaintingLayer() != s_layerWasSelfPainting) |
| 197 | setChildNeedsLayout(); |
| 198 | } |
| 199 | |
| 200 | bool newStyleIsViewportConstrained = style().hasViewportConstrainedPosition(); |
| 201 | bool oldStyleIsViewportConstrained = oldStyle && oldStyle->hasViewportConstrainedPosition(); |
| 202 | if (newStyleIsViewportConstrained != oldStyleIsViewportConstrained) { |
| 203 | if (newStyleIsViewportConstrained && layer()) |
| 204 | view().frameView().addViewportConstrainedObject(this); |
| 205 | else |
| 206 | view().frameView().removeViewportConstrainedObject(this); |
| 207 | } |
| 208 | |
| 209 | #if ENABLE(CSS_SCROLL_SNAP) |
| 210 | const RenderStyle& newStyle = style(); |
| 211 | if (oldStyle && scrollSnapContainerRequiresUpdateForStyleUpdate(*oldStyle, newStyle)) { |
| 212 | if (RenderLayer* renderLayer = layer()) { |
| 213 | renderLayer->updateSnapOffsets(); |
| 214 | renderLayer->updateScrollSnapState(); |
| 215 | } else if (isBody() || isDocumentElementRenderer()) { |
| 216 | FrameView& frameView = view().frameView(); |
| 217 | frameView.updateSnapOffsets(); |
| 218 | frameView.updateScrollSnapState(); |
| 219 | frameView.updateScrollingCoordinatorScrollSnapProperties(); |
| 220 | } |
| 221 | } |
| 222 | if (oldStyle && oldStyle->scrollSnapArea() != newStyle.scrollSnapArea()) { |
| 223 | auto* scrollSnapBox = enclosingScrollableContainerForSnapping(); |
| 224 | if (scrollSnapBox && scrollSnapBox->layer()) { |
| 225 | const RenderStyle& style = scrollSnapBox->style(); |
| 226 | if (style.scrollSnapType().strictness != ScrollSnapStrictness::None) { |
| 227 | scrollSnapBox->layer()->updateSnapOffsets(); |
| 228 | scrollSnapBox->layer()->updateScrollSnapState(); |
| 229 | if (scrollSnapBox->isBody() || scrollSnapBox->isDocumentElementRenderer()) |
| 230 | scrollSnapBox->view().frameView().updateScrollingCoordinatorScrollSnapProperties(); |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | #endif |
| 235 | } |
| 236 | |
| 237 | bool RenderLayerModelObject::shouldPlaceBlockDirectionScrollbarOnLeft() const |
| 238 | { |
| 239 | // RTL Scrollbars require some system support, and this system support does not exist on certain versions of OS X. iOS uses a separate mechanism. |
| 240 | #if PLATFORM(IOS_FAMILY) |
| 241 | return false; |
| 242 | #else |
| 243 | switch (settings().userInterfaceDirectionPolicy()) { |
| 244 | case UserInterfaceDirectionPolicy::Content: |
| 245 | return style().shouldPlaceBlockDirectionScrollbarOnLeft(); |
| 246 | case UserInterfaceDirectionPolicy::System: |
| 247 | return settings().systemLayoutDirection() == TextDirection::RTL; |
| 248 | } |
| 249 | ASSERT_NOT_REACHED(); |
| 250 | return style().shouldPlaceBlockDirectionScrollbarOnLeft(); |
| 251 | #endif |
| 252 | } |
| 253 | |
| 254 | bool RenderLayerModelObject::hasRepaintLayoutRects() const |
| 255 | { |
| 256 | return gRepaintLayoutRectsMap && gRepaintLayoutRectsMap->contains(this); |
| 257 | } |
| 258 | |
| 259 | void RenderLayerModelObject::setRepaintLayoutRects(const RepaintLayoutRects& rects) |
| 260 | { |
| 261 | if (!gRepaintLayoutRectsMap) |
| 262 | gRepaintLayoutRectsMap = new RepaintLayoutRectsMap(); |
| 263 | gRepaintLayoutRectsMap->set(this, rects); |
| 264 | } |
| 265 | |
| 266 | void RenderLayerModelObject::clearRepaintLayoutRects() |
| 267 | { |
| 268 | if (gRepaintLayoutRectsMap) |
| 269 | gRepaintLayoutRectsMap->remove(this); |
| 270 | } |
| 271 | |
| 272 | RepaintLayoutRects RenderLayerModelObject::repaintLayoutRects() const |
| 273 | { |
| 274 | if (!hasRepaintLayoutRects()) |
| 275 | return RepaintLayoutRects(); |
| 276 | return gRepaintLayoutRectsMap->get(this); |
| 277 | } |
| 278 | |
| 279 | void RenderLayerModelObject::computeRepaintLayoutRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap) |
| 280 | { |
| 281 | if (!m_layer || !m_layer->isSelfPaintingLayer()) |
| 282 | clearRepaintLayoutRects(); |
| 283 | else |
| 284 | setRepaintLayoutRects(RepaintLayoutRects(*this, repaintContainer, geometryMap)); |
| 285 | } |
| 286 | |
| 287 | bool RenderLayerModelObject::startTransition(double timeOffset, CSSPropertyID propertyId, const RenderStyle* fromStyle, const RenderStyle* toStyle) |
| 288 | { |
| 289 | if (!layer() || !layer()->backing()) |
| 290 | return false; |
| 291 | return layer()->backing()->startTransition(timeOffset, propertyId, fromStyle, toStyle); |
| 292 | } |
| 293 | |
| 294 | void RenderLayerModelObject::transitionPaused(double timeOffset, CSSPropertyID propertyId) |
| 295 | { |
| 296 | if (!layer() || !layer()->backing()) |
| 297 | return; |
| 298 | layer()->backing()->transitionPaused(timeOffset, propertyId); |
| 299 | } |
| 300 | |
| 301 | void RenderLayerModelObject::transitionFinished(CSSPropertyID propertyId) |
| 302 | { |
| 303 | if (!layer() || !layer()->backing()) |
| 304 | return; |
| 305 | layer()->backing()->transitionFinished(propertyId); |
| 306 | } |
| 307 | |
| 308 | bool RenderLayerModelObject::startAnimation(double timeOffset, const Animation& animation, const KeyframeList& keyframes) |
| 309 | { |
| 310 | if (!layer() || !layer()->backing()) |
| 311 | return false; |
| 312 | return layer()->backing()->startAnimation(timeOffset, animation, keyframes); |
| 313 | } |
| 314 | |
| 315 | void RenderLayerModelObject::animationPaused(double timeOffset, const String& name) |
| 316 | { |
| 317 | if (!layer() || !layer()->backing()) |
| 318 | return; |
| 319 | layer()->backing()->animationPaused(timeOffset, name); |
| 320 | } |
| 321 | |
| 322 | void RenderLayerModelObject::animationSeeked(double timeOffset, const String& name) |
| 323 | { |
| 324 | if (!layer() || !layer()->backing()) |
| 325 | return; |
| 326 | layer()->backing()->animationSeeked(timeOffset, name); |
| 327 | } |
| 328 | |
| 329 | void RenderLayerModelObject::animationFinished(const String& name) |
| 330 | { |
| 331 | if (!layer() || !layer()->backing()) |
| 332 | return; |
| 333 | layer()->backing()->animationFinished(name); |
| 334 | } |
| 335 | |
| 336 | void RenderLayerModelObject::suspendAnimations(MonotonicTime time) |
| 337 | { |
| 338 | if (!layer() || !layer()->backing()) |
| 339 | return; |
| 340 | layer()->backing()->suspendAnimations(time); |
| 341 | } |
| 342 | |
| 343 | } // namespace WebCore |
| 344 | |
| 345 | |