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)
|
||||
}
|
||||
|
||||
#[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)]
|
||||
#[cold]
|
||||
pub fn error<'gc>(
|
||||
|
|
|
@ -111,6 +111,7 @@ pub struct SystemClasses<'gc> {
|
|||
pub verifyerror: ClassObject<'gc>,
|
||||
pub ioerror: ClassObject<'gc>,
|
||||
pub eoferror: ClassObject<'gc>,
|
||||
pub urierror: ClassObject<'gc>,
|
||||
pub error: ClassObject<'gc>,
|
||||
pub uncaughterrorevents: ClassObject<'gc>,
|
||||
pub statictext: ClassObject<'gc>,
|
||||
|
@ -219,6 +220,7 @@ impl<'gc> SystemClasses<'gc> {
|
|||
verifyerror: object,
|
||||
ioerror: object,
|
||||
eoferror: object,
|
||||
urierror: object,
|
||||
error: object,
|
||||
uncaughterrorevents: object,
|
||||
statictext: object,
|
||||
|
@ -520,6 +522,14 @@ pub fn load_player_globals<'gc>(
|
|||
toplevel::encode_uri_component,
|
||||
script,
|
||||
)?;
|
||||
function(activation, "", "decodeURI", toplevel::decode_uri, script)?;
|
||||
function(
|
||||
activation,
|
||||
"",
|
||||
"decodeURIComponent",
|
||||
toplevel::decode_uri_component,
|
||||
script,
|
||||
)?;
|
||||
function(activation, "", "unescape", toplevel::unescape, script)?;
|
||||
|
||||
avm2_system_class!(vector, activation, vector::create_class(activation), script);
|
||||
|
@ -606,6 +616,7 @@ fn load_playerglobal<'gc>(
|
|||
("", "RegExp", regexp),
|
||||
("", "ReferenceError", referenceerror),
|
||||
("", "TypeError", typeerror),
|
||||
("", "URIError", urierror),
|
||||
("", "VerifyError", verifyerror),
|
||||
("", "XML", xml),
|
||||
("", "XMLList", xml_list),
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
use ruffle_wstr::Units;
|
||||
|
||||
use crate::avm2::activation::Activation;
|
||||
use crate::avm2::error::{uri_error, Error};
|
||||
use crate::avm2::object::Object;
|
||||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use crate::string::{AvmString, WStr, WString};
|
||||
use crate::stub::Stub;
|
||||
use ruffle_wstr::Integer;
|
||||
|
@ -379,3 +379,134 @@ fn encode_utf8_with_exclusions<'gc>(
|
|||
|
||||
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