OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/paint/PaintPropertyTreeBuilder.h" | 5 #include "core/paint/PaintPropertyTreeBuilder.h" |
6 | 6 |
7 #include "core/frame/FrameView.h" | 7 #include "core/frame/FrameView.h" |
8 #include "core/frame/LocalFrame.h" | 8 #include "core/frame/LocalFrame.h" |
9 #include "core/frame/Settings.h" | 9 #include "core/frame/Settings.h" |
10 #include "core/layout/LayoutInline.h" | 10 #include "core/layout/LayoutInline.h" |
11 #include "core/layout/LayoutPart.h" | 11 #include "core/layout/LayoutPart.h" |
12 #include "core/layout/svg/LayoutSVGRoot.h" | 12 #include "core/layout/svg/LayoutSVGRoot.h" |
13 #include "core/paint/ObjectPaintProperties.h" | 13 #include "core/paint/ObjectPaintProperties.h" |
14 #include "core/paint/PaintLayer.h" | 14 #include "core/paint/PaintLayer.h" |
15 #include "core/paint/SVGRootPainter.h" | 15 #include "core/paint/SVGRootPainter.h" |
16 #include "platform/transforms/TransformationMatrix.h" | 16 #include "platform/transforms/TransformationMatrix.h" |
17 #include "wtf/PtrUtil.h" | 17 #include "wtf/PtrUtil.h" |
18 #include <memory> | 18 #include <memory> |
19 | 19 |
20 namespace blink { | 20 namespace blink { |
21 | 21 |
22 void PaintPropertyTreeBuilder::buildTreeRootNodes(FrameView& rootFrame, PaintPro
pertyTreeBuilderContext& context) | 22 void PaintPropertyTreeBuilder::buildTreeRootNodes(FrameView& rootFrame, PaintPro
pertyTreeBuilderContext& context) |
23 { | 23 { |
| 24 Settings* settings = rootFrame.frame().settings(); |
| 25 if (settings && settings->rootLayerScrolls()) |
| 26 return; |
| 27 |
24 if (!rootFrame.rootTransform() || rootFrame.rootTransform()->parent()) { | 28 if (!rootFrame.rootTransform() || rootFrame.rootTransform()->parent()) { |
25 rootFrame.setRootTransform(TransformPaintPropertyNode::create(nullptr, T
ransformationMatrix(), FloatPoint3D())); | 29 rootFrame.setRootTransform(TransformPaintPropertyNode::create(nullptr, T
ransformationMatrix(), FloatPoint3D())); |
26 rootFrame.setRootClip(ClipPaintPropertyNode::create(nullptr, rootFrame.r
ootTransform(), FloatRoundedRect(LayoutRect::infiniteIntRect()))); | 30 rootFrame.setRootClip(ClipPaintPropertyNode::create(nullptr, rootFrame.r
ootTransform(), FloatRoundedRect(LayoutRect::infiniteIntRect()))); |
27 rootFrame.setRootEffect(EffectPaintPropertyNode::create(nullptr, 1.0)); | 31 rootFrame.setRootEffect(EffectPaintPropertyNode::create(nullptr, 1.0)); |
28 } else { | 32 } else { |
29 DCHECK(rootFrame.rootClip() && !rootFrame.rootClip()->parent()); | 33 DCHECK(rootFrame.rootClip() && !rootFrame.rootClip()->parent()); |
30 DCHECK(rootFrame.rootEffect() && !rootFrame.rootEffect()->parent()); | 34 DCHECK(rootFrame.rootEffect() && !rootFrame.rootEffect()->parent()); |
31 } | 35 } |
32 | 36 |
33 context.current.transform = context.absolutePosition.transform = context.fix
edPosition.transform = rootFrame.rootTransform(); | 37 context.current.transform = context.absolutePosition.transform = context.fix
edPosition.transform = rootFrame.rootTransform(); |
34 context.current.clip = context.absolutePosition.clip = context.fixedPosition
.clip = rootFrame.rootClip(); | 38 context.current.clip = context.absolutePosition.clip = context.fixedPosition
.clip = rootFrame.rootClip(); |
35 context.currentEffect = rootFrame.rootEffect(); | 39 context.currentEffect = rootFrame.rootEffect(); |
36 } | 40 } |
37 | 41 |
38 void PaintPropertyTreeBuilder::buildTreeNodes(FrameView& frameView, PaintPropert
yTreeBuilderContext& context) | 42 void PaintPropertyTreeBuilder::buildTreeNodes(FrameView& frameView, PaintPropert
yTreeBuilderContext& context) |
39 { | 43 { |
40 // TODO(pdr): Creating paint properties for FrameView here will not be | 44 Settings* settings = frameView.frame().settings(); |
41 // needed once settings()->rootLayerScrolls() is enabled. | 45 if (settings && settings->rootLayerScrolls()) { |
42 // TODO(pdr): Make this conditional on the rootLayerScrolls setting. | 46 LayoutView* layoutView = frameView.layoutView(); |
| 47 if (!layoutView) |
| 48 return; |
| 49 |
| 50 TransformationMatrix frameTranslate; |
| 51 frameTranslate.translate( |
| 52 frameView.x() + layoutView->location().x() + context.current.paintOf
fset.x(), |
| 53 frameView.y() + layoutView->location().y() + context.current.paintOf
fset.y()); |
| 54 context.current.transform = layoutView->getMutableForPainting().ensureOb
jectPaintProperties().createOrUpdatePaintOffsetTranslation( |
| 55 context.current.transform, frameTranslate, FloatPoint3D()); |
| 56 context.current.paintOffset = LayoutPoint(); |
| 57 context.current.renderingContextID = 0; |
| 58 context.current.shouldFlattenInheritedTransform = true; |
| 59 context.absolutePosition = context.current; |
| 60 context.containerForAbsolutePosition = nullptr; // This will get set in
updateOutOfFlowContext(). |
| 61 context.fixedPosition = context.current; |
| 62 return; |
| 63 } |
43 | 64 |
44 TransformationMatrix frameTranslate; | 65 TransformationMatrix frameTranslate; |
45 frameTranslate.translate(frameView.x() + context.current.paintOffset.x(), fr
ameView.y() + context.current.paintOffset.y()); | 66 frameTranslate.translate(frameView.x() + context.current.paintOffset.x(), fr
ameView.y() + context.current.paintOffset.y()); |
46 if (TransformPaintPropertyNode* existingPreTranslation = frameView.preTransl
ation()) | 67 if (TransformPaintPropertyNode* existingPreTranslation = frameView.preTransl
ation()) |
47 existingPreTranslation->update(context.current.transform, frameTranslate
, FloatPoint3D()); | 68 existingPreTranslation->update(context.current.transform, frameTranslate
, FloatPoint3D()); |
48 else | 69 else |
49 frameView.setPreTranslation(TransformPaintPropertyNode::create(context.c
urrent.transform, frameTranslate, FloatPoint3D())); | 70 frameView.setPreTranslation(TransformPaintPropertyNode::create(context.c
urrent.transform, frameTranslate, FloatPoint3D())); |
50 | 71 |
51 FloatRoundedRect contentClip(IntRect(IntPoint(), frameView.visibleContentSiz
e())); | 72 FloatRoundedRect contentClip(IntRect(IntPoint(), frameView.visibleContentSiz
e())); |
52 if (ClipPaintPropertyNode* existingContentClip = frameView.contentClip()) | 73 if (ClipPaintPropertyNode* existingContentClip = frameView.contentClip()) |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
91 LayoutPoint fractionalPaintOffset = LayoutPoint(context.current.pain
tOffset - roundedPaintOffset); | 112 LayoutPoint fractionalPaintOffset = LayoutPoint(context.current.pain
tOffset - roundedPaintOffset); |
92 | 113 |
93 context.current.transform = object.getMutableForPainting().ensureObj
ectPaintProperties().createOrUpdatePaintOffsetTranslation( | 114 context.current.transform = object.getMutableForPainting().ensureObj
ectPaintProperties().createOrUpdatePaintOffsetTranslation( |
94 context.current.transform, TransformationMatrix().translate(roun
dedPaintOffset.x(), roundedPaintOffset.y()), FloatPoint3D(), | 115 context.current.transform, TransformationMatrix().translate(roun
dedPaintOffset.x(), roundedPaintOffset.y()), FloatPoint3D(), |
95 context.current.shouldFlattenInheritedTransform, context.current
.renderingContextID); | 116 context.current.shouldFlattenInheritedTransform, context.current
.renderingContextID); |
96 context.current.paintOffset = fractionalPaintOffset; | 117 context.current.paintOffset = fractionalPaintOffset; |
97 return; | 118 return; |
98 } | 119 } |
99 } | 120 } |
100 | 121 |
| 122 if (object.isLayoutView()) |
| 123 return; |
| 124 |
101 if (ObjectPaintProperties* properties = object.getMutableForPainting().objec
tPaintProperties()) | 125 if (ObjectPaintProperties* properties = object.getMutableForPainting().objec
tPaintProperties()) |
102 properties->clearPaintOffsetTranslation(); | 126 properties->clearPaintOffsetTranslation(); |
103 } | 127 } |
104 | 128 |
105 static FloatPoint3D transformOrigin(const LayoutBox& box) | 129 static FloatPoint3D transformOrigin(const LayoutBox& box) |
106 { | 130 { |
107 const ComputedStyle& style = box.styleRef(); | 131 const ComputedStyle& style = box.styleRef(); |
108 FloatSize borderBoxSize(box.size()); | 132 FloatSize borderBoxSize(box.size()); |
109 return FloatPoint3D( | 133 return FloatPoint3D( |
110 floatValueForLength(style.transformOriginX(), borderBoxSize.width()), | 134 floatValueForLength(style.transformOriginX(), borderBoxSize.width()), |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 return; | 186 return; |
163 } | 187 } |
164 } | 188 } |
165 | 189 |
166 if (ObjectPaintProperties* properties = object.getMutableForPainting().objec
tPaintProperties()) | 190 if (ObjectPaintProperties* properties = object.getMutableForPainting().objec
tPaintProperties()) |
167 properties->clearTransform(); | 191 properties->clearTransform(); |
168 } | 192 } |
169 | 193 |
170 void PaintPropertyTreeBuilder::updateEffect(const LayoutObject& object, PaintPro
pertyTreeBuilderContext& context) | 194 void PaintPropertyTreeBuilder::updateEffect(const LayoutObject& object, PaintPro
pertyTreeBuilderContext& context) |
171 { | 195 { |
| 196 if (object.isLayoutView() && !context.currentEffect) { |
| 197 const LayoutView& layoutView = toLayoutView(object); |
| 198 DCHECK(layoutView.frameView()->frame().settings()->rootLayerScrolls()); |
| 199 DCHECK(layoutView.frameView()->frame().isMainFrame()); |
| 200 context.currentEffect = layoutView.getMutableForPainting().ensureObjectP
aintProperties().createOrUpdateEffect(nullptr, 1.0); |
| 201 return; |
| 202 } |
| 203 |
172 if (!object.styleRef().hasOpacity()) { | 204 if (!object.styleRef().hasOpacity()) { |
173 if (ObjectPaintProperties* properties = object.getMutableForPainting().o
bjectPaintProperties()) | 205 if (ObjectPaintProperties* properties = object.getMutableForPainting().o
bjectPaintProperties()) |
174 properties->clearEffect(); | 206 properties->clearEffect(); |
175 return; | 207 return; |
176 } | 208 } |
177 | 209 |
178 context.currentEffect = object.getMutableForPainting().ensureObjectPaintProp
erties().createOrUpdateEffect( | 210 context.currentEffect = object.getMutableForPainting().ensureObjectPaintProp
erties().createOrUpdateEffect( |
179 context.currentEffect, object.styleRef().opacity()); | 211 context.currentEffect, object.styleRef().opacity()); |
180 } | 212 } |
181 | 213 |
182 void PaintPropertyTreeBuilder::updateCssClip(const LayoutObject& object, PaintPr
opertyTreeBuilderContext& context) | 214 void PaintPropertyTreeBuilder::updateCssClip(const LayoutObject& object, PaintPr
opertyTreeBuilderContext& context) |
183 { | 215 { |
184 if (object.hasClip()) { | 216 if (object.hasClip()) { |
185 // Create clip node for descendants that are not fixed position. | 217 // Create clip node for descendants that are not fixed position. |
186 // We don't have to setup context.absolutePosition.clip here because thi
s object must be | 218 // We don't have to setup context.absolutePosition.clip here because thi
s object must be |
187 // a container for absolute position descendants, and will copy from in-
flow context later | 219 // a container for absolute position descendants, and will copy from in-
flow context later |
188 // at updateOutOfFlowContext() step. | 220 // at updateOutOfFlowContext() step. |
189 DCHECK(object.canContainAbsolutePositionObjects()); | 221 DCHECK(object.canContainAbsolutePositionObjects()); |
190 LayoutRect clipRect = toLayoutBox(object).clipRect(context.current.paint
Offset); | 222 LayoutRect clipRect = toLayoutBox(object).clipRect(context.current.paint
Offset); |
191 context.current.clip = object.getMutableForPainting().ensureObjectPaintP
roperties().createOrUpdateCssClip( | 223 context.current.clip = object.getMutableForPainting().ensureObjectPaintP
roperties().createOrUpdateCssClip( |
192 context.current.clip, context.current.transform, FloatRoundedRect(Fl
oatRect(clipRect))); | 224 context.current.clip, context.current.transform, FloatRoundedRect(Fl
oatRect(clipRect))); |
193 return; | 225 return; |
194 } | 226 } |
195 | 227 |
196 if (ObjectPaintProperties* properties = object.getMutableForPainting().objec
tPaintProperties()) | 228 if (ObjectPaintProperties* properties = object.getMutableForPainting().objec
tPaintProperties()) |
197 properties->clearCssClip(); | 229 properties->clearCssClip(); |
198 } | 230 } |
199 | 231 |
200 void PaintPropertyTreeBuilder::updateLocalBorderBoxContext(const LayoutObject& o
bject, const PaintPropertyTreeBuilderContext& context) | 232 void PaintPropertyTreeBuilder::updateLocalBorderBoxContext(const LayoutObject& o
bject, PaintPropertyTreeBuilderContext& context) |
201 { | 233 { |
202 // Avoid adding an ObjectPaintProperties for non-boxes to save memory, since
we don't need them at the moment. | 234 // Avoid adding an ObjectPaintProperties for non-boxes to save memory, since
we don't need them at the moment. |
203 if (!object.isBox() && !object.hasLayer()) | 235 if (!object.isBox() && !object.hasLayer()) |
204 return; | 236 return; |
205 | 237 |
206 std::unique_ptr<ObjectPaintProperties::LocalBorderBoxProperties> borderBoxCo
ntext = | 238 std::unique_ptr<ObjectPaintProperties::LocalBorderBoxProperties> borderBoxCo
ntext = |
207 wrapUnique(new ObjectPaintProperties::LocalBorderBoxProperties); | 239 wrapUnique(new ObjectPaintProperties::LocalBorderBoxProperties); |
208 borderBoxContext->paintOffset = context.current.paintOffset; | 240 borderBoxContext->paintOffset = context.current.paintOffset; |
209 borderBoxContext->propertyTreeState = PropertyTreeState(context.current.tran
sform, context.current.clip, context.currentEffect); | 241 borderBoxContext->propertyTreeState = PropertyTreeState(context.current.tran
sform, context.current.clip, context.currentEffect); |
| 242 |
| 243 if (!context.current.clip) { |
| 244 DCHECK(object.isLayoutView()); |
| 245 DCHECK(toLayoutView(object).frameView()->frame().isMainFrame()); |
| 246 DCHECK(toLayoutView(object).frameView()->frame().settings()->rootLayerSc
rolls()); |
| 247 borderBoxContext->propertyTreeState.clip = ClipPaintPropertyNode::create
(nullptr, context.current.transform, FloatRoundedRect(LayoutRect::infiniteIntRec
t())); |
| 248 context.current.clip = borderBoxContext->propertyTreeState.clip.get(); |
| 249 } |
| 250 |
210 object.getMutableForPainting().ensureObjectPaintProperties().setLocalBorderB
oxProperties(std::move(borderBoxContext)); | 251 object.getMutableForPainting().ensureObjectPaintProperties().setLocalBorderB
oxProperties(std::move(borderBoxContext)); |
| 252 |
211 } | 253 } |
212 | 254 |
213 // TODO(trchen): Remove this once we bake the paint offset into frameRect. | 255 // TODO(trchen): Remove this once we bake the paint offset into frameRect. |
214 void PaintPropertyTreeBuilder::updateScrollbarPaintOffset(const LayoutObject& ob
ject, const PaintPropertyTreeBuilderContext& context) | 256 void PaintPropertyTreeBuilder::updateScrollbarPaintOffset(const LayoutObject& ob
ject, const PaintPropertyTreeBuilderContext& context) |
215 { | 257 { |
216 IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset); | 258 IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset); |
217 if (roundedPaintOffset != IntPoint() && object.isBoxModelObject()) { | 259 if (roundedPaintOffset != IntPoint() && object.isBoxModelObject()) { |
218 if (PaintLayerScrollableArea* scrollableArea = toLayoutBoxModelObject(ob
ject).getScrollableArea()) { | 260 if (PaintLayerScrollableArea* scrollableArea = toLayoutBoxModelObject(ob
ject).getScrollableArea()) { |
219 if (scrollableArea->horizontalScrollbar() || scrollableArea->vertica
lScrollbar()) { | 261 if (scrollableArea->horizontalScrollbar() || scrollableArea->vertica
lScrollbar()) { |
220 auto paintOffset = TransformationMatrix().translate(roundedPaint
Offset.x(), roundedPaintOffset.y()); | 262 auto paintOffset = TransformationMatrix().translate(roundedPaint
Offset.x(), roundedPaintOffset.y()); |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 context.current.shouldFlattenInheritedTransform = false; | 355 context.current.shouldFlattenInheritedTransform = false; |
314 context.current.renderingContextID = 0; | 356 context.current.renderingContextID = 0; |
315 } | 357 } |
316 | 358 |
317 void PaintPropertyTreeBuilder::updateScrollTranslation(const LayoutObject& objec
t, PaintPropertyTreeBuilderContext& context) | 359 void PaintPropertyTreeBuilder::updateScrollTranslation(const LayoutObject& objec
t, PaintPropertyTreeBuilderContext& context) |
318 { | 360 { |
319 if (object.isBoxModelObject() && object.hasOverflowClip()) { | 361 if (object.isBoxModelObject() && object.hasOverflowClip()) { |
320 PaintLayer* layer = toLayoutBoxModelObject(object).layer(); | 362 PaintLayer* layer = toLayoutBoxModelObject(object).layer(); |
321 DCHECK(layer); | 363 DCHECK(layer); |
322 DoubleSize scrollOffset = layer->getScrollableArea()->scrollOffset(); | 364 DoubleSize scrollOffset = layer->getScrollableArea()->scrollOffset(); |
323 | 365 bool forceScrollingForLayoutView = false; |
324 if (!scrollOffset.isZero() || layer->scrollsOverflow()) { | 366 if (object.isLayoutView()) { |
| 367 Settings* settings = object.document().settings(); |
| 368 forceScrollingForLayoutView = (settings && settings->rootLayerScroll
s()); |
| 369 } |
| 370 if (forceScrollingForLayoutView || !scrollOffset.isZero() || layer->scro
llsOverflow()) { |
325 TransformationMatrix matrix = TransformationMatrix().translate(-scro
llOffset.width(), -scrollOffset.height()); | 371 TransformationMatrix matrix = TransformationMatrix().translate(-scro
llOffset.width(), -scrollOffset.height()); |
326 context.current.transform = object.getMutableForPainting().ensureObj
ectPaintProperties().createOrUpdateScrollTranslation( | 372 context.current.transform = object.getMutableForPainting().ensureObj
ectPaintProperties().createOrUpdateScrollTranslation( |
327 context.current.transform, matrix, FloatPoint3D(), context.curre
nt.shouldFlattenInheritedTransform, context.current.renderingContextID); | 373 context.current.transform, matrix, FloatPoint3D(), context.curre
nt.shouldFlattenInheritedTransform, context.current.renderingContextID); |
328 context.current.shouldFlattenInheritedTransform = false; | 374 context.current.shouldFlattenInheritedTransform = false; |
329 return; | 375 return; |
330 } | 376 } |
331 } | 377 } |
332 | 378 |
333 if (ObjectPaintProperties* properties = object.getMutableForPainting().objec
tPaintProperties()) | 379 if (ObjectPaintProperties* properties = object.getMutableForPainting().objec
tPaintProperties()) |
334 properties->clearScrollTranslation(); | 380 properties->clearScrollTranslation(); |
335 } | 381 } |
336 | 382 |
337 void PaintPropertyTreeBuilder::updateOutOfFlowContext(const LayoutObject& object
, PaintPropertyTreeBuilderContext& context) | 383 void PaintPropertyTreeBuilder::updateOutOfFlowContext(const LayoutObject& object
, PaintPropertyTreeBuilderContext& context) |
338 { | 384 { |
339 if (object.canContainAbsolutePositionObjects()) { | 385 if (object.canContainAbsolutePositionObjects()) { |
340 context.absolutePosition = context.current; | 386 context.absolutePosition = context.current; |
341 context.containerForAbsolutePosition = &object; | 387 context.containerForAbsolutePosition = &object; |
342 } | 388 } |
343 | 389 |
344 // TODO(pdr): Remove the !object.isLayoutView() condition when removing Fram
eView | 390 if (object.isLayoutView()) { |
345 // paint properties for rootLayerScrolls. | 391 Settings* settings = object.document().settings(); |
346 if (!object.isLayoutView() && object.canContainFixedPositionObjects()) { | 392 if (settings && settings->rootLayerScrolls()) { |
| 393 context.fixedPosition = context.current; |
| 394 const TransformPaintPropertyNode* transform = object.objectPaintProp
erties()->paintOffsetTranslation(); |
| 395 DCHECK(transform); |
| 396 context.fixedPosition.transform = transform; |
| 397 } |
| 398 } else if (object.canContainFixedPositionObjects()) { |
347 context.fixedPosition = context.current; | 399 context.fixedPosition = context.current; |
348 } else if (object.getMutableForPainting().objectPaintProperties() && object.
objectPaintProperties()->cssClip()) { | 400 } else if (object.getMutableForPainting().objectPaintProperties() && object.
objectPaintProperties()->cssClip()) { |
349 // CSS clip applies to all descendants, even if this object is not a con
taining block | 401 // CSS clip applies to all descendants, even if this object is not a con
taining block |
350 // ancestor of the descendant. It is okay for absolute-position descenda
nts because | 402 // ancestor of the descendant. It is okay for absolute-position descenda
nts because |
351 // having CSS clip implies being absolute position container. However fo
r fixed-position | 403 // having CSS clip implies being absolute position container. However fo
r fixed-position |
352 // descendants we need to insert the clip here if we are not a containin
g block ancestor | 404 // descendants we need to insert the clip here if we are not a containin
g block ancestor |
353 // of them. | 405 // of them. |
354 auto* cssClip = object.getMutableForPainting().objectPaintProperties()->
cssClip(); | 406 auto* cssClip = object.getMutableForPainting().objectPaintProperties()->
cssClip(); |
355 | 407 |
356 // Before we actually create anything, check whether in-flow context and
fixed-position | 408 // Before we actually create anything, check whether in-flow context and
fixed-position |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
438 return; | 490 return; |
439 | 491 |
440 updateOverflowClip(object, context); | 492 updateOverflowClip(object, context); |
441 updatePerspective(object, context); | 493 updatePerspective(object, context); |
442 updateSvgLocalToBorderBoxTransform(object, context); | 494 updateSvgLocalToBorderBoxTransform(object, context); |
443 updateScrollTranslation(object, context); | 495 updateScrollTranslation(object, context); |
444 updateOutOfFlowContext(object, context); | 496 updateOutOfFlowContext(object, context); |
445 } | 497 } |
446 | 498 |
447 } // namespace blink | 499 } // namespace blink |
OLD | NEW |