This brings us closer to matching the Flash Player
enumeration behavior. Unfortunately, the precise enumeration
order for ScriptObject properties depends on the precise
order in the internal avmplus hashmap. This order is deterministic,
but adding/removing a property effectively randomizes it. Hopefully
there aren't any SWFS that depend on the *exact* order.
Handle removed clips inline in `Avm1::run_frame`, such that
`DisplayObject::prev_avm1_clip` is no longer used. Thus it can be
removed in a follow-up commit.
When we run a 'goto' where the initial and target frame are the same,
we need to skip triggering any sounds in the target frame.
Some games like 'This is the only level too' rely on this behavior:
they repeatedly run 'movieClip.gotoAndStop(current_frame_id)',
where 'current_frame_id' is the id of a frame that starts playing
a sound. Without this change, the sound will restart every frame
intead of playing exactly once.
We previously used 'coerce_to_object', which produced
an error with `Value::Null`. Instead, we can just ues
`value.as_type_of`, which will correctly handle `null`
The 'charCode' and 'keyCode' properties are now implemented
on `KeyboardEvent`
The input injection code we use does not support keyboard events,
so we can't yet write a regression test for this. However,
both 'You need to burn the rope' and 'This is the Only Level TOO'
now properly handle keyboard events with this PR.
* avm2: Implement call stack
* avm2: Class traits should have a special prefix
* avm2: Stack tracebacks should also contain error message
* avm2: Move method naming to Executable
* avm2: Handle getter and setter methods in tracebacks
* chore: Formatting
* chore: Add comments
* avm2: Make full_name write to a string, instead of creating a new one
* core: Make GcArena publicly accessible
* core: Add Deref impl for Either type
* desktop: Add AVM2 call stack to panic message
* avm2: Prefix native methods with a `/`
* chore: Appease clippy
* avm2: Check if method actually contains bytecode instead of unwrapping
* web: Add AVM2 stack trace to panic message
* chore: Formatting
* chore: Clippy
* avm2: Fix stack traces for free standing functions
* core: Remove global data from context
* core: Rename GcGlobalData to GcCallstack
* core: Introduce StaticCallstack, make GcArena private again
Co-authored-by: Adrian Wielgosik <4729533+adrian17@users.noreply.github.com>
Declare `NaN`, `Infinity` and `undefined` in ActionScript, similarly
to how `avmplus` does in its `actionscript.lang.as`.
Note that `null` is only removed, without an ActionScript declaration,
as it seems like `avmplus` neither declares it. Probably `null` is
only usable as a compile-time constant.
Extract `swf::Reader::read_do_abc()` which, as the name suggests,
reads a `DoAbc` tag, and use it before calling to `Avm2::load_abc`.
Finally, introduce `DoAbcFlag` using `bitflags`.
This greatly simplifies the ABC loading code.
This is in keeping with the whole idea of a "recursive frame": gotos run the entire frame lifecycle on the target clip, including broadcasts for `frameConstructed` and `exitFrame`.
We still retain the queue system as events are fired at removal time, and those events can trigger more gotos. If such a goto happens, AS3 code will hit a clip still in the old state rather than an inconsistent one. I don't have test coverage for this exact scenario just yet.
The rationale for the catch-up logic is as follows:
* We must always enter-frame and construct objects, even if those respective display events haven't happened yet.
* Display objects created in event handlers still need to run catchup phases, otherwise they will tag-stream desync
* Frame scripts are never triggered by catchup phases
* `exit_frame` is not a catchup phase as it is *only* an event broadcast currently
Normally a function closures also closes around its base clip.
If the base clip is removed, and then the function is executed, the
base clip then defaults to `this`.
However, Ruffle was incorrectly using the wrong base clip when
loading the `_root` and `_parent` registers in this case.
Fixes#5645.
This PR implements the `Loader.load` method, as well as
the associated `LoaderInfo` properties and events.
We can now load in an external AVM2 SWf: it will be added
as a child of `Loader` object, and will render properly
to the screen.
Limitations:
* The only supported `URLRequest` property is `url`
* `LoaderContext` is not supported at all - we always use the default
behavior
* Only `Loader.load` is implemented - we do not yet support unloading.
* We fire a plain 'Event' for the 'progress' event, instead of using
the (not yet implemented) 'ProgressEvent' class
The main changes in this PR are:
* The AVM2 `Loader` class now has an associated display object,
`LoaderDisplay`. This is basically a stub, and just renders
its single child (if it exists).
* `LoaderStream::Stage` is renamed to `LoaderStream::NotYetLoaded`.
This is used for both the `Stage` and an 'uninitialized'
`Loader.contentLoaderInfo`. In both cases, certain properties throw
errors, while others return actual values.
* The rust `Loader` manager now handles both AVM1 and AVM2 movie loads.
Previously, the viewport height and width were stored in
both `Stage` and the `RenderBackend`. Any changes to the viewport
dimensions (e.g. due to window resizing) needed to be updated in both
places to keep our handling of the viewport consistent.
This PR adds a new `ViewportDimensions` type, which holds the
width, height, and scale factor. It is stored inside the
`RenderBackend` impl, and is retrieved using the newly added
method `RenderBackend.get_viewport_dimensions`. After a `Player`
has been constructed, any code that needes access to the viewport
dimensions will ultimate go through this method.
Unfortunately, `Stage` needs to use the viewport dimensions
in `build_matrices`. Therefore, any code modifying the viewport
dimensions should go through `player.set_viewport_dimensions`,
which ensures that the stage matrices are rebuilt after the render
backend is updated.
When doing mouse picking, interactive children were considered
before all non-interactives, which could cause an `_droptarget` to
be set to an underlying movieclip even if a shape occluded it.
Now consider all children in render order so that the top-most
shape will capture the mouse input.
If we try to go to a frame that doesn't exist, or hasn't been loaded yet, we will stop on the last available frame, but skip any tags that would have run there. This is technically a desync, but it hasn't caused any problems so far as any further timeline interaction would trigger a rewind (which isn't affected by desyncs).
Of course, now that we're actually testing the tag stream position it *does* cause problems. We actually have to fix up the position to be correct even though it will never be used (hopefully). It may be prudent to do this outside of the `timeline_debug` feature as well in the future.