avm2: Implement encodeURI and encodeURIComponent

This commit is contained in:
Aaron Hill 2023-02-25 15:05:36 -06:00
parent e970174339
commit 7f58b92348
5 changed files with 158 additions and 28 deletions

View File

@ -468,6 +468,14 @@ pub fn load_player_globals<'gc>(
function(activation, "", "parseInt", toplevel::parse_int, script)?;
function(activation, "", "parseFloat", toplevel::parse_float, script)?;
function(activation, "", "escape", toplevel::escape, script)?;
function(activation, "", "encodeURI", toplevel::encode_uri, script)?;
function(
activation,
"",
"encodeURIComponent",
toplevel::encode_uri_component,
script,
)?;
avm2_system_class!(regexp, activation, regexp::create_class(activation), script);
avm2_system_class!(vector, activation, vector::create_class(activation), script);

View File

@ -1,5 +1,7 @@
//! Global scope built-ins
use ruffle_wstr::Units;
use crate::avm2::activation::Activation;
use crate::avm2::object::Object;
use crate::avm2::value::Value;
@ -7,6 +9,7 @@ use crate::avm2::Error;
use crate::string::{AvmString, WStr, WString};
use crate::stub::Stub;
use std::borrow::Cow;
use std::fmt::Write;
pub fn trace<'gc>(
activation: &mut Activation<'_, 'gc>,
@ -263,3 +266,68 @@ pub fn escape<'gc>(
Ok(AvmString::new(activation.context.gc_context, output).into())
}
pub fn encode_uri<'gc>(
activation: &mut Activation<'_, 'gc>,
_this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
encode_utf8_with_exclusions(
activation,
args,
// Characters that are not escaped, sourced from as3 docs
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@;/?:@&=+$,#-_.!~*'()",
)
}
pub fn encode_uri_component<'gc>(
activation: &mut Activation<'_, 'gc>,
_this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
encode_utf8_with_exclusions(
activation,
args,
// Characters that are not escaped, sourced from as3 docs
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.!~*'()",
)
}
fn encode_utf8_with_exclusions<'gc>(
activation: &mut Activation<'_, 'gc>,
args: &[Value<'gc>],
not_converted: &str,
) -> Result<Value<'gc>, Error<'gc>> {
let value = match args.first() {
None => return Ok("undefined".into()),
Some(Value::Undefined) => return Ok("null".into()),
Some(value) => value,
};
let mut output = String::new();
let input = value.coerce_to_string(activation)?;
let input_string = match input.units() {
// Latin-1 values map directly to unicode codepoints,
// so we can directly convert to a `char`
Units::Bytes(bytes) => bytes.iter().map(|b| *b as char).collect(),
Units::Wide(wide) => String::from_utf16_lossy(wide),
};
for x in input_string.chars() {
if not_converted.contains(x) {
output.push(x);
} else {
let mut bytes = [0; 4];
let utf8_bytes = x.encode_utf8(&mut bytes);
let mut encoded = String::new();
// Each byte in the utf-8 encoding is encoded as a hex value
for byte in utf8_bytes.bytes() {
write!(encoded, "%{x:02X}", x = byte).unwrap();
}
output.push_str(&encoded);
}
}
Ok(AvmString::new_utf8(activation.context.gc_context, output).into())
}

View File

@ -4,38 +4,44 @@
}
}
trace("// escape()");
trace(escape());
trace("");
import flash.utils.getDefinitionByName;
trace("// escape(undefined)");
trace(escape(undefined));
trace("");
var fns = ["escape", "encodeURI", "encodeURIComponent"];
for each (var fnName in fns) {
var fn = getDefinitionByName(fnName);
trace("// " + fnName + "()");
trace(fn());
trace("");
trace("// typeof(escape(undefined))");
trace(typeof(escape(undefined)));
trace("");
trace("// " + fnName + "(undefined)");
trace(fn(undefined));
trace("");
trace("// escape(null)");
trace(escape(null));
trace("");
trace("// typeof(" + fnName + "(undefined))");
trace(typeof(fn(undefined)));
trace("");
var input = "test";
trace("// escape(\"" + input + "\")");
trace(escape(input));
trace("");
trace("// " + fnName + "(null)");
trace(fn(null));
trace("");
var input = "!\"£$%^&*()1234567890qwertyuiop[]asdfghjkl;'#\zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:@~|ZXCVBNM<>?\u0010";
trace("// escape(\"" + input + "\")");
trace(escape(input));
trace("");
var input = "test";
trace("// " + fnName + "(\"" + input + "\")");
trace(fn(input));
trace("");
var input = "\x05";
trace("// escape(\"\\x05\")");
trace(escape(input));
trace("");
var input = "!\"£$%^&*()1234567890qwertyuiop[]asdfghjkl;'#\zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:@~|ZXCVBNM<>?\u0010";
trace("// " + fnName + "(\"" + input + "\")");
trace(fn(input));
trace("");
var input = "😭";
trace("// escape(\"" + input + "\")");
trace(escape(input));
trace("");
var input = "\x05";
trace("// " + fnName + "(\"\\x05\")");
trace(fn(input));
trace("");
var input = "😭";
trace("// " + fnName + "(\"" + input + "\")");
trace(fn(input));
trace("");
}

View File

@ -22,3 +22,51 @@ test
// escape("😭")
%uD83D%uDE2D
// encodeURI()
undefined
// encodeURI(undefined)
null
// typeof(encodeURI(undefined))
string
// encodeURI(null)
null
// encodeURI("test")
test
// encodeURI("!"£$%^&*()1234567890qwertyuiop[]asdfghjkl;'#zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:@~|ZXCVBNM<>?")
!%22%C2%A3$%25%5E&*()1234567890qwertyuiop%5B%5Dasdfghjkl;'#zxcvbnm,./QWERTYUIOP%7B%7DASDFGHJKL:@~%7CZXCVBNM%3C%3E?%10
// encodeURI("\x05")
%05
// encodeURI("😭")
%F0%9F%98%AD
// encodeURIComponent()
undefined
// encodeURIComponent(undefined)
null
// typeof(encodeURIComponent(undefined))
string
// encodeURIComponent(null)
null
// encodeURIComponent("test")
test
// encodeURIComponent("!"£$%^&*()1234567890qwertyuiop[]asdfghjkl;'#zxcvbnm,./QWERTYUIOP{}ASDFGHJKL:@~|ZXCVBNM<>?")
!%22%C2%A3%24%25%5E%26*()1234567890qwertyuiop%5B%5Dasdfghjkl%3B'%23zxcvbnm%2C.%2FQWERTYUIOP%7B%7DASDFGHJKL%3A%40~%7CZXCVBNM%3C%3E%3F%10
// encodeURIComponent("\x05")
%05
// encodeURIComponent("😭")
%F0%9F%98%AD