avm2: Implement AIR playerglobal versioning

This builds on our existing playerglobal versioning support
to add in AIR versioning. We closely follow the avmplus implementation:

* When an SWF is loaded, we chose either a FlashPlayer or AIR
APIVersion for its SWF version, based on our configured player runtime.
* When loading playerglobals, we look at the player runtime. In AIR
  mode, we map FlashPlayer-versioned definitions to the closest AIR
  version. This ensures that all runtime APIVersions are in the
  same series (either AIR or FlashPlayer). In FlashPlayer mode,
  all AIR-versioned definitions get mapped to VM_INTERNAL, hiding
  them from user code.

Part of our existing api versioning code was implemented incorrectly.
Within playerglobals, we need to treat all unmarked namespaces as
VM_INTERNAL - this allows things like playerglobal script
initializer "initproperty" opcodes to see any VM_INTERNAL AIR
definitions (when we run under FlashPlayer mode). Previously, we
were using AllVersions, which would result in those VM_INTERNAL
definitions being hidden from other playerglobal code, which is
not correct.

Using this support, I've added a stub for the AIR-only
'flash.net.DatagramSocket'. I've also extended the test framework
with a new 'player_options.runtime' config option, which can be
set to "AIR" or "FlashPlayer" to configure the test runtime mode.

I've also added two new tests:
* 'air_hidden_lookup' runs under the FlashPlayer runtime, and verifies
  that a list of classes (currently just "DatagramSocket" are
  inacessible).
* 'air_datagram_socket', which uses `player_options.runtime = "AIR"`
  to construct an instance of `flash.net.DatagramSocket`. We can
  extend this test once we implement more of `DatagramSocket`

With this commit, we have all of the needed infrastructure to start
implementing and testing AIR-only classes and methods.
This commit is contained in:
Aaron Hill 2023-11-25 19:19:44 -05:00
parent 375c99e601
commit 287ca8801a
23 changed files with 267 additions and 40 deletions

1
Cargo.lock generated
View File

