Commit d9569a65 authored by Fredrik Söderquist's avatar Fredrik Söderquist Committed by Commit Bot

Fix bounding box calculation for <tspan> (and other LayoutSVGInlines)

Make the various bounding box calculations for LayoutSVGInline return
values derived from the associated flow boxes rather than delegating
to the ancestor LayoutSVGText. Refactor and reuse the code from the
similar functionality LayoutSVGText.

Since the reference box for these element should still remain the same
as the reference box for the ancestor <text> add a new helper to
compute (delegate) that and use that when resolving clips/filter/et.c.

Bug: 951706, 349835, 267481
Change-Id: Idf4a4c586ef9902cac2e795e9fbda0009b2ea0ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2088094
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Reviewed-by: default avatarStephen Chenney <schenney@chromium.org>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#748175}
parent 8d36b2d6
...@@ -60,27 +60,27 @@ InlineFlowBox* LayoutSVGInline::CreateInlineFlowBox() { ...@@ -60,27 +60,27 @@ InlineFlowBox* LayoutSVGInline::CreateInlineFlowBox() {
} }
FloatRect LayoutSVGInline::ObjectBoundingBox() const { FloatRect LayoutSVGInline::ObjectBoundingBox() const {
if (const LayoutSVGText* text_root = FloatRect bounds;
LayoutSVGText::LocateLayoutSVGTextAncestor(this)) for (InlineFlowBox* box : *LineBoxes())
return text_root->ObjectBoundingBox(); bounds.Unite(FloatRect(box->FrameRect()));
return bounds;
return FloatRect();
} }
FloatRect LayoutSVGInline::StrokeBoundingBox() const { FloatRect LayoutSVGInline::StrokeBoundingBox() const {
if (const LayoutSVGText* text_root = if (!FirstLineBox())
LayoutSVGText::LocateLayoutSVGTextAncestor(this)) return FloatRect();
return text_root->StrokeBoundingBox(); return SVGLayoutSupport::ExtendTextBBoxWithStroke(*this, ObjectBoundingBox());
return FloatRect();
} }
FloatRect LayoutSVGInline::VisualRectInLocalSVGCoordinates() const { FloatRect LayoutSVGInline::VisualRectInLocalSVGCoordinates() const {
if (const LayoutSVGText* text_root = if (!FirstLineBox())
LayoutSVGText::LocateLayoutSVGTextAncestor(this)) return FloatRect();
return text_root->VisualRectInLocalSVGCoordinates(); const LayoutSVGText* text_root =
LayoutSVGText::LocateLayoutSVGTextAncestor(this);
return FloatRect(); if (!text_root)
return FloatRect();
return SVGLayoutSupport::ComputeVisualRectForText(
*this, ObjectBoundingBox(), text_root->ObjectBoundingBox());
} }
PhysicalRect LayoutSVGInline::VisualRectInDocument( PhysicalRect LayoutSVGInline::VisualRectInDocument(
...@@ -103,18 +103,10 @@ const LayoutObject* LayoutSVGInline::PushMappingToContainer( ...@@ -103,18 +103,10 @@ const LayoutObject* LayoutSVGInline::PushMappingToContainer(
void LayoutSVGInline::AbsoluteQuads(Vector<FloatQuad>& quads, void LayoutSVGInline::AbsoluteQuads(Vector<FloatQuad>& quads,
MapCoordinatesFlags mode) const { MapCoordinatesFlags mode) const {
const LayoutSVGText* text_root =
LayoutSVGText::LocateLayoutSVGTextAncestor(this);
if (!text_root)
return;
FloatRect text_bounding_box = text_root->StrokeBoundingBox();
for (InlineFlowBox* box : *LineBoxes()) { for (InlineFlowBox* box : *LineBoxes()) {
FloatRect box_rect(box->FrameRect());
quads.push_back(LocalToAbsoluteQuad( quads.push_back(LocalToAbsoluteQuad(
FloatRect(text_bounding_box.X() + box->X().ToFloat(), SVGLayoutSupport::ExtendTextBBoxWithStroke(*this, box_rect), mode));
text_bounding_box.Y() + box->Y().ToFloat(),
box->Width().ToFloat(), box->Height().ToFloat()),
mode));
} }
} }
......
...@@ -38,11 +38,6 @@ class LayoutSVGInline : public LayoutInline { ...@@ -38,11 +38,6 @@ class LayoutSVGInline : public LayoutInline {
bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const override; bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const override;
// Chapter 10.4 of the SVG Specification say that we should use the
// object bounding box of the parent text element.
// We search for the root text element and take its bounding box.
// It is also necessary to take the stroke and visual rect of this element,
// since we need it for filters.
FloatRect ObjectBoundingBox() const final; FloatRect ObjectBoundingBox() const final;
FloatRect StrokeBoundingBox() const final; FloatRect StrokeBoundingBox() const final;
FloatRect VisualRectInLocalSVGCoordinates() const final; FloatRect VisualRectInLocalSVGCoordinates() const final;
......
...@@ -147,7 +147,7 @@ SVGPaintServer SVGPaintServer::RequestForLayoutObject( ...@@ -147,7 +147,7 @@ SVGPaintServer SVGPaintServer::RequestForLayoutObject(
return SVGPaintServer(paint_description.color); return SVGPaintServer(paint_description.color);
SVGPaintServer paint_server = paint_description.resource->PreparePaintServer( SVGPaintServer paint_server = paint_description.resource->PreparePaintServer(
*SVGResources::GetClient(layout_object), *SVGResources::GetClient(layout_object),
layout_object.ObjectBoundingBox()); SVGResources::ReferenceBoxForEffects(layout_object));
if (paint_server.IsValid()) if (paint_server.IsValid())
return paint_server; return paint_server;
if (paint_description.has_fallback) if (paint_description.has_fallback)
......
...@@ -367,27 +367,17 @@ FloatRect LayoutSVGText::ObjectBoundingBox() const { ...@@ -367,27 +367,17 @@ FloatRect LayoutSVGText::ObjectBoundingBox() const {
} }
FloatRect LayoutSVGText::StrokeBoundingBox() const { FloatRect LayoutSVGText::StrokeBoundingBox() const {
FloatRect stroke_boundaries = ObjectBoundingBox(); if (!FirstRootBox())
const SVGComputedStyle& svg_style = StyleRef().SvgStyle(); return FloatRect();
if (!svg_style.HasStroke()) return SVGLayoutSupport::ExtendTextBBoxWithStroke(*this, ObjectBoundingBox());
return stroke_boundaries;
DCHECK(GetElement());
SVGLengthContext length_context(GetElement());
stroke_boundaries.Inflate(
length_context.ValueForLength(svg_style.StrokeWidth()));
return stroke_boundaries;
} }
FloatRect LayoutSVGText::VisualRectInLocalSVGCoordinates() const { FloatRect LayoutSVGText::VisualRectInLocalSVGCoordinates() const {
FloatRect visual_rect = StrokeBoundingBox(); if (!FirstRootBox())
SVGLayoutSupport::AdjustVisualRectWithResources(*this, ObjectBoundingBox(), return FloatRect();
visual_rect); const FloatRect object_bounds = ObjectBoundingBox();
return SVGLayoutSupport::ComputeVisualRectForText(*this, object_bounds,
if (const ShadowList* text_shadow = StyleRef().TextShadow()) object_bounds);
text_shadow->AdjustRectForShadow(visual_rect);
return visual_rect;
} }
void LayoutSVGText::AddOutlineRects(Vector<PhysicalRect>& rects, void LayoutSVGText::AddOutlineRects(Vector<PhysicalRect>& rects,
......
...@@ -409,6 +409,34 @@ void SVGLayoutSupport::AdjustVisualRectWithResources( ...@@ -409,6 +409,34 @@ void SVGLayoutSupport::AdjustVisualRectWithResources(
visual_rect.Intersect(masker->ResourceBoundingBox(object_bounding_box)); visual_rect.Intersect(masker->ResourceBoundingBox(object_bounding_box));
} }
FloatRect SVGLayoutSupport::ExtendTextBBoxWithStroke(
const LayoutObject& layout_object,
const FloatRect& text_bounds) {
DCHECK(layout_object.IsSVGText() || layout_object.IsSVGInline());
FloatRect bounds = text_bounds;
const SVGComputedStyle& svg_style = layout_object.StyleRef().SvgStyle();
if (svg_style.HasStroke()) {
SVGLengthContext length_context(To<SVGElement>(layout_object.GetNode()));
// TODO(fs): This approximation doesn't appear to be conservative enough
// since while text (usually?) won't have caps it could have joins and thus
// miters.
bounds.Inflate(length_context.ValueForLength(svg_style.StrokeWidth()));
}
return bounds;
}
FloatRect SVGLayoutSupport::ComputeVisualRectForText(
const LayoutObject& layout_object,
const FloatRect& text_bounds,
const FloatRect& reference_box) {
DCHECK(layout_object.IsSVGText() || layout_object.IsSVGInline());
FloatRect visual_rect = ExtendTextBBoxWithStroke(layout_object, text_bounds);
if (const ShadowList* text_shadow = layout_object.StyleRef().TextShadow())
text_shadow->AdjustRectForShadow(visual_rect);
AdjustVisualRectWithResources(layout_object, reference_box, visual_rect);
return visual_rect;
}
bool SVGLayoutSupport::HasFilterResource(const LayoutObject& object) { bool SVGLayoutSupport::HasFilterResource(const LayoutObject& object) {
SVGResources* resources = SVGResources* resources =
SVGResourcesCache::CachedResourcesForLayoutObject(object); SVGResourcesCache::CachedResourcesForLayoutObject(object);
......
...@@ -68,6 +68,15 @@ class CORE_EXPORT SVGLayoutSupport { ...@@ -68,6 +68,15 @@ class CORE_EXPORT SVGLayoutSupport {
const FloatRect& object_bounding_box, const FloatRect& object_bounding_box,
FloatRect&); FloatRect&);
// Add any contribution from 'stroke' to a text content bounding rect.
static FloatRect ExtendTextBBoxWithStroke(const LayoutObject&,
const FloatRect& text_bounds);
// Compute the visual rect for the a text content LayoutObject.
static FloatRect ComputeVisualRectForText(const LayoutObject&,
const FloatRect& text_bounds,
const FloatRect& reference_box);
// Determine if the LayoutObject references a filter resource object. // Determine if the LayoutObject references a filter resource object.
static bool HasFilterResource(const LayoutObject&); static bool HasFilterResource(const LayoutObject&);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_paint_server.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
#include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/reference_clip_path_operation.h" #include "third_party/blink/renderer/core/style/reference_clip_path_operation.h"
...@@ -51,6 +52,20 @@ SVGElementResourceClient* SVGResources::GetClient(const LayoutObject& object) { ...@@ -51,6 +52,20 @@ SVGElementResourceClient* SVGResources::GetClient(const LayoutObject& object) {
return To<SVGElement>(object.GetNode())->GetSVGResourceClient(); return To<SVGElement>(object.GetNode())->GetSVGResourceClient();
} }
FloatRect SVGResources::ReferenceBoxForEffects(
const LayoutObject& layout_object) {
// Text "sub-elements" (<tspan>, <textpath>, <a>) should use the entire
// <text>s object bounding box rather then their own.
// https://svgwg.org/svg2-draft/text.html#ObjectBoundingBoxUnitsTextObjects
const LayoutObject* obb_layout_object = &layout_object;
if (layout_object.IsSVGInline()) {
obb_layout_object =
LayoutSVGText::LocateLayoutSVGTextAncestor(&layout_object);
}
DCHECK(obb_layout_object);
return obb_layout_object->ObjectBoundingBox();
}
static HashSet<AtomicString>& ClipperFilterMaskerTags() { static HashSet<AtomicString>& ClipperFilterMaskerTags() {
DEFINE_STATIC_LOCAL( DEFINE_STATIC_LOCAL(
HashSet<AtomicString>, tag_list, HashSet<AtomicString>, tag_list,
......
...@@ -50,6 +50,7 @@ class SVGResources { ...@@ -50,6 +50,7 @@ class SVGResources {
SVGResources(); SVGResources();
static SVGElementResourceClient* GetClient(const LayoutObject&); static SVGElementResourceClient* GetClient(const LayoutObject&);
static FloatRect ReferenceBoxForEffects(const LayoutObject&);
static std::unique_ptr<SVGResources> BuildResources(const LayoutObject&, static std::unique_ptr<SVGResources> BuildResources(const LayoutObject&,
const ComputedStyle&); const ComputedStyle&);
......
...@@ -65,7 +65,7 @@ LayoutSVGResourceClipper* ResolveElementReference( ...@@ -65,7 +65,7 @@ LayoutSVGResourceClipper* ResolveElementReference(
FloatRect ClipPathClipper::LocalReferenceBox(const LayoutObject& object) { FloatRect ClipPathClipper::LocalReferenceBox(const LayoutObject& object) {
if (object.IsSVGChild()) if (object.IsSVGChild())
return object.ObjectBoundingBox(); return SVGResources::ReferenceBoxForEffects(object);
if (object.IsBox()) if (object.IsBox())
return FloatRect(ToLayoutBox(object).BorderBoxRect()); return FloatRect(ToLayoutBox(object).BorderBoxRect());
......
...@@ -23,8 +23,9 @@ base::Optional<IntRect> CSSMaskPainter::MaskBoundingBox( ...@@ -23,8 +23,9 @@ base::Optional<IntRect> CSSMaskPainter::MaskBoundingBox(
SVGResourcesCache::CachedResourcesForLayoutObject(object); SVGResourcesCache::CachedResourcesForLayoutObject(object);
LayoutSVGResourceMasker* masker = resources ? resources->Masker() : nullptr; LayoutSVGResourceMasker* masker = resources ? resources->Masker() : nullptr;
if (masker) { if (masker) {
return EnclosingIntRect( const FloatRect reference_box =
masker->ResourceBoundingBox(object.ObjectBoundingBox())); SVGResources::ReferenceBoxForEffects(object);
return EnclosingIntRect(masker->ResourceBoundingBox(reference_box));
} }
} }
......
...@@ -101,7 +101,7 @@ GraphicsContext* SVGFilterPainter::PrepareEffect( ...@@ -101,7 +101,7 @@ GraphicsContext* SVGFilterPainter::PrepareEffect(
} }
auto* node_map = MakeGarbageCollected<SVGFilterGraphNodeMap>(); auto* node_map = MakeGarbageCollected<SVGFilterGraphNodeMap>();
FilterEffectBuilder builder(object.ObjectBoundingBox(), 1); FilterEffectBuilder builder(SVGResources::ReferenceBoxForEffects(object), 1);
Filter* filter = builder.BuildReferenceFilter( Filter* filter = builder.BuildReferenceFilter(
To<SVGFilterElement>(*filter_.GetElement()), nullptr, node_map); To<SVGFilterElement>(*filter_.GetElement()), nullptr, node_map);
if (!filter || !filter->LastEffect()) if (!filter || !filter->LastEffect())
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/paint/svg_mask_painter.h" #include "third_party/blink/renderer/core/paint/svg_mask_painter.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
#include "third_party/blink/renderer/core/paint/object_paint_properties.h" #include "third_party/blink/renderer/core/paint/object_paint_properties.h"
#include "third_party/blink/renderer/core/paint/paint_info.h" #include "third_party/blink/renderer/core/paint/paint_info.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
...@@ -41,7 +42,8 @@ void SVGMaskPainter::FinishEffect(const LayoutObject& object, ...@@ -41,7 +42,8 @@ void SVGMaskPainter::FinishEffect(const LayoutObject& object,
AffineTransform content_transformation; AffineTransform content_transformation;
sk_sp<const PaintRecord> record = mask_.CreatePaintRecord( sk_sp<const PaintRecord> record = mask_.CreatePaintRecord(
content_transformation, object.ObjectBoundingBox(), context); content_transformation, SVGResources::ReferenceBoxForEffects(object),
context);
if (DrawingRecorder::UseCachedDrawingIfPossible(context, display_item_client, if (DrawingRecorder::UseCachedDrawingIfPossible(context, display_item_client,
DisplayItem::kSVGMask)) DisplayItem::kSVGMask))
......
<!doctype html>
<title>{Element,Range}.prototype.getBoundingClientRect on SVG &lt;tspan&gt;</title>
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect">
<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-range-getboundingclientrect">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<svg>
<text y="180" font-size="100" font-family="Ahem"
fill="lightblue">X<tspan fill="blue">XX</tspan></text>
</svg>
<script>
function check(object) {
let rect = object.getBoundingClientRect();
assert_equals(rect.left, 108, 'left');
assert_equals(rect.top, 108, 'top');
assert_equals(rect.width, 200, 'width');
assert_equals(rect.height, 100, 'height');
}
async_test(t => {
window.addEventListener("load", t.step_func_done(() => {
let tspan = document.querySelector('tspan');
check(tspan);
}));
}, document.title + ', Element');
async_test(t => {
window.addEventListener("load", t.step_func_done(() => {
let tspan = document.querySelector('tspan');
let range = new Range();
range.selectNode(tspan);
check(range);
}));
}, document.title + ', Range');
</script>
<!DOCTYPE html> <!doctype html>
<title>getBBox tests</title> <title>SVGGraphicsElement.prototype.getBBox</title>
<script src="../../resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<div id="testcontainer"> <div id="testcontainer">
<svg width="1" height="1" visibility="hidden"> <svg width="1" height="1" visibility="hidden">
<g id="g1"> <g id="g1">
...@@ -126,5 +126,4 @@ test(function() { ...@@ -126,5 +126,4 @@ test(function() {
let r13 = document.getElementById("r13"); let r13 = document.getElementById("r13");
assert_rect_approx_equals(g11.getBBox(), r13.getBBox(), EPSILON); assert_rect_approx_equals(g11.getBBox(), r13.getBBox(), EPSILON);
}, "empty image does not contribute to parent bbox"); }, "empty image does not contribute to parent bbox");
</script> </script>
<!doctype html>
<title>SVGGraphicsElement.prototype.getBBox on &lt;tspan&gt;</title>
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<svg height="100">
<text y="180" font-size="100" font-family="Ahem" fill="lightblue"
transform="translate(0, -100)">X<tspan fill="blue">X</tspan></text>
</svg>
<script>
async_test(t => {
onload = t.step_func_done(() => {
let tspan = document.querySelector('tspan');
let bbox = tspan.getBBox();
for (let property of ['x', 'y', 'width', 'height'])
assert_equals(bbox[property], 100, property);
});
});
</script>
...@@ -27,39 +27,39 @@ ...@@ -27,39 +27,39 @@
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PA'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{
"object": "InlineTextBox 'PA'",
"rect": [10, 60, 310, 380],
"reason": "disappeared"
},
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "appeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 180, 280, 140],
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PA'",
"rect": [10, 60, 310, 380], "rect": [10, 60, 280, 140],
"reason": "disappeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'SS'", "object": "InlineTextBox 'SS'",
"rect": [10, 60, 310, 380], "rect": [10, 60, 280, 140],
"reason": "appeared" "reason": "appeared"
},
{
"object": "InlineTextBox 'PA'",
"rect": [10, 60, 150, 140],
"reason": "disappeared"
} }
] ]
} }
......
...@@ -26,34 +26,34 @@ ...@@ -26,34 +26,34 @@
"rect": [10, 60, 310, 380], "rect": [10, 60, 310, 380],
"reason": "disappeared" "reason": "disappeared"
}, },
{
"object": "InlineTextBox 'PA'",
"rect": [10, 60, 310, 380],
"reason": "disappeared"
},
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "appeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 180, 280, 140],
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 60, 280, 140],
"reason": "appeared"
},
{
"object": "InlineTextBox 'PA'",
"rect": [10, 60, 150, 140],
"reason": "disappeared" "reason": "disappeared"
} }
] ]
......
...@@ -17,34 +17,34 @@ ...@@ -17,34 +17,34 @@
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'FAIL'", "object": "InlineTextBox ' '",
"rect": [10, 60, 310, 380], "rect": [10, 180, 310, 260],
"reason": "disappeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "disappeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox ' '", "object": "InlineTextBox 'PASS'",
"rect": [10, 180, 310, 260], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 180, 310, 260], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'FAIL'",
"rect": [10, 180, 310, 260], "rect": [10, 60, 260, 140],
"reason": "appeared" "reason": "disappeared"
} }
] ]
} }
......
...@@ -22,28 +22,28 @@ ...@@ -22,28 +22,28 @@
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'FAIL'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 260], "rect": [10, 180, 280, 140],
"reason": "disappeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 260], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 260], "rect": [10, 60, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 260], "rect": [10, 60, 280, 140],
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'FAIL'",
"rect": [10, 60, 310, 260], "rect": [10, 60, 260, 140],
"reason": "disappeared" "reason": "disappeared"
} }
] ]
......
...@@ -12,13 +12,13 @@ ...@@ -12,13 +12,13 @@
"reason": "subtree" "reason": "subtree"
}, },
{ {
"object": "InlineTextBox 'Y'", "object": "InlineTextBox 'Z'",
"rect": [8, 8, 300, 100], "rect": [8, 8, 300, 100],
"reason": "subtree" "reason": "subtree"
}, },
{ {
"object": "InlineTextBox 'Z'", "object": "InlineTextBox 'Y'",
"rect": [8, 8, 300, 100], "rect": [108, 8, 100, 100],
"reason": "subtree" "reason": "subtree"
} }
] ]
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
"paintInvalidations": [ "paintInvalidations": [
{ {
"object": "InlineTextBox 'Y'", "object": "InlineTextBox 'Y'",
"rect": [8, 8, 300, 100], "rect": [108, 8, 100, 100],
"reason": "subtree" "reason": "subtree"
} }
] ]
......
...@@ -27,39 +27,39 @@ ...@@ -27,39 +27,39 @@
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PA'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{
"object": "InlineTextBox 'PA'",
"rect": [10, 60, 310, 380],
"reason": "disappeared"
},
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "appeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 180, 280, 140],
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PA'",
"rect": [10, 60, 310, 380], "rect": [10, 60, 280, 140],
"reason": "disappeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'SS'", "object": "InlineTextBox 'SS'",
"rect": [10, 60, 310, 380], "rect": [10, 60, 280, 140],
"reason": "appeared" "reason": "appeared"
},
{
"object": "InlineTextBox 'PA'",
"rect": [10, 60, 150, 140],
"reason": "disappeared"
} }
] ]
} }
......
...@@ -26,34 +26,34 @@ ...@@ -26,34 +26,34 @@
"rect": [10, 60, 310, 380], "rect": [10, 60, 310, 380],
"reason": "disappeared" "reason": "disappeared"
}, },
{
"object": "InlineTextBox 'PA'",
"rect": [10, 60, 310, 380],
"reason": "disappeared"
},
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "appeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 180, 280, 140],
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 60, 280, 140],
"reason": "appeared"
},
{
"object": "InlineTextBox 'PA'",
"rect": [10, 60, 150, 140],
"reason": "disappeared" "reason": "disappeared"
} }
] ]
......
...@@ -17,34 +17,34 @@ ...@@ -17,34 +17,34 @@
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'FAIL'", "object": "InlineTextBox ' '",
"rect": [10, 60, 310, 380], "rect": [10, 180, 310, 260],
"reason": "disappeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "disappeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 380], "rect": [10, 300, 280, 140],
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox ' '", "object": "InlineTextBox 'PASS'",
"rect": [10, 180, 310, 260], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 180, 310, 260], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'FAIL'",
"rect": [10, 180, 310, 260], "rect": [10, 60, 260, 140],
"reason": "appeared" "reason": "disappeared"
} }
] ]
} }
......
...@@ -22,28 +22,28 @@ ...@@ -22,28 +22,28 @@
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'FAIL'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 260], "rect": [10, 180, 280, 140],
"reason": "disappeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 260], "rect": [10, 180, 280, 140],
"reason": "appeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 260], "rect": [10, 60, 280, 140],
"reason": "appeared" "reason": "appeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'PASS'",
"rect": [10, 60, 310, 260], "rect": [10, 60, 280, 140],
"reason": "disappeared" "reason": "disappeared"
}, },
{ {
"object": "InlineTextBox 'PASS'", "object": "InlineTextBox 'FAIL'",
"rect": [10, 60, 310, 260], "rect": [10, 60, 260, 140],
"reason": "disappeared" "reason": "disappeared"
} }
] ]
......
...@@ -12,13 +12,13 @@ ...@@ -12,13 +12,13 @@
"reason": "subtree" "reason": "subtree"
}, },
{ {
"object": "InlineTextBox 'Y'", "object": "InlineTextBox 'Z'",
"rect": [8, 8, 300, 100], "rect": [8, 8, 300, 100],
"reason": "subtree" "reason": "subtree"
}, },
{ {
"object": "InlineTextBox 'Z'", "object": "InlineTextBox 'Y'",
"rect": [8, 8, 300, 100], "rect": [108, 8, 100, 100],
"reason": "subtree" "reason": "subtree"
} }
] ]
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
"paintInvalidations": [ "paintInvalidations": [
{ {
"object": "InlineTextBox 'Y'", "object": "InlineTextBox 'Y'",
"rect": [8, 8, 300, 100], "rect": [108, 8, 100, 100],
"reason": "subtree" "reason": "subtree"
} }
] ]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment