| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/PaintInvalidator.h" | 5 #include "core/paint/PaintInvalidator.h" |
| 6 | 6 |
| 7 #include "core/editing/FrameSelection.h" | 7 #include "core/editing/FrameSelection.h" |
| 8 #include "core/frame/FrameView.h" | 8 #include "core/frame/FrameView.h" |
| 9 #include "core/frame/LocalFrame.h" | 9 #include "core/frame/LocalFrame.h" |
| 10 #include "core/frame/Settings.h" | 10 #include "core/frame/Settings.h" |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 100 } else if (object == context.paint_invalidation_container) { | 100 } else if (object == context.paint_invalidation_container) { |
| 101 result = LayoutRect(rect); | 101 result = LayoutRect(rect); |
| 102 } else { | 102 } else { |
| 103 // For non-root SVG, the input rect is in local SVG coordinates in which | 103 // For non-root SVG, the input rect is in local SVG coordinates in which |
| 104 // paint offset doesn't apply. | 104 // paint offset doesn't apply. |
| 105 if (!is_svg_child) | 105 if (!is_svg_child) |
| 106 rect.MoveBy(Point(object.PaintOffset())); | 106 rect.MoveBy(Point(object.PaintOffset())); |
| 107 | 107 |
| 108 auto container_contents_properties = | 108 auto container_contents_properties = |
| 109 context.paint_invalidation_container->ContentsProperties(); | 109 context.paint_invalidation_container->ContentsProperties(); |
| 110 if (context.tree_builder_context_->fragments[0].current.transform == | 110 if (context.tree_builder_context_->current.transform == |
| 111 container_contents_properties.Transform() && | 111 container_contents_properties.Transform() && |
| 112 context.tree_builder_context_->fragments[0].current.clip == | 112 context.tree_builder_context_->current.clip == |
| 113 container_contents_properties.Clip()) { | 113 container_contents_properties.Clip()) { |
| 114 result = LayoutRect(rect); | 114 result = LayoutRect(rect); |
| 115 } else { | 115 } else { |
| 116 // Use enclosingIntRect to ensure the final visual rect will cover the | 116 // Use enclosingIntRect to ensure the final visual rect will cover the |
| 117 // rect in source coordinates no matter if the painting will use pixel | 117 // rect in source coordinates no matter if the painting will use pixel |
| 118 // snapping, when transforms are applied. If there is no transform, | 118 // snapping, when transforms are applied. If there is no transform, |
| 119 // enclosingIntRect is applied in the last step of paint invalidation | 119 // enclosingIntRect is applied in the last step of paint invalidation |
| 120 // (see CompositedLayerMapping::setContentsNeedDisplayInRect()). | 120 // (see CompositedLayerMapping::setContentsNeedDisplayInRect()). |
| 121 if (!is_svg_child && | 121 if (!is_svg_child && context.tree_builder_context_->current.transform != |
| 122 context.tree_builder_context_->fragments[0].current.transform != | 122 container_contents_properties.Transform()) |
| 123 container_contents_properties.Transform()) | |
| 124 rect = Rect(EnclosingIntRect(rect)); | 123 rect = Rect(EnclosingIntRect(rect)); |
| 125 | 124 |
| 126 PropertyTreeState current_tree_state( | 125 PropertyTreeState current_tree_state( |
| 127 context.tree_builder_context_->fragments[0].current.transform, | 126 context.tree_builder_context_->current.transform, |
| 128 context.tree_builder_context_->fragments[0].current.clip, nullptr); | 127 context.tree_builder_context_->current.clip, nullptr); |
| 129 | 128 |
| 130 FloatClipRect float_rect((FloatRect(rect))); | 129 FloatClipRect float_rect((FloatRect(rect))); |
| 131 GeometryMapper::SourceToDestinationVisualRect( | 130 GeometryMapper::SourceToDestinationVisualRect( |
| 132 current_tree_state, container_contents_properties, float_rect); | 131 current_tree_state, container_contents_properties, float_rect); |
| 133 result = LayoutRect(float_rect.Rect()); | 132 result = LayoutRect(float_rect.Rect()); |
| 134 } | 133 } |
| 135 | 134 |
| 136 // Convert the result to the container's contents space. | 135 // Convert the result to the container's contents space. |
| 137 result.MoveBy(-context.paint_invalidation_container->PaintOffset()); | 136 result.MoveBy(-context.paint_invalidation_container->PaintOffset()); |
| 138 } | 137 } |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 175 // In SPv2, locationInBacking is in the space of their local transform node. | 174 // In SPv2, locationInBacking is in the space of their local transform node. |
| 176 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) | 175 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) |
| 177 return object.PaintOffset(); | 176 return object.PaintOffset(); |
| 178 | 177 |
| 179 LayoutPoint point; | 178 LayoutPoint point; |
| 180 if (object != context.paint_invalidation_container) { | 179 if (object != context.paint_invalidation_container) { |
| 181 point.MoveBy(object.PaintOffset()); | 180 point.MoveBy(object.PaintOffset()); |
| 182 | 181 |
| 183 const auto* container_transform = | 182 const auto* container_transform = |
| 184 context.paint_invalidation_container->ContentsProperties().Transform(); | 183 context.paint_invalidation_container->ContentsProperties().Transform(); |
| 185 if (context.tree_builder_context_->fragments[0].current.transform != | 184 if (context.tree_builder_context_->current.transform != |
| 186 container_transform) { | 185 container_transform) { |
| 187 FloatRect rect = FloatRect(FloatPoint(point), FloatSize()); | 186 FloatRect rect = FloatRect(FloatPoint(point), FloatSize()); |
| 188 GeometryMapper::SourceToDestinationRect( | 187 GeometryMapper::SourceToDestinationRect( |
| 189 context.tree_builder_context_->fragments[0].current.transform, | 188 context.tree_builder_context_->current.transform, container_transform, |
| 190 container_transform, rect); | 189 rect); |
| 191 point = LayoutPoint(rect.Location()); | 190 point = LayoutPoint(rect.Location()); |
| 192 } | 191 } |
| 193 | 192 |
| 194 // Convert the result to the container's contents space. | 193 // Convert the result to the container's contents space. |
| 195 point.MoveBy(-context.paint_invalidation_container->PaintOffset()); | 194 point.MoveBy(-context.paint_invalidation_container->PaintOffset()); |
| 196 } | 195 } |
| 197 | 196 |
| 198 if (context.paint_invalidation_container->Layer()->GroupedMapping()) { | 197 if (context.paint_invalidation_container->Layer()->GroupedMapping()) { |
| 199 FloatPoint float_point(point); | 198 FloatPoint float_point(point); |
| 200 PaintLayer::MapPointInPaintInvalidationContainerToBacking( | 199 PaintLayer::MapPointInPaintInvalidationContainerToBacking( |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 | 255 |
| 257 // This is temporary to workaround paint invalidation issues in | 256 // This is temporary to workaround paint invalidation issues in |
| 258 // non-rootLayerScrolls mode. | 257 // non-rootLayerScrolls mode. |
| 259 // It undoes FrameView's content clip and scroll for paint invalidation of frame | 258 // It undoes FrameView's content clip and scroll for paint invalidation of frame |
| 260 // scroll controls and the LayoutView to which the content clip and scroll don't | 259 // scroll controls and the LayoutView to which the content clip and scroll don't |
| 261 // apply. | 260 // apply. |
| 262 class ScopedUndoFrameViewContentClipAndScroll { | 261 class ScopedUndoFrameViewContentClipAndScroll { |
| 263 public: | 262 public: |
| 264 ScopedUndoFrameViewContentClipAndScroll( | 263 ScopedUndoFrameViewContentClipAndScroll( |
| 265 const FrameView& frame_view, | 264 const FrameView& frame_view, |
| 266 const PaintPropertyTreeBuilderContext& tree_builder_context) | 265 const PaintPropertyTreeBuilderFragmentContext& tree_builder_context) |
| 267 : tree_builder_context_( | 266 : tree_builder_context_( |
| 268 const_cast<PaintPropertyTreeBuilderContext&>(tree_builder_context)), | 267 const_cast<PaintPropertyTreeBuilderFragmentContext&>( |
| 269 saved_context_(tree_builder_context_.fragments[0].current) { | 268 tree_builder_context)), |
| 269 saved_context_(tree_builder_context_.current) { |
| 270 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); | 270 DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); |
| 271 | 271 |
| 272 if (frame_view.ContentClip() == saved_context_.clip) { | 272 if (frame_view.ContentClip() == saved_context_.clip) { |
| 273 tree_builder_context_.fragments[0].current.clip = | 273 tree_builder_context_.current.clip = saved_context_.clip->Parent(); |
| 274 saved_context_.clip->Parent(); | |
| 275 } | 274 } |
| 276 if (const auto* scroll_translation = frame_view.ScrollTranslation()) { | 275 if (const auto* scroll_translation = frame_view.ScrollTranslation()) { |
| 277 if (scroll_translation->ScrollNode() == saved_context_.scroll) { | 276 if (scroll_translation->ScrollNode() == saved_context_.scroll) { |
| 278 tree_builder_context_.fragments[0].current.scroll = | 277 tree_builder_context_.current.scroll = saved_context_.scroll->Parent(); |
| 279 saved_context_.scroll->Parent(); | |
| 280 } | 278 } |
| 281 if (scroll_translation == saved_context_.transform) { | 279 if (scroll_translation == saved_context_.transform) { |
| 282 tree_builder_context_.fragments[0].current.transform = | 280 tree_builder_context_.current.transform = |
| 283 saved_context_.transform->Parent(); | 281 saved_context_.transform->Parent(); |
| 284 } | 282 } |
| 285 } | 283 } |
| 286 } | 284 } |
| 287 | 285 |
| 288 ~ScopedUndoFrameViewContentClipAndScroll() { | 286 ~ScopedUndoFrameViewContentClipAndScroll() { |
| 289 tree_builder_context_.fragments[0].current = saved_context_; | 287 tree_builder_context_.current = saved_context_; |
| 290 } | 288 } |
| 291 | 289 |
| 292 private: | 290 private: |
| 293 PaintPropertyTreeBuilderContext& tree_builder_context_; | 291 PaintPropertyTreeBuilderFragmentContext& tree_builder_context_; |
| 294 PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext | 292 PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext |
| 295 saved_context_; | 293 saved_context_; |
| 296 }; | 294 }; |
| 297 | 295 |
| 298 } // namespace | 296 } // namespace |
| 299 | 297 |
| 300 void PaintInvalidator::UpdatePaintInvalidationContainer( | 298 void PaintInvalidator::UpdatePaintInvalidationContainer( |
| 301 const LayoutObject& object, | 299 const LayoutObject& object, |
| 302 PaintInvalidatorContext& context) { | 300 PaintInvalidatorContext& context) { |
| 303 if (object.IsPaintInvalidationContainer()) { | 301 if (object.IsPaintInvalidationContainer()) { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 355 } | 353 } |
| 356 } | 354 } |
| 357 | 355 |
| 358 DCHECK(context.paint_invalidation_container == | 356 DCHECK(context.paint_invalidation_container == |
| 359 object.ContainerForPaintInvalidation()); | 357 object.ContainerForPaintInvalidation()); |
| 360 DCHECK(context.painting_layer == object.PaintingLayer()); | 358 DCHECK(context.painting_layer == object.PaintingLayer()); |
| 361 } | 359 } |
| 362 | 360 |
| 363 void PaintInvalidator::UpdateVisualRectIfNeeded( | 361 void PaintInvalidator::UpdateVisualRectIfNeeded( |
| 364 const LayoutObject& object, | 362 const LayoutObject& object, |
| 363 const PaintPropertyTreeBuilderContext* tree_builder_context, |
| 365 PaintInvalidatorContext& context) { | 364 PaintInvalidatorContext& context) { |
| 366 context.old_visual_rect = object.VisualRect(); | 365 context.old_visual_rect = object.VisualRect(); |
| 367 context.old_location = ObjectPaintInvalidator(object).LocationInBacking(); | 366 context.old_location = ObjectPaintInvalidator(object).LocationInBacking(); |
| 368 | 367 |
| 369 #if DCHECK_IS_ON() | 368 #if DCHECK_IS_ON() |
| 370 FindObjectVisualRectNeedingUpdateScope finder(object, context); | 369 FindObjectVisualRectNeedingUpdateScope finder( |
| 370 object, context, |
| 371 tree_builder_context && tree_builder_context->is_actually_needed); |
| 372 |
| 373 context.tree_builder_context_actually_needed_ = |
| 374 tree_builder_context->is_actually_needed; |
| 371 #endif | 375 #endif |
| 372 | 376 |
| 373 if (!context.NeedsVisualRectUpdate(object)) { | 377 if (!context.NeedsVisualRectUpdate(object)) { |
| 374 context.new_location = context.old_location; | 378 context.new_location = context.old_location; |
| 375 return; | 379 return; |
| 376 } | 380 } |
| 377 | 381 |
| 378 UpdateVisualRect(object, context); | 382 DCHECK(tree_builder_context); |
| 383 for (auto& fragment : tree_builder_context->fragments) { |
| 384 context.tree_builder_context_ = &fragment; |
| 385 UpdateVisualRect(object, context); |
| 386 } |
| 379 } | 387 } |
| 380 | 388 |
| 381 void PaintInvalidator::UpdateVisualRect(const LayoutObject& object, | 389 void PaintInvalidator::UpdateVisualRect(const LayoutObject& object, |
| 382 PaintInvalidatorContext& context) { | 390 PaintInvalidatorContext& context) { |
| 383 // The paint offset should already be updated through | 391 // The paint offset should already be updated through |
| 384 // PaintPropertyTreeBuilder::updatePropertiesForSelf. | 392 // PaintPropertyTreeBuilder::updatePropertiesForSelf. |
| 385 DCHECK(context.tree_builder_context_); | 393 DCHECK(context.tree_builder_context_->current.paint_offset == |
| 386 DCHECK(context.tree_builder_context_->fragments[0].current.paint_offset == | |
| 387 object.PaintOffset()); | 394 object.PaintOffset()); |
| 388 | 395 |
| 389 Optional<ScopedUndoFrameViewContentClipAndScroll> | 396 Optional<ScopedUndoFrameViewContentClipAndScroll> |
| 390 undo_frame_view_content_clip_and_scroll; | 397 undo_frame_view_content_clip_and_scroll; |
| 391 | 398 |
| 392 if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled() && | 399 if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled() && |
| 393 object.IsLayoutView() && !object.IsPaintInvalidationContainer()) { | 400 object.IsLayoutView() && !object.IsPaintInvalidationContainer()) { |
| 394 undo_frame_view_content_clip_and_scroll.emplace( | 401 undo_frame_view_content_clip_and_scroll.emplace( |
| 395 *ToLayoutView(object).GetFrameView(), *context.tree_builder_context_); | 402 *ToLayoutView(object).GetFrameView(), *context.tree_builder_context_); |
| 396 } | 403 } |
| 397 | 404 |
| 398 LayoutRect new_visual_rect = ComputeVisualRectInBacking(object, context); | 405 LayoutRect new_visual_rect = ComputeVisualRectInBacking(object, context); |
| 399 if (object.IsBoxModelObject()) { | 406 if (object.IsBoxModelObject()) { |
| 400 context.new_location = ComputeLocationInBacking(object, context); | 407 context.new_location = ComputeLocationInBacking(object, context); |
| 401 // Location of empty visual rect doesn't affect paint invalidation. Set it | 408 // Location of empty visual rect doesn't affect paint invalidation. Set it |
| 402 // to newLocation to avoid saving the previous location separately in | 409 // to newLocation to avoid saving the previous location separately in |
| 403 // ObjectPaintInvalidator. | 410 // ObjectPaintInvalidator. |
| 404 if (new_visual_rect.IsEmpty()) | 411 if (new_visual_rect.IsEmpty()) |
| 405 new_visual_rect.SetLocation(context.new_location); | 412 new_visual_rect.SetLocation(context.new_location); |
| 406 } else { | 413 } else { |
| 407 // Use visual rect location for non-LayoutBoxModelObject because it suffices | 414 // Use visual rect location for non-LayoutBoxModelObject because it suffices |
| 408 // to check whether a visual rect changes for layout caused invalidation. | 415 // to check whether a visual rect changes for layout caused invalidation. |
| 409 context.new_location = new_visual_rect.Location(); | 416 context.new_location = new_visual_rect.Location(); |
| 410 } | 417 } |
| 411 | 418 |
| 412 object.GetMutableForPainting().SetVisualRect(new_visual_rect); | 419 object.GetMutableForPainting().SetVisualRect(new_visual_rect); |
| 413 ObjectPaintInvalidator(object).SetLocationInBacking(context.new_location); | 420 ObjectPaintInvalidator(object).SetLocationInBacking(context.new_location); |
| 414 } | 421 } |
| 415 | 422 |
| 416 void PaintInvalidator::InvalidatePaint(FrameView& frame_view, | 423 void PaintInvalidator::InvalidatePaint( |
| 417 PaintInvalidatorContext& context) { | 424 FrameView& frame_view, |
| 425 const PaintPropertyTreeBuilderContext* tree_builder_context, |
| 426 |
| 427 PaintInvalidatorContext& context) { |
| 418 LayoutView* layout_view = frame_view.GetLayoutView(); | 428 LayoutView* layout_view = frame_view.GetLayoutView(); |
| 419 CHECK(layout_view); | 429 CHECK(layout_view); |
| 420 | 430 |
| 421 context.paint_invalidation_container = | 431 context.paint_invalidation_container = |
| 422 context.paint_invalidation_container_for_stacked_contents = | 432 context.paint_invalidation_container_for_stacked_contents = |
| 423 &layout_view->ContainerForPaintInvalidation(); | 433 &layout_view->ContainerForPaintInvalidation(); |
| 424 context.painting_layer = layout_view->Layer(); | 434 context.painting_layer = layout_view->Layer(); |
| 435 if (tree_builder_context) { |
| 436 context.tree_builder_context_ = &tree_builder_context->fragments[0]; |
| 437 #if DCHECK_IS_ON() |
| 438 context.tree_builder_context_actually_needed_ = |
| 439 tree_builder_context->is_actually_needed; |
| 440 #endif |
| 441 } |
| 425 | 442 |
| 426 if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { | 443 if (!RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { |
| 427 Optional<ScopedUndoFrameViewContentClipAndScroll> undo; | 444 Optional<ScopedUndoFrameViewContentClipAndScroll> undo; |
| 428 if (context.tree_builder_context_) | 445 if (tree_builder_context) |
| 429 undo.emplace(frame_view, *context.tree_builder_context_); | 446 undo.emplace(frame_view, *context.tree_builder_context_); |
| 430 frame_view.InvalidatePaintOfScrollControlsIfNeeded(context); | 447 frame_view.InvalidatePaintOfScrollControlsIfNeeded(context); |
| 431 } | 448 } |
| 432 } | 449 } |
| 433 | 450 |
| 434 void PaintInvalidator::InvalidatePaint(const LayoutObject& object, | 451 void PaintInvalidator::InvalidatePaint( |
| 435 PaintInvalidatorContext& context) { | 452 const LayoutObject& object, |
| 453 const PaintPropertyTreeBuilderContext* tree_builder_context, |
| 454 PaintInvalidatorContext& context) { |
| 436 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("blink.invalidation"), | 455 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("blink.invalidation"), |
| 437 "PaintInvalidator::invalidatePaintIfNeeded()", "object", | 456 "PaintInvalidator::invalidatePaintIfNeeded()", "object", |
| 438 object.DebugName().Ascii()); | 457 object.DebugName().Ascii()); |
| 439 | 458 |
| 440 object.GetMutableForPainting().EnsureIsReadyForPaintInvalidation(); | 459 object.GetMutableForPainting().EnsureIsReadyForPaintInvalidation(); |
| 441 | 460 |
| 442 UpdatePaintingLayer(object, context); | 461 UpdatePaintingLayer(object, context); |
| 443 | 462 |
| 444 if (object.GetDocument().Printing() && | 463 if (object.GetDocument().Printing() && |
| 445 !RuntimeEnabledFeatures::printBrowserEnabled()) | 464 !RuntimeEnabledFeatures::printBrowserEnabled()) |
| 446 return; // Don't invalidate paints if we're printing. | 465 return; // Don't invalidate paints if we're printing. |
| 447 | 466 |
| 448 // TODO(crbug.com/637313): Use GeometryMapper which now supports filter | 467 // TODO(crbug.com/637313): Use GeometryMapper which now supports filter |
| 449 // geometry effects, after skia optimizes filter's mapRect operation. | 468 // geometry effects, after skia optimizes filter's mapRect operation. |
| 450 // TODO(crbug.com/648274): This is a workaround for multi-column contents. | 469 // TODO(crbug.com/648274): This is a workaround for multi-column contents. |
| 451 if (object.HasFilterInducingProperty() || object.IsLayoutFlowThread()) { | 470 if (object.HasFilterInducingProperty() || object.IsLayoutFlowThread()) { |
| 452 context.forced_subtree_invalidation_flags |= | 471 context.forced_subtree_invalidation_flags |= |
| 453 PaintInvalidatorContext::kForcedSubtreeSlowPathRect; | 472 PaintInvalidatorContext::kForcedSubtreeSlowPathRect; |
| 454 } | 473 } |
| 455 | 474 |
| 456 UpdatePaintInvalidationContainer(object, context); | 475 UpdatePaintInvalidationContainer(object, context); |
| 457 UpdateVisualRectIfNeeded(object, context); | 476 UpdateVisualRectIfNeeded(object, tree_builder_context, context); |
| 458 | 477 |
| 459 if (!object.ShouldCheckForPaintInvalidation() && | 478 if (!object.ShouldCheckForPaintInvalidation() && |
| 460 !(context.forced_subtree_invalidation_flags & | 479 !(context.forced_subtree_invalidation_flags & |
| 461 ~PaintInvalidatorContext::kForcedSubtreeVisualRectUpdate)) { | 480 ~PaintInvalidatorContext::kForcedSubtreeVisualRectUpdate)) { |
| 462 // We are done updating anything needed. No other paint invalidation work to | 481 // We are done updating anything needed. No other paint invalidation work to |
| 463 // do for this object. | 482 // do for this object. |
| 464 return; | 483 return; |
| 465 } | 484 } |
| 466 | 485 |
| 467 if (object.IsSVGHiddenContainer()) { | 486 if (object.IsSVGHiddenContainer()) { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 510 | 529 |
| 511 void PaintInvalidator::ProcessPendingDelayedPaintInvalidations() { | 530 void PaintInvalidator::ProcessPendingDelayedPaintInvalidations() { |
| 512 for (auto target : pending_delayed_paint_invalidations_) { | 531 for (auto target : pending_delayed_paint_invalidations_) { |
| 513 target->GetMutableForPainting() | 532 target->GetMutableForPainting() |
| 514 .SetShouldDoFullPaintInvalidationWithoutGeometryChange( | 533 .SetShouldDoFullPaintInvalidationWithoutGeometryChange( |
| 515 kPaintInvalidationDelayedFull); | 534 kPaintInvalidationDelayedFull); |
| 516 } | 535 } |
| 517 } | 536 } |
| 518 | 537 |
| 519 } // namespace blink | 538 } // namespace blink |
| OLD | NEW |