core: Strip BOM in URLLoader text data (#17513)

This commit is contained in:
Yaman Kassir 2024-09-15 19:39:52 +02:00 committed by GitHub
parent c378835038
commit c457fba947
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 103 additions and 64 deletions

View File

@ -183,39 +183,43 @@ pub fn read_utf<'gc>(
Ok(Value::Undefined)
}
pub fn strip_bom<'gc>(activation: &mut Activation<'_, 'gc>, mut bytes: &[u8]) -> AvmString<'gc> {
// UTF-8 BOM
if let Some(without_bom) = bytes.strip_prefix(&[0xEF, 0xBB, 0xBF]) {
bytes = without_bom;
// Little-endian UTF-16 BOM
} else if let Some(without_bom) = bytes.strip_prefix(&[0xFF, 0xFE]) {
let utf16_bytes: Vec<_> = without_bom
.chunks_exact(2)
.map(|pair| u16::from_le_bytes([pair[0], pair[1]]))
.collect();
return AvmString::new(
activation.context.gc_context,
WString::from_buf(utf16_bytes),
);
// Big-endian UTF-16 BOM
} else if let Some(without_bom) = bytes.strip_prefix(&[0xFE, 0xFF]) {
let utf16_bytes: Vec<_> = without_bom
.chunks_exact(2)
.map(|pair| u16::from_be_bytes([pair[0], pair[1]]))
.collect();
return AvmString::new(
activation.context.gc_context,
WString::from_buf(utf16_bytes),
);
}
AvmString::new_utf8_bytes(activation.context.gc_context, bytes)
}
pub fn to_string<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bytearray) = this.as_bytearray() {
let mut bytes = bytearray.bytes();
if let Some(without_bom) = bytes.strip_prefix(&[0xEF, 0xBB, 0xBF]) {
bytes = without_bom;
// Little-endian UTF-16 BOM
} else if let Some(without_bom) = bytes.strip_prefix(&[0xFF, 0xFE]) {
let utf16_bytes: Vec<_> = without_bom
.chunks_exact(2)
.map(|pair| u16::from_le_bytes([pair[0], pair[1]]))
.collect();
return Ok(AvmString::new(
activation.context.gc_context,
WString::from_buf(utf16_bytes),
)
.into());
// Big-endian UTF-16 BOM
} else if let Some(without_bom) = bytes.strip_prefix(&[0xFE, 0xFF]) {
let utf16_bytes: Vec<_> = without_bom
.chunks_exact(2)
.map(|pair| u16::from_be_bytes([pair[0], pair[1]]))
.collect();
return Ok(AvmString::new(
activation.context.gc_context,
WString::from_buf(utf16_bytes),
)
.into());
}
return Ok(AvmString::new_utf8_bytes(activation.context.gc_context, bytes).into());
return Ok(strip_bom(activation, bytearray.bytes()).into());
}
Ok(Value::Undefined)

View File

