avm2: implement decodeURI
This commit is contained in:
parent
5a18a409f7
commit
f0a8e50be1
|
@ -210,6 +210,17 @@ pub fn eof_error<'gc>(
|
||||||
error_constructor(activation, class, message, code)
|
error_constructor(activation, class, message, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
#[cold]
|
||||||
|
pub fn uri_error<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
message: &str,
|
||||||
|
code: u32,
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
let class = activation.avm2().classes().urierror;
|
||||||
|
error_constructor(activation, class, message, code)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
#[cold]
|
#[cold]
|
||||||
pub fn error<'gc>(
|
pub fn error<'gc>(
|
||||||
|
|
|
@ -111,6 +111,7 @@ pub struct SystemClasses<'gc> {
|
||||||
pub verifyerror: ClassObject<'gc>,
|
pub verifyerror: ClassObject<'gc>,
|
||||||
pub ioerror: ClassObject<'gc>,
|
pub ioerror: ClassObject<'gc>,
|
||||||
pub eoferror: ClassObject<'gc>,
|
pub eoferror: ClassObject<'gc>,
|
||||||
|
pub urierror: ClassObject<'gc>,
|
||||||
pub error: ClassObject<'gc>,
|
pub error: ClassObject<'gc>,
|
||||||
pub uncaughterrorevents: ClassObject<'gc>,
|
pub uncaughterrorevents: ClassObject<'gc>,
|
||||||
pub statictext: ClassObject<'gc>,
|
pub statictext: ClassObject<'gc>,
|
||||||
|
@ -219,6 +220,7 @@ impl<'gc> SystemClasses<'gc> {
|
||||||
verifyerror: object,
|
verifyerror: object,
|
||||||
ioerror: object,
|
ioerror: object,
|
||||||
eoferror: object,
|
eoferror: object,
|
||||||
|
urierror: object,
|
||||||
error: object,
|
error: object,
|
||||||
uncaughterrorevents: object,
|
uncaughterrorevents: object,
|
||||||
statictext: object,
|
statictext: object,
|
||||||
|
@ -520,6 +522,14 @@ pub fn load_player_globals<'gc>(
|
||||||
toplevel::encode_uri_component,
|
toplevel::encode_uri_component,
|
||||||
script,
|
script,
|
||||||
)?;
|
)?;
|
||||||
|
function(activation, "", "decodeURI", toplevel::decode_uri, script)?;
|
||||||
|
function(
|
||||||
|
activation,
|
||||||
|
"",
|
||||||
|
"decodeURIComponent",
|
||||||
|
toplevel::decode_uri_component,
|
||||||
|
script,
|
||||||
|
)?;
|
||||||
function(activation, "", "unescape", toplevel::unescape, script)?;
|
function(activation, "", "unescape", toplevel::unescape, script)?;
|
||||||
|
|
||||||
avm2_system_class!(vector, activation, vector::create_class(activation), script);
|
avm2_system_class!(vector, activation, vector::create_class(activation), script);
|
||||||
|
@ -606,6 +616,7 @@ fn load_playerglobal<'gc>(
|
||||||
("", "RegExp", regexp),
|
("", "RegExp", regexp),
|
||||||
("", "ReferenceError", referenceerror),
|
("", "ReferenceError", referenceerror),
|
||||||
("", "TypeError", typeerror),
|
("", "TypeError", typeerror),
|
||||||
|
("", "URIError", urierror),
|
||||||
("", "VerifyError", verifyerror),
|
("", "VerifyError", verifyerror),
|
||||||
("", "XML", xml),
|
("", "XML", xml),
|
||||||
("", "XMLList", xml_list),
|
("", "XMLList", xml_list),
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
use ruffle_wstr::Units;
|
use ruffle_wstr::Units;
|
||||||
|
|
||||||
use crate::avm2::activation::Activation;
|
use crate::avm2::activation::Activation;
|
||||||
|
use crate::avm2::error::{uri_error, Error};
|
||||||
use crate::avm2::object::Object;
|
use crate::avm2::object::Object;
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
use crate::avm2::Error;
|
|
||||||
use crate::string::{AvmString, WStr, WString};
|
use crate::string::{AvmString, WStr, WString};
|
||||||
use crate::stub::Stub;
|
use crate::stub::Stub;
|
||||||
use ruffle_wstr::Integer;
|
use ruffle_wstr::Integer;
|
||||||
|
@ -379,3 +379,134 @@ fn encode_utf8_with_exclusions<'gc>(
|
||||||
|
|
||||||
Ok(AvmString::new_utf8(activation.context.gc_context, output).into())
|
Ok(AvmString::new_utf8(activation.context.gc_context, output).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn decode_uri<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
_this: Option<Object<'gc>>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
decode(
|
||||||
|
activation,
|
||||||
|
args,
|
||||||
|
// Characters that are reserved, sourced from as3 docs
|
||||||
|
"#$&+,/:;=?@",
|
||||||
|
"decodeURI",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_uri_component<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
_this: Option<Object<'gc>>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
|
decode(activation, args, "", "decodeURIComponent")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_percent<I>(chars: &mut I) -> Option<u8>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = Result<char, std::char::DecodeUtf16Error>>,
|
||||||
|
{
|
||||||
|
let high = chars.next()?.ok()?.to_digit(16)?;
|
||||||
|
let low = chars.next()?.ok()?.to_digit(16)?;
|
||||||
|
Some(low as u8 | ((high as u8) << 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
// code derived from flash.utils.unescapeMultiByte
|
||||||
|
// FIXME: support bugzilla #538107
|
||||||
|
fn decode<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc>,
|
||||||
|
args: &[Value<'gc>],
|
||||||
|
reserved_set: &str,
|
||||||
|
func_name: &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.coerce_to_string(activation)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut output = WString::new();
|
||||||
|
let mut chars = value.chars();
|
||||||
|
let mut bytes = Vec::with_capacity(4);
|
||||||
|
|
||||||
|
while let Some(c) = chars.next() {
|
||||||
|
let Ok(c) = c else {
|
||||||
|
return Err(Error::AvmError(uri_error(
|
||||||
|
activation,
|
||||||
|
&format!("Error #1052: Invalid URI passed to {func_name} function."),
|
||||||
|
1052,
|
||||||
|
)?));
|
||||||
|
};
|
||||||
|
|
||||||
|
if c != '%' {
|
||||||
|
output.push_char(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes.clear();
|
||||||
|
let Some(byte) = handle_percent(&mut chars) else {
|
||||||
|
return Err(Error::AvmError(uri_error(
|
||||||
|
activation,
|
||||||
|
&format!("Error #1052: Invalid URI passed to {func_name} function."),
|
||||||
|
1052,
|
||||||
|
)?));
|
||||||
|
};
|
||||||
|
bytes.push(byte);
|
||||||
|
if (byte & 0x80) != 0 {
|
||||||
|
let n = byte.leading_ones();
|
||||||
|
|
||||||
|
if n == 1 || n > 4 {
|
||||||
|
return Err(Error::AvmError(uri_error(
|
||||||
|
activation,
|
||||||
|
&format!("Error #1052: Invalid URI passed to {func_name} function."),
|
||||||
|
1052,
|
||||||
|
)?));
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 1..n {
|
||||||
|
if chars.next() != Some(Ok('%')) {
|
||||||
|
return Err(Error::AvmError(uri_error(
|
||||||
|
activation,
|
||||||
|
&format!("Error #1052: Invalid URI passed to {func_name} function."),
|
||||||
|
1052,
|
||||||
|
)?));
|
||||||
|
}; // consume %
|
||||||
|
|
||||||
|
let Some(byte) = handle_percent(&mut chars) else {
|
||||||
|
return Err(Error::AvmError(uri_error(
|
||||||
|
activation,
|
||||||
|
&format!("Error #1052: Invalid URI passed to {func_name} function."),
|
||||||
|
1052,
|
||||||
|
)?));
|
||||||
|
};
|
||||||
|
|
||||||
|
if (byte & 0xC0) != 0x80 {
|
||||||
|
return Err(Error::AvmError(uri_error(
|
||||||
|
activation,
|
||||||
|
&format!("Error #1052: Invalid URI passed to {func_name} function."),
|
||||||
|
1052,
|
||||||
|
)?));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes.push(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Ok(decoded) = std::str::from_utf8(&bytes) else {
|
||||||
|
return Err(Error::AvmError(uri_error(
|
||||||
|
activation,
|
||||||
|
&format!("Error #1052: Invalid URI passed to {func_name} function."),
|
||||||
|
1052,
|
||||||
|
)?));
|
||||||
|
};
|
||||||
|
if reserved_set.contains(decoded) {
|
||||||
|
for byte in &bytes {
|
||||||
|
write!(output, "%{x:02X}", x = byte).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output.push_utf8(decoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(AvmString::new(activation.context.gc_context, output).into())
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package {
|
||||||
|
|
||||||
|
public class Test {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import flash.utils.getDefinitionByName;
|
||||||
|
|
||||||
|
var fns = ["decodeURI", "decodeURIComponent"];
|
||||||
|
for each (var fnName in fns) {
|
||||||
|
var fn = getDefinitionByName(fnName);
|
||||||
|
trace("// " + fnName + "()");
|
||||||
|
trace(fn());
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
trace("// " + fnName + "(undefined)");
|
||||||
|
trace(fn(undefined));
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
trace("// typeof(" + fnName + "(undefined))");
|
||||||
|
trace(typeof(fn(undefined)));
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
trace("// " + fnName + "(null)");
|
||||||
|
trace(fn(null));
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
var input = "test";
|
||||||
|
trace("// " + fnName + "(\"" + input + "\")");
|
||||||
|
trace(fn(input));
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
var input = "%3A";
|
||||||
|
trace("// " + fnName + "(\"" + input + "\")");
|
||||||
|
trace(fn(input));
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
var input = "%E0%A4%A";
|
||||||
|
trace("// " + fnName + "(\"" + input + "\")");
|
||||||
|
try {
|
||||||
|
trace(fn(input));
|
||||||
|
} catch (e) {
|
||||||
|
trace(e);
|
||||||
|
}
|
||||||
|
trace("");
|
||||||
|
var input = "%FFabcd";
|
||||||
|
trace("// " + fnName + "(\"" + input + "\")");
|
||||||
|
try {
|
||||||
|
trace(fn(input));
|
||||||
|
} catch (e) {
|
||||||
|
trace(e);
|
||||||
|
}
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
var src:String = String.fromCharCode(0xD842, 0xDF9F);
|
||||||
|
var input = encodeURIComponent(src);
|
||||||
|
trace("// " + fnName + "(\"" + input + "\")");
|
||||||
|
try {
|
||||||
|
trace(fn(input));
|
||||||
|
} catch (e) {
|
||||||
|
trace(e);
|
||||||
|
}
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
|
||||||
|
var input = "\x05";
|
||||||
|
trace("// " + fnName + "(\"\\x05\")");
|
||||||
|
trace(fn(input));
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
var input = "😭";
|
||||||
|
trace("// " + fnName + "(\"" + input + "\")");
|
||||||
|
trace(fn(input));
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
var input = "~!%40%23%24%25%5E%26*()_%2B%5B%5D%5C%7B%7D%7C%3B'%2C.%2F%3C%3E%3F";
|
||||||
|
trace("// " + fnName + "(\"" + input + "\")");
|
||||||
|
trace(fn(input));
|
||||||
|
trace("");
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// decodeURI()
|
||||||
|
undefined
|
||||||
|
|
||||||
|
// decodeURI(undefined)
|
||||||
|
null
|
||||||
|
|
||||||
|
// typeof(decodeURI(undefined))
|
||||||
|
string
|
||||||
|
|
||||||
|
// decodeURI(null)
|
||||||
|
null
|
||||||
|
|
||||||
|
// decodeURI("test")
|
||||||
|
test
|
||||||
|
|
||||||
|
// decodeURI("%3A")
|
||||||
|
%3A
|
||||||
|
|
||||||
|
// decodeURI("%E0%A4%A")
|
||||||
|
URIError: Error #1052: Invalid URI passed to decodeURI function.
|
||||||
|
|
||||||
|
// decodeURI("%FFabcd")
|
||||||
|
URIError: Error #1052: Invalid URI passed to decodeURI function.
|
||||||
|
|
||||||
|
// decodeURI("%F0%A0%AE%9F")
|
||||||
|
𠮟
|
||||||
|
|
||||||
|
// decodeURI("\x05")
|
||||||
|
|
||||||
|
|
||||||
|
// decodeURI("😭")
|
||||||
|
😭
|
||||||
|
|
||||||
|
// decodeURI("~!%40%23%24%25%5E%26*()_%2B%5B%5D%5C%7B%7D%7C%3B'%2C.%2F%3C%3E%3F")
|
||||||
|
~!%40%23%24%^%26*()_%2B[]\{}|%3B'%2C.%2F<>%3F
|
||||||
|
|
||||||
|
// decodeURIComponent()
|
||||||
|
undefined
|
||||||
|
|
||||||
|
// decodeURIComponent(undefined)
|
||||||
|
null
|
||||||
|
|
||||||
|
// typeof(decodeURIComponent(undefined))
|
||||||
|
string
|
||||||
|
|
||||||
|
// decodeURIComponent(null)
|
||||||
|
null
|
||||||
|
|
||||||
|
// decodeURIComponent("test")
|
||||||
|
test
|
||||||
|
|
||||||
|
// decodeURIComponent("%3A")
|
||||||
|
:
|
||||||
|
|
||||||
|
// decodeURIComponent("%E0%A4%A")
|
||||||
|
URIError: Error #1052: Invalid URI passed to decodeURIComponent function.
|
||||||
|
|
||||||
|
// decodeURIComponent("%FFabcd")
|
||||||
|
URIError: Error #1052: Invalid URI passed to decodeURIComponent function.
|
||||||
|
|
||||||
|
// decodeURIComponent("%F0%A0%AE%9F")
|
||||||
|
𠮟
|
||||||
|
|
||||||
|
// decodeURIComponent("\x05")
|
||||||
|
|
||||||
|
|
||||||
|
// decodeURIComponent("😭")
|
||||||
|
😭
|
||||||
|
|
||||||
|
// decodeURIComponent("~!%40%23%24%25%5E%26*()_%2B%5B%5D%5C%7B%7D%7C%3B'%2C.%2F%3C%3E%3F")
|
||||||
|
~!@#$%^&*()_+[]\{}|;',./<>?
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
num_frames = 1
|
Loading…
Reference in New Issue