@ -4263,6 +4263,7 @@ dependencies = [
"egui",
"egui_extras",
"encoding_rs",
"enum-map",
"enumset",
"flash-lso",
"flate2",

View File

@ -58,6 +58,7 @@ flv-rs = { path = "../flv" }
async-channel = "2.1.1"
jpegxr = { git = "https://github.com/ruffle-rs/jpegxr", branch = "ruffle", optional = true }
image = { version = "0.24.7", default-features = false, features = ["tiff", "dxt"] }
enum-map = "2.7.3"
[target.'cfg(not(target_family = "wasm"))'.dependencies.futures]
version = "0.3.29"

View File

@ -11,6 +11,7 @@ use crate::context::{GcContext, UpdateContext};
use crate::display_object::{DisplayObject, DisplayObjectWeak, TDisplayObject};
use crate::string::AvmString;
use crate::tag_utils::SwfMovie;
use crate::PlayerRuntime;
use fnv::FnvHashMap;
use gc_arena::{Collect, GcCell, Mutation};
@ -92,6 +93,10 @@ pub struct Avm2<'gc> {
/// The Flash Player version we're emulating.
player_version: u8,
/// The player runtime we're emulating
#[collect(require_static)]
pub player_runtime: PlayerRuntime,
/// Values currently present on the operand stack.
stack: Vec<Value<'gc>>,
@ -180,7 +185,11 @@ pub struct Avm2<'gc> {
impl<'gc> Avm2<'gc> {
/// Construct a new AVM interpreter.
pub fn new(context: &mut GcContext<'_, 'gc>, player_version: u8) -> Self {
pub fn new(
context: &mut GcContext<'_, 'gc>,
player_version: u8,
player_runtime: PlayerRuntime,
) -> Self {
let playerglobals_domain = Domain::uninitialized_domain(context.gc_context, None);
let stage_domain =
Domain::uninitialized_domain(context.gc_context, Some(playerglobals_domain));
@ -191,6 +200,7 @@ impl<'gc> Avm2<'gc> {
Self {
player_version,
player_runtime,
stack: Vec::new(),
scope_stack: Vec::new(),
call_stack: GcCell::new(context.gc_context, CallStack::new()),

View File

@ -1,7 +1,10 @@
use std::sync::OnceLock;
use crate::PlayerRuntime;
use enum_map::{enum_map, Enum, EnumMap};
// Based on https://github.com/adobe/avmplus/blob/master/core/api-versions.h
// FIXME - if we ever add in AIR emulation, then implement the 'version series' logic.
// For now, it's fine to just compare Flash player version against air versions directly.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, FromPrimitive)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, FromPrimitive, Enum)]
#[allow(non_camel_case_types)]
pub enum ApiVersion {
AllVersions = 0,
@ -59,37 +62,142 @@ pub enum ApiVersion {
VM_INTERNAL = 52,
}
static TRANSFER_TABLE: OnceLock<EnumMap<ApiVersion, (ApiVersion, ApiVersion)>> = OnceLock::new();
impl ApiVersion {
pub fn from_swf_version(val: u8) -> Option<ApiVersion> {
match val {
pub fn to_valid_playerglobals_version(self, runtime: PlayerRuntime) -> ApiVersion {
// This maps an ApiVersion from our playerglobals SWF to the closest valid version,
// based on the active runtime.
//
// If our runtime is AIR, then we leave ApiVersion::AIR_* unchanged, and map
// ApiVersion::FP_* to the closest AIR version. This has the effect of exposing
// API versioned with an AIR-specific version, and also exposing all of the normal FP
// APIs that were included in that AIR release.
//
// If our runtime is FlashPlayer, then we leave ApiVersion::FP_* unchanged, and
// map ApiVersion::AIR_* to VM_INTERNAL. This hides all AIR-specific APIs when
// running in FlashPlayer.
//
// See https://github.com/adobe/avmplus/blob/858d034a3bd3a54d9b70909386435cf4aec81d21/core/api-versions.cpp#L63
let active_series = TRANSFER_TABLE.get_or_init(|| {
enum_map! {
ApiVersion::AllVersions => (ApiVersion::AllVersions, ApiVersion::AllVersions),
ApiVersion::AIR_1_0 => (ApiVersion::AIR_1_0, ApiVersion::VM_INTERNAL),
ApiVersion::FP_10_0 => (ApiVersion::AIR_1_5, ApiVersion::FP_10_0),
ApiVersion::AIR_1_5 => (ApiVersion::AIR_1_5, ApiVersion::VM_INTERNAL),
ApiVersion::AIR_1_5_1 => (ApiVersion::AIR_1_5_1, ApiVersion::VM_INTERNAL),
ApiVersion::FP_10_0_32 => (ApiVersion::AIR_1_5_2, ApiVersion::FP_10_0_32),
ApiVersion::AIR_1_5_2 => (ApiVersion::AIR_1_5_2, ApiVersion::VM_INTERNAL),
ApiVersion::FP_10_1 => (ApiVersion::AIR_2_0, ApiVersion::FP_10_1),
ApiVersion::AIR_2_0 => (ApiVersion::AIR_2_0, ApiVersion::VM_INTERNAL),
ApiVersion::AIR_2_5 => (ApiVersion::AIR_2_5, ApiVersion::VM_INTERNAL),
ApiVersion::FP_10_2 => (ApiVersion::AIR_2_6, ApiVersion::FP_10_2),
ApiVersion::AIR_2_6 => (ApiVersion::AIR_2_6, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_12 => (ApiVersion::SWF_12, ApiVersion::SWF_12),
ApiVersion::AIR_2_7 => (ApiVersion::AIR_2_7, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_13 => (ApiVersion::AIR_3_0, ApiVersion::SWF_13),
ApiVersion::AIR_3_0 => (ApiVersion::AIR_3_0, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_14 => (ApiVersion::AIR_3_1, ApiVersion::SWF_14),
ApiVersion::AIR_3_1 => (ApiVersion::AIR_3_1, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_15 => (ApiVersion::AIR_3_2, ApiVersion::SWF_15),
ApiVersion::AIR_3_2 => (ApiVersion::AIR_3_2, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_16 => (ApiVersion::AIR_3_3, ApiVersion::SWF_16),
ApiVersion::AIR_3_3 => (ApiVersion::AIR_3_3, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_17 => (ApiVersion::AIR_3_4, ApiVersion::SWF_17),
ApiVersion::AIR_3_4 => (ApiVersion::AIR_3_4, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_18 => (ApiVersion::AIR_3_5, ApiVersion::SWF_18),
ApiVersion::AIR_3_5 => (ApiVersion::AIR_3_5, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_19 => (ApiVersion::AIR_3_6, ApiVersion::SWF_19),
ApiVersion::AIR_3_6 => (ApiVersion::AIR_3_6, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_20 => (ApiVersion::AIR_3_7, ApiVersion::SWF_20),
ApiVersion::AIR_3_7 => (ApiVersion::AIR_3_7, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_21 => (ApiVersion::AIR_3_8, ApiVersion::SWF_21),
ApiVersion::AIR_3_8 => (ApiVersion::AIR_3_8, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_22 => (ApiVersion::AIR_3_9, ApiVersion::SWF_22),
ApiVersion::AIR_3_9 => (ApiVersion::AIR_3_9, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_23 => (ApiVersion::AIR_4_0, ApiVersion::SWF_23),
ApiVersion::AIR_4_0 => (ApiVersion::AIR_4_0, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_24 => (ApiVersion::AIR_13_0, ApiVersion::SWF_24),
ApiVersion::AIR_13_0 => (ApiVersion::AIR_13_0, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_25 => (ApiVersion::AIR_14_0, ApiVersion::SWF_25),
ApiVersion::AIR_14_0 => (ApiVersion::AIR_14_0, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_26 => (ApiVersion::AIR_15_0, ApiVersion::SWF_26),
ApiVersion::AIR_15_0 => (ApiVersion::AIR_15_0, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_27 => (ApiVersion::AIR_16_0, ApiVersion::SWF_27),
ApiVersion::AIR_16_0 => (ApiVersion::AIR_16_0, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_28 => (ApiVersion::AIR_17_0, ApiVersion::SWF_28),
ApiVersion::AIR_17_0 => (ApiVersion::AIR_17_0, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_29 => (ApiVersion::AIR_18_0, ApiVersion::SWF_29),
ApiVersion::AIR_18_0 => (ApiVersion::AIR_18_0, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_30 => (ApiVersion::AIR_19_0, ApiVersion::SWF_30),
ApiVersion::AIR_19_0 => (ApiVersion::AIR_19_0, ApiVersion::VM_INTERNAL),
ApiVersion::SWF_31 => (ApiVersion::AIR_20_0, ApiVersion::SWF_31),
ApiVersion::AIR_20_0 => (ApiVersion::AIR_20_0, ApiVersion::VM_INTERNAL),
ApiVersion::VM_INTERNAL => (ApiVersion::VM_INTERNAL, ApiVersion::VM_INTERNAL),
}
})[self];
match runtime {
PlayerRuntime::AIR => active_series.0,
PlayerRuntime::FlashPlayer => active_series.1,
}
}
pub fn from_swf_version(val: u8, runtime: PlayerRuntime) -> Option<ApiVersion> {
// Based on this table: https://github.com/ruffle-rs/ruffle/wiki/SWF-version-chart
match (val, runtime) {
// There's no specific entry for SWF 9 in avmplus,
// so map it to the lowest entry.
9 => Some(ApiVersion::AllVersions),
10 => Some(ApiVersion::FP_10_1),
11 => Some(ApiVersion::FP_10_2),
12 => Some(ApiVersion::SWF_12),
13 => Some(ApiVersion::SWF_13),
14 => Some(ApiVersion::SWF_14),
15 => Some(ApiVersion::SWF_15),
16 => Some(ApiVersion::SWF_16),
17 => Some(ApiVersion::SWF_17),
18 => Some(ApiVersion::SWF_18),
19 => Some(ApiVersion::SWF_19),
20 => Some(ApiVersion::SWF_20),
21 => Some(ApiVersion::SWF_21),
22 => Some(ApiVersion::SWF_22),
23 => Some(ApiVersion::SWF_23),
24 => Some(ApiVersion::SWF_24),
25 => Some(ApiVersion::SWF_25),
26 => Some(ApiVersion::SWF_26),
27 => Some(ApiVersion::SWF_27),
28 => Some(ApiVersion::SWF_28),
29 => Some(ApiVersion::SWF_29),
30 => Some(ApiVersion::SWF_30),
31 => Some(ApiVersion::SWF_31),
(9, _) => Some(ApiVersion::AllVersions),
(10, PlayerRuntime::FlashPlayer) => Some(ApiVersion::FP_10_1),
(10, PlayerRuntime::AIR) => Some(ApiVersion::AIR_2_0),
(11, PlayerRuntime::FlashPlayer) => Some(ApiVersion::FP_10_2),
(11, PlayerRuntime::AIR) => Some(ApiVersion::AIR_2_6),
(12, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_12),
(12, PlayerRuntime::AIR) => Some(ApiVersion::AIR_2_7),
(13, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_13),
(13, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_0),
(14, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_14),
(14, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_1),
(15, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_15),
(15, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_2),
(16, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_16),
(16, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_3),
(17, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_17),
(17, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_4),
(18, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_18),
(18, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_5),
(19, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_19),
(19, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_6),
(20, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_20),
(20, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_7),
(21, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_21),
(21, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_8),
(22, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_22),
(22, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_9),
(23, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_23),
(23, PlayerRuntime::AIR) => Some(ApiVersion::AIR_4_0),
(24, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_24),
(24, PlayerRuntime::AIR) => Some(ApiVersion::AIR_13_0),
(25, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_25),
(25, PlayerRuntime::AIR) => Some(ApiVersion::AIR_14_0),
(26, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_26),
(26, PlayerRuntime::AIR) => Some(ApiVersion::AIR_15_0),
(27, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_27),
(27, PlayerRuntime::AIR) => Some(ApiVersion::AIR_16_0),
(28, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_28),
(28, PlayerRuntime::AIR) => Some(ApiVersion::AIR_17_0),
(29, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_29),
(29, PlayerRuntime::AIR) => Some(ApiVersion::AIR_18_0),
(30, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_30),
(30, PlayerRuntime::AIR) => Some(ApiVersion::AIR_19_0),
(31, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_31),
(31, PlayerRuntime::AIR) => Some(ApiVersion::AIR_20_0),
// We haven't yet created entries from higher versions - just map them
// to the highest non-VM_INTERNAL version.
_ if val > 31 => Some(ApiVersion::SWF_31),
(32.., PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_31),
(32.., PlayerRuntime::AIR) => Some(ApiVersion::AIR_20_0),
_ => None,
}
}

View File

@ -702,7 +702,7 @@ fn load_playerglobal<'gc>(
// Lookup with the highest version, so we we see all defined classes here
let ns = Namespace::package($package, ApiVersion::VM_INTERNAL, &mut activation.borrow_gc());
let name = QName::new(ns, $class_name);
let class_object = activation.domain().get_defined_value(activation, name)?;
let class_object = activation.domain().get_defined_value(activation, name).unwrap_or_else(|e| panic!("Failed to lookup {name:?}: {e:?}"));
let class_object = class_object.as_object().unwrap().as_class_object().unwrap();
let sc = activation.avm2().system_classes.as_mut().unwrap();
sc.$field = class_object;

View File

@ -0,0 +1,6 @@
package flash.net {
[API("668")] // AIR 2.0
public class DatagramSocket {
}
}

View File

@ -254,6 +254,7 @@ include "flash/media/VideoStreamSettings.as"
include "flash/external/ExternalInterface.as"
include "flash/net.as"
include "flash/net/DatagramSocket.as"
include "flash/net/FileFilter.as"
include "flash/net/FileReference.as"
include "flash/net/FileReferenceList.as"

View File

@ -107,9 +107,38 @@ impl<'gc> Namespace<'gc> {
)));
}
// FIXME - what other versioned urls are there?
let is_versioned_url = |url: AvmAtom<'gc>| url.as_wstr().is_empty();
// FIXME - AvmCore gets this from an external source. I'm not exactly sure
// what the contents it, but it's probably all 'flash.*', 'air.*', etc. namespaces
// This is only ever used when parsing our playerglobals, so we just treat everything
// as versioned for now. As a result, any intra-playerglobal *references* that lack
// an explicit version marker will be treated as ApiVersion::VM_INTERNAL.
// The only exceptions are the 'AS3' ("http://adobe.com/AS3/2006/builtin")
// and "flash_proxy" (b"http://www.adobe.com/2006/actionscript/flash/proxy") namespaces.
// These are used by user code, and are not given version markers in playerglobals
// by the ASC compiler. As a result, we do not treat them as versioned, so that
// references from within playerglobals will use ApiVersion::AllVersions;
//
// For example, consider the AIR-only class `flash.net.DatagramSocket`. The class
// definition has version marker corresponding to an AIR-only version - when running
// the Flash Player runtime, we will map this to VM_INTERNAL in `ApiVersion::to_valid_playerglobals_version`
// (which hides it from user code). However, the playerglobal will still try to initialize this class via:
//
// ```
// initproperty QName(PackageNamespace("flash.net"),"DatagramSocket")
// ```
//
// This is a namespace without a version marker (the compiler only ever generates version
// markers in definitions, not references). As a result, we will treat this as a VM_INTERNAL
// which will allow `initproperty` to see the `flash.net.DatagramSocket` class definition,
// even when running as the FlashPlayer (not AIR) runtime.
//
// Outside of playerglobals, we'll tag all namespaces with a version based on the SWF version.
// This is always less than VM_INTERNAL, so AIR-only classes will be correctly hidden outside
// of playerglobals when using the FlashPlayer runtime.
let is_versioned_url = |url: AvmAtom<'gc>| {
url.as_wstr() != b"http://adobe.com/AS3/2006/builtin"
&& url.as_wstr() != b"http://www.adobe.com/2006/actionscript/flash/proxy"
};
let is_public = matches!(
abc_namespace,
AbcNamespace::Namespace(_) | AbcNamespace::Package(_)
@ -137,6 +166,11 @@ impl<'gc> Namespace<'gc> {
if !has_version_mark && is_public && is_versioned_url(namespace_name) {
api_version = ApiVersion::VM_INTERNAL;
}
// In avmplus, this conversion is done later in in 'getValidApiVersion'
// However, there's no reason to hold on to invalid API versions for the
// current active series (player runtime), so let's just do the conversion immediately.
api_version =
api_version.to_valid_playerglobals_version(context.avm2.player_runtime);
} else if is_public {
api_version = translation_unit.api_version(context.avm2);
};

View File

@ -16,6 +16,7 @@ use crate::avm2::{Avm2, Error};
use crate::context::{GcContext, UpdateContext};
use crate::string::{AvmAtom, AvmString};
use crate::tag_utils::SwfMovie;
use crate::PlayerRuntime;
use gc_arena::{Collect, Gc, GcCell, Mutation};
use std::cell::Ref;
use std::mem::drop;
@ -135,7 +136,10 @@ impl<'gc> TranslationUnit<'gc> {
pub fn api_version(self, avm2: &Avm2<'gc>) -> ApiVersion {
if self.domain().is_playerglobals_domain(avm2) {
// FIXME: get this from the player version we're emulating
ApiVersion::SWF_31
match avm2.player_runtime {
PlayerRuntime::FlashPlayer => ApiVersion::SWF_31,
PlayerRuntime::AIR => ApiVersion::AIR_20_0,
}
} else {
avm2.root_api_version
}

View File

@ -57,6 +57,7 @@ use ruffle_render::commands::CommandList;
use ruffle_render::quality::StageQuality;
use ruffle_render::transform::TransformStack;
use ruffle_video::backend::VideoBackend;
use serde::Deserialize;
use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::ops::DerefMut;
@ -392,7 +393,10 @@ impl Player {
self.mutate_with_update_context(|context| {
if context.swf.is_action_script_3() {
context.avm2.root_api_version = ApiVersion::from_swf_version(context.swf.version())
context.avm2.root_api_version = ApiVersion::from_swf_version(
context.swf.version(),
context.avm2.player_runtime,
)
.unwrap_or_else(|| panic!("Unknown SWF version {}", context.swf.version()));
}
@ -2432,6 +2436,7 @@ impl PlayerBuilder {
fn create_gc_root<'gc>(
gc_context: &'gc gc_arena::Mutation<'gc>,
player_version: u8,
player_runtime: PlayerRuntime,
fullscreen: bool,
fake_movie: Arc<SwfMovie>,
external_interface_providers: Vec<Box<dyn ExternalInterfaceProvider>>,
@ -2452,7 +2457,7 @@ impl PlayerBuilder {
audio_manager: AudioManager::new(),
action_queue: ActionQueue::new(),
avm1: Avm1::new(&mut init, player_version),
avm2: Avm2::new(&mut init, player_version),
avm2: Avm2::new(&mut init, player_version, player_runtime),
interner,
current_context_menu: None,
drag_object: None,
@ -2572,6 +2577,7 @@ impl PlayerBuilder {
Self::create_gc_root(
gc_context,
player_version,
self.player_runtime,
self.fullscreen,
fake_movie.clone(),
self.external_interface_providers,
@ -2682,7 +2688,7 @@ fn run_mouse_pick<'gc>(
}
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
pub enum PlayerRuntime {
#[default]
FlashPlayer,

View File

@ -166,6 +166,7 @@ impl ActivePlayer {
.with_spoofed_url(opt.spoof_url.clone().map(|url| url.to_string()))
.with_page_url(opt.spoof_url.clone().map(|url| url.to_string()))
.with_player_version(Some(opt.player_version))
.with_player_runtime(opt.player_runtime)
.with_frame_rate(opt.frame_rate);
let player = builder.build();

View File

@ -40,6 +40,7 @@ viewport_dimensions = { width = 100, height = 100, scale_factor = 1 } # The size
with_renderer = { optional = false, sample_count = 4, exclude_warp = false } # If this test requires a renderer to run. Optional will enable the renderer where available.
with_audio = false # If this test requires an audio backend to run.
with_video = false # If this test requires a video decoder backend to run.
runtime = "AIR" # The runtime to emulate ("FlashPlayer" or "AIR"). Defaults to "FlashPlayer"
# A list of image comparisons to perform during the test. This block is repeatable infinitely, as long as each name is unique.
# The comparison part of a test is optional and only runs when `imgtests` feature is enabled

View File

@ -7,7 +7,7 @@ use approx::assert_relative_eq;
use image::ImageOutputFormat;
use regex::Regex;
use ruffle_core::tag_utils::SwfMovie;
use ruffle_core::{PlayerBuilder, ViewportDimensions};
use ruffle_core::{PlayerBuilder, PlayerRuntime, ViewportDimensions};
use ruffle_render::backend::RenderBackend;
use ruffle_render::quality::StageQuality;
use serde::Deserialize;
@ -135,6 +135,7 @@ pub struct PlayerOptions {
with_renderer: Option<RenderOptions>,
with_audio: bool,
with_video: bool,
runtime: PlayerRuntime,
}
impl PlayerOptions {
@ -157,6 +158,8 @@ impl PlayerOptions {
player_builder = player_builder.with_audio(TestAudioBackend::default());
}
player_builder = player_builder.with_player_runtime(self.runtime);
#[cfg(feature = "ruffle_video_software")]
if self.with_video {
use ruffle_video_software::backend::SoftwareVideoBackend;

View File

@ -0,0 +1,18 @@
package {
import flash.display.MovieClip;
public class Test extends MovieClip {
public function Test() {
}
}
}
import flash.net.DatagramSocket;
var socket = new DatagramSocket();
// TODO - test functionally once we implement it in Ruffle
trace("Socket: " + socket);

View File

@ -0,0 +1 @@
Socket: [object DatagramSocket]

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,4 @@
num_ticks = 1
[player_options]
runtime = "AIR"

View File

@ -0,0 +1,26 @@
package {
import flash.display.MovieClip;
public class Test extends MovieClip {
public function Test() {
}
}
}
import flash.utils.getDefinitionByName;
const HIDDEN_CLASSES: Array = ["flash.net.DatagramSocket"];
for each (var klass in HIDDEN_CLASSES) {
try {
var obj = getDefinitionByName(klass);
trace("ERROR: " + klass + " is accessible from Flash Player");
} catch (e) {
trace(klass + " is inaccessible from Flash Player");
}
}

View File

@ -0,0 +1 @@
flash.net.DatagramSocket is inaccessible from Flash Player

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
num_ticks = 1