@ -5,13 +5,14 @@ use crate::avm1::{Attribute, Avm1};
use crate::avm1::{ExecutionReason, NativeObject};
use crate::avm1::{Object, SoundObject, TObject, Value};
use crate::avm2::bytearray::ByteArrayStorage;
use crate::avm2::globals::flash::utils::byte_array::strip_bom;
use crate::avm2::object::{
ByteArrayObject, EventObject as Avm2EventObject, FileReferenceObject, LoaderStream,
TObject as _,
};
use crate::avm2::{
Activation as Avm2Activation, Avm2, BitmapDataObject, Domain as Avm2Domain,
Object as Avm2Object, Value as Avm2Value,
Object as Avm2Object,
};
use crate::backend::navigator::{ErrorResponse, OwnedFuture, Request, SuccessResponse};
use crate::backend::ui::DialogResultFuture;
@ -1560,8 +1561,7 @@ impl<'gc> Loader<'gc> {
if body.is_empty() {
None
} else {
let string_value =
AvmString::new_utf8_bytes(activation.context.gc_context, &body);
let string_value = strip_bom(activation, &body);
activation
.avm2()
@ -1576,10 +1576,7 @@ impl<'gc> Loader<'gc> {
tracing::warn!("Invalid URLLoaderDataFormat: {}", data_format);
}
let string_value =
AvmString::new_utf8_bytes(activation.context.gc_context, &body);
Some(Avm2Value::String(string_value))
Some(strip_bom(activation, &body).into())
};
if let Some(data_object) = data_object {

58
tests/tests/swfs/avm2/bom/Test.as vendored Executable file
View File

@ -0,0 +1,58 @@
package {
import flash.display.MovieClip;
import flash.utils.ByteArray;
import flash.utils.Endian;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.IOErrorEvent;
public class Test extends MovieClip {
public function Test() {
var utf8 = new ByteArray();
var utf8Bytes = [0xef, 0xbb, 0xbf, 0x46, 0x78];
for each (var byte in utf8Bytes) {
utf8.writeByte(byte);
}
trace("ByteArray UTF-8: " + utf8);
var utf16le = new ByteArray();
var utf16leBytes = [0xff, 0xfe, 0x0, 0x22, 0x78, 0x0];
for each (var byte in utf16leBytes) {
utf16le.writeByte(byte);
}
trace("ByteArray UTF-16 Little endian: " + utf16le);
var utf16be = new ByteArray();
var utf16beBytes = [0xfe, 0xff, 0x22, 0x0, 0x0, 0x78];
for each (var byte in utf16beBytes) {
utf16be.writeByte(byte);
}
trace("ByteArray UTF-16 Big endian: " + utf16be);
var files = ["utf8", "utf16le", "utf16be", "utf8", "utf16le", "utf16be"];
var current = files.shift();
var urlLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, function(event:IOErrorEvent):void {
trace("URLLoader IOError: " + event);
});
urlLoader.addEventListener(Event.COMPLETE, function(event:Event):void {
trace("URLLoader dataFormat=" + urlLoader.dataFormat + " " + current + ": " + event.target.data);
if (files.length > 0) {
if (files.length == 3) {
urlLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
}
current = files.shift();
urlLoader.load(new URLRequest(current));
}
});
urlLoader.load(new URLRequest(current));
}
}
}

9
tests/tests/swfs/avm2/bom/output.txt vendored Normal file
View File

@ -0,0 +1,9 @@
ByteArray UTF-8: Fx
ByteArray UTF-16 Little endian: ∀x
ByteArray UTF-16 Big endian: ∀x
URLLoader dataFormat=text utf8: lastName=Jones&firstName=Tom
URLLoader dataFormat=text utf16le: lastName=Jo∀nes&firstName=Tom
URLLoader dataFormat=text utf16be: lastName=Jo∀nes&firstName=Tom
URLLoader dataFormat=variables utf8: firstName=Tom&lastName=Jones
URLLoader dataFormat=variables utf16le: firstName=Tom&lastName=Jo%E2%88%80nes
URLLoader dataFormat=variables utf16be: firstName=Tom&lastName=Jo%E2%88%80nes

BIN
tests/tests/swfs/avm2/bom/test.swf vendored Executable file

Binary file not shown.

BIN
tests/tests/swfs/avm2/bom/utf16be vendored Normal file

Binary file not shown.

BIN
tests/tests/swfs/avm2/bom/utf16le vendored Normal file

Binary file not shown.

1
tests/tests/swfs/avm2/bom/utf8 vendored Normal file
View File

@ -0,0 +1 @@
lastName=Jones&firstName=Tom

View File

@ -1,28 +0,0 @@
package {
import flash.display.MovieClip;
import flash.utils.ByteArray;
import flash.utils.Endian;
public class Test extends MovieClip {
public function Test() {
var le = new ByteArray();
var leBytes = [0xff, 0xfe, 0x0, 0x22, 0x78, 0x0];
for each (var byte in leBytes) {
le.writeByte(byte);
}
trace("Little endian: " + le);
var be = new ByteArray();
var beBytes = [0xfe, 0xff, 0x22, 0x0, 0x0, 0x78];
for each (var byte in beBytes) {
be.writeByte(byte);
}
trace("Big endian: " + be);
}
}
}

View File

@ -1,2 +0,0 @@
Little endian: ∀x
Big endian: ∀x

Binary file not shown.

Binary file not shown.