mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-31 21:52:54 +00:00
Clear Android SVG descendant geometry caches when the canvas resizes. Cover growth and shrinkage with a native pixel-rendering regression.
189 lines
8.6 KiB
Diff
189 lines
8.6 KiB
Diff
diff --git a/node_modules/react-native-svg/lib/commonjs/lib/extract/extractTransform.js b/node_modules/react-native-svg/lib/commonjs/lib/extract/extractTransform.js
|
|
index f22cca3..0a73681 100644
|
|
--- a/node_modules/react-native-svg/lib/commonjs/lib/extract/extractTransform.js
|
|
+++ b/node_modules/react-native-svg/lib/commonjs/lib/extract/extractTransform.js
|
|
@@ -152,7 +152,12 @@ function extractTransform(props) {
|
|
}
|
|
function extractTransformSvgView(props) {
|
|
if (typeof props.transform === 'string') {
|
|
- return (0, _transformToRn.parse)(props.transform);
|
|
+ try {
|
|
+ return (0, _transformToRn.parse)(props.transform);
|
|
+ } catch (e) {
|
|
+ console.error(e);
|
|
+ return undefined;
|
|
+ }
|
|
}
|
|
return props.transform;
|
|
}
|
|
diff --git a/node_modules/react-native-svg/lib/module/lib/extract/extractTransform.js b/node_modules/react-native-svg/lib/module/lib/extract/extractTransform.js
|
|
index c9d3d6a..085586c 100644
|
|
--- a/node_modules/react-native-svg/lib/module/lib/extract/extractTransform.js
|
|
+++ b/node_modules/react-native-svg/lib/module/lib/extract/extractTransform.js
|
|
@@ -142,7 +142,12 @@ export default function extractTransform(props) {
|
|
}
|
|
export function extractTransformSvgView(props) {
|
|
if (typeof props.transform === 'string') {
|
|
- return parseTransformSvgToRnStyle(props.transform);
|
|
+ try {
|
|
+ return parseTransformSvgToRnStyle(props.transform);
|
|
+ } catch (e) {
|
|
+ console.error(e);
|
|
+ return undefined;
|
|
+ }
|
|
}
|
|
return props.transform;
|
|
}
|
|
diff --git a/node_modules/react-native-svg/src/lib/extract/extractTransform.ts b/node_modules/react-native-svg/src/lib/extract/extractTransform.ts
|
|
index 0c854d6..7dc2828 100644
|
|
--- a/node_modules/react-native-svg/src/lib/extract/extractTransform.ts
|
|
+++ b/node_modules/react-native-svg/src/lib/extract/extractTransform.ts
|
|
@@ -216,7 +216,19 @@ export function extractTransformSvgView(
|
|
props: TransformsStyle
|
|
): TransformsStyle['transform'] {
|
|
if (typeof props.transform === 'string') {
|
|
- return parseTransformSvgToRnStyle(props.transform);
|
|
+ // A root <Svg> is a real RN view, so a string transform has to be rewritten into RN's
|
|
+ // style syntax. The grammar behind this parser only accepts SVG transform functions and
|
|
+ // throws on anything else - including ordinary CSS (`none`, `rotate(-90deg)`). Every other
|
|
+ // extractor in this file already swallows that error; this one did not, so one unrecognised
|
|
+ // string escaped as an uncaught SyntaxError during render and took the whole app down.
|
|
+ // Untrusted SVG reaches this path (a repository's own favicon.svg/logo.svg is rendered
|
|
+ // through SvgXml/SvgCss), so degrade to "no transform" like the rest of the library.
|
|
+ try {
|
|
+ return parseTransformSvgToRnStyle(props.transform);
|
|
+ } catch (e) {
|
|
+ console.error(e);
|
|
+ return undefined;
|
|
+ }
|
|
}
|
|
return props.transform as TransformsStyle['transform'];
|
|
}
|
|
diff --git a/node_modules/react-native-svg/android/build.gradle b/node_modules/react-native-svg/android/build.gradle
|
|
--- a/node_modules/react-native-svg/android/build.gradle
|
|
+++ b/node_modules/react-native-svg/android/build.gradle
|
|
@@ -103,6 +103,7 @@
|
|
}
|
|
|
|
defaultConfig {
|
|
+ testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
|
minSdkVersion safeExtGet('minSdkVersion', 16)
|
|
//noinspection OldTargetApi
|
|
targetSdkVersion safeExtGet('targetSdkVersion', 28)
|
|
@@ -136,6 +137,8 @@
|
|
}
|
|
|
|
dependencies {
|
|
+ androidTestImplementation 'androidx.test:runner:1.6.2'
|
|
+ androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
|
implementation 'com.facebook.react:react-native:+'
|
|
implementation("com.facebook.fresco:fresco:${FRESCO_VERSION}") {
|
|
exclude group: 'com.facebook.soloader'
|
|
diff --git a/node_modules/react-native-svg/android/src/main/java/com/horcrux/svg/SvgView.java b/node_modules/react-native-svg/android/src/main/java/com/horcrux/svg/SvgView.java
|
|
--- a/node_modules/react-native-svg/android/src/main/java/com/horcrux/svg/SvgView.java
|
|
+++ b/node_modules/react-native-svg/android/src/main/java/com/horcrux/svg/SvgView.java
|
|
@@ -159,6 +159,8 @@
|
|
@Override
|
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
super.onSizeChanged(w, h, oldw, oldh);
|
|
+ // Percentage lengths and paths depend on the canvas size, not just SVG props.
|
|
+ clearChildCache();
|
|
this.invalidate();
|
|
}
|
|
|
|
diff --git a/node_modules/react-native-svg/android/src/androidTest/java/com/horcrux/svg/SvgResizeTest.java b/node_modules/react-native-svg/android/src/androidTest/java/com/horcrux/svg/SvgResizeTest.java
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/node_modules/react-native-svg/android/src/androidTest/java/com/horcrux/svg/SvgResizeTest.java
|
|
@@ -0,0 +1,90 @@
|
|
+package com.horcrux.svg;
|
|
+
|
|
+import static org.junit.Assert.assertEquals;
|
|
+import static org.junit.Assert.assertTrue;
|
|
+
|
|
+import android.graphics.Bitmap;
|
|
+import android.graphics.Canvas;
|
|
+import android.graphics.Color;
|
|
+import android.view.View;
|
|
+import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
+import androidx.test.platform.app.InstrumentationRegistry;
|
|
+import com.facebook.react.bridge.BridgeReactContext;
|
|
+import com.facebook.react.bridge.DynamicFromObject;
|
|
+import com.facebook.react.bridge.JavaOnlyArray;
|
|
+import com.facebook.react.bridge.JavaOnlyMap;
|
|
+import com.facebook.react.soloader.OpenSourceMergedSoMapping;
|
|
+import com.facebook.react.uimanager.DisplayMetricsHolder;
|
|
+import com.facebook.soloader.SoLoader;
|
|
+import org.junit.Test;
|
|
+import org.junit.runner.RunWith;
|
|
+
|
|
+@RunWith(AndroidJUnit4.class)
|
|
+public class SvgResizeTest {
|
|
+ @Test
|
|
+ public void percentageFadeFollowsCanvasGrowthAndShrinkage() {
|
|
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
|
+ android.content.Context app = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
|
+ DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(app);
|
|
+ try {
|
|
+ SoLoader.init(app, OpenSourceMergedSoMapping.INSTANCE);
|
|
+ } catch (java.io.IOException exception) {
|
|
+ throw new RuntimeException(exception);
|
|
+ }
|
|
+ BridgeReactContext context = new DrawingContext(app);
|
|
+ SvgView svg = new SvgView(context);
|
|
+ LinearGradientView gradient = new LinearGradientView(context);
|
|
+ gradient.setName("fade");
|
|
+ gradient.setX1(new DynamicFromObject("0%"));
|
|
+ gradient.setY1(new DynamicFromObject("0%"));
|
|
+ gradient.setX2(new DynamicFromObject("0%"));
|
|
+ gradient.setY2(new DynamicFromObject("100%"));
|
|
+ gradient.setGradientUnits(0);
|
|
+ gradient.setGradient(JavaOnlyArray.of(0.0, Color.RED, 1.0, Color.TRANSPARENT));
|
|
+ svg.addView(gradient);
|
|
+ // Exercise recursive invalidation as well as the direct child case.
|
|
+ GroupView group = new GroupView(context);
|
|
+ RectView rectangle = new RectView(context);
|
|
+ rectangle.setX(new DynamicFromObject(0.0));
|
|
+ rectangle.setY(new DynamicFromObject(0.0));
|
|
+ rectangle.setWidth(new DynamicFromObject("100%"));
|
|
+ rectangle.setHeight(new DynamicFromObject("100%"));
|
|
+ rectangle.setFill(JavaOnlyMap.of("type", 1, "brushRef", "fade"));
|
|
+ group.addView(rectangle);
|
|
+ svg.addView(group);
|
|
+
|
|
+ assertFade(svg, 80, 60);
|
|
+ assertFade(svg, 160, 120);
|
|
+ assertFade(svg, 60, 40);
|
|
+ assertFade(svg, 160, 120);
|
|
+ });
|
|
+ }
|
|
+
|
|
+ // Pixel rendering needs a React context but does not send layout events to JavaScript.
|
|
+ private static final class DrawingContext extends BridgeReactContext
|
|
+ implements com.facebook.react.uimanager.events.EventDispatcherProvider {
|
|
+ DrawingContext(android.content.Context context) { super(context); }
|
|
+
|
|
+ @Override
|
|
+ public com.facebook.react.uimanager.events.EventDispatcher getEventDispatcher() { return null; }
|
|
+ }
|
|
+
|
|
+ private static void assertFade(SvgView svg, int width, int height) {
|
|
+ svg.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
|
|
+ View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
|
|
+ svg.layout(0, 0, width, height);
|
|
+ Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
|
+ svg.draw(new Canvas(image));
|
|
+ try {
|
|
+ int middle = Color.alpha(image.getPixel(width / 2, height / 2));
|
|
+ int right = Color.alpha(image.getPixel(width - 2, height / 2));
|
|
+ assertTrue("Gradient midpoint must follow canvas height at " + width + "x" + height
|
|
+ + "; alpha=" + middle, middle >= 120 && middle <= 135);
|
|
+ assertEquals("Fade must reach the right edge at " + width + "x" + height, middle, right);
|
|
+ assertTrue("Fade must end at the new bottom edge",
|
|
+ Color.alpha(image.getPixel(width / 2, height - 2)) < 20);
|
|
+ } finally {
|
|
+ image.recycle();
|
|
+ }
|
|
+ }
|
|
+}
|