Clears the read and write buffer of Socket before returning to ActionScript, this could potentially be problematic
when a SWF reuses the same Socket to connect.
Also remove the handle from Arena when server closes the connection, this fixes
connected still returning true after server disconnection.
Some of the tests are currently disabled because (separate from this PR)
we can't actually run `toString()` on a Vector.<Object>, due to our
broken vector handling.
* avm2+tests: Allow setting prototype to undefined
This is used internally by Adobe Flex in many places. Also add test.
* avm2: Set prototype when constructing a Function with null prototype
* tests: Expand prototype_set_null test
* chore: fmt
The special meta key `defaultValue` should be used to set
the default `value` field. Also, `index` is the position
within *just* the image or texture params, not within
all of the params.
This adds a way to modify socket behavior to restrict, ask, or allow all
connections. Also adds a whitelist which can be used to allow specific hosts
without enabiling ask or allow all behaviors.
Use ProgressEvent for socketData event instead of a bare event.
Remove unwraps and use correct errors/exceptions.
Correct name in make_error_2008 call.
Use expect() for getting ByteArray instead of an error.
Correct some get_*() calls
Interfaces should only be listed when calling describeType
with a class instance (not with a `Class` object).
Additionally, AS3 methods can actually be displayed under
some circumstances. This is due to weird legacy behavior
that avmplus implements. A full fix requirs us to implement
namespace versioning, but we can get closer by special-casing
playerglobals (which is special-cased by namespace versioning
anyway).
The 'DisplayObject.visible' flag seems to be ignored for mask objects
(and their descendants) - mouse picking still takes them into account,
and they get rendered as part of the mask.
This commit changes display object rendering and AVM2 mouse picking
to ignore DisplayObject.visibility when dealing with mask objects/
descendants.
The variable use_new_invalid_bounds has been moved from the
UpdateContext and the Player to the Avm1 struct because it is only used
for AVM1 code. A getter method and an activator method setting it to
true have been added as well.
The get_bounds function, which uses the variable, has been adapted to
use the getter and the activator.
Additionally, documentation has been added and improved.
The MovieClip now contains documentation about all MovieClip states and
the use_new_invalid_bounds documentation has been extended to explain
the logic of how the variable is set and used.
The MovieClip variable oldest_parent_swf_version and its getter method
have been removed because they were unnecessary, as the UpdateContext
already provides the root movie.
The call of the getter method has been replaced by
activation.context.swf.version().
The suggested changes in the feedback to the pull request have been
implemented.
Therefore, this commit consists of multiple smaller changes:
- The unload_movie method has been renamed to avm1_unload_movie.
- The movieclip_default_state test now doesn't test
getTextSnapshot().getCount() because the underlying methods haven't
been implemented in AVM1 yet.
This should be reverted when they have been implemented or stubbed in
AVM1.
An unload_movie method has been added to the MovieClip. It unloads the
MovieClip, which means that one frame after it has been called,
avm1_unload and the new transform_to_unloaded_state method get called
and the MovieClip enters the unloaded state in which some attributes
have certain values.
To do this, it creates and spawns a future which calls avm1_unload and
transform_to_unloaded_state (which actually makes the MovieClip enter
the unloaded state).
It uses the new MovieUnloader (which has been added to the Loader enum)
to pass the MovieClip to the asynchronous code. The MovieUnloader saves
a handle and the target clip, and the asynchronous code retrieves it
from the LoadManager.
unload_movie is now called when a MovieClip should be unloaded.
TODOs have been added, describing to check whether other avm1_unload
calls also need to be delayed by one frame and whether unload_movie also
needs to be called in other places.
Additionally, a spelling mistake has been corrected.
Previously, the MovieClip default state contained wrong values for
opaqueBackground, transform.pixelBounds and getRect & getBounds with
another default MovieClip as the parameter.
This has been corrected; the calculation of these values has been fixed.
To do that, a use_new_invalid_bounds_value variable has been added to
the UpdateContext and the Player (from which the UpdateContext is
created).
Additionally, an oldest_parent_swf_version variable has been added to
the MovieClip with a getter method for it. It is calculated and saved on
demand in the getter.
These variables are retrieved in the get_bounds function and used to set
use_new_invalid_bounds_value and to calculate the correct bounds.
Ruffle now uses the correct MovieClip default state.
The set_cur_preload_frame and set_current_frame methods have been added
to the MovieClip.
If the Loader now loads an image, these methods are called to set the
MovieClip image state correctly.
Additionally, load_error_swf now uses set_cur_preload_frame instead of
the removed movie_not_available method.
A load_initial_loading_swf function has been added to the Loader. It
makes the MovieClip enter the initial loading state in which some
attributes have certain initial loading values to signal that the file
is currently being loaded and neither an error has occurred nor the
first frame has been successfully loaded yet.
The Loader calls the function when initialising the loading before it
gets an error or success.
Additionally, load_error_swf has been improved: If a local URL is
fetched on a WASM target, the _url property will now be correctly set as
in the flash player.
The documentation has been improved.
All NavigatorBackend implementations have been refactored, resulting in
improved code quality, less duplicated code, more consistent and easier
to understand procedures, additional error handling and better error
messages.
A resolve_url method has been added to the NavigatorBackend trait. It
takes a URL and and resolves it to the actual URL from which a file can
be fetched (including handling of relative links and pre-processing). It
has been implemented in each NavigatorBackend implementation.
Duplicated code has been put into new public functions in
core/src/backend/navigator.rs which are called by all NavigatorBackend
implementations.
ExternalNavigatorBackend:
- The navigate_to_url and fetch methods have been adapted to use
resolve_url, removing redundant code.
- Error handling has been added in case that the URL can't be converted
to a PathBuf.
- A TODO about differences between the flash player fetch and the
Ruffle fetch implementation has been added.
WebNavigatorBackend:
- The previous resolve_url method exclusively to the WebNavigatorBackend
has been replaced by the new resolve_url method. It is used by
navigate_to_url and fetch.
- resolve_url now always pre-processes the URL if it's valid (even if no
base_url exists) and explicitly returns whether the URL can be parsed.
- navigate_to_url now traces an explanatory error each if the URL can't
be parsed or is local.
- fetch now returns an explanatory error each if the URL can't be parsed
or is local (previously, a vague "Got JS error" has been returned).
TestNavigatorBackend & NullNavigatorBackend:
- fetch pre-processes the URL now (using the resolve_url implementation).
- If the URL isn't local, an explanatory error is returned (previously,
it was just an "Invalid URL" error).
- If the URL can't be parsed, an explanatory error with the reason is
returned (previously, it was just an "Invalid URL" error).
Additionally, error messages in all NavigatorBackend implementations
have been improved and made more consistent, e.g. if a local file can't
be read.
Previously, the MovieClip error state contained one TODO in order to be
completely implemented: The HeaderExt variable uncompressed_len needs to
be -1 in the error state, but its type has been u32.
The type has now been changed to i32, and the variable is set to -1 in
the default_error_header function. Therefore, the MovieClip error state
is now fully implemented.
Other connected functions and variables like SwfMovie::uncompressed_len
or MovieClip::total_bytes have been adjusted to this type change. Code
has been adjusted if needed where this value is used.
A load_error_swf function has been added to the Loader. It makes the
MovieClip enter the error state in which some attributes have certain
error values to signal that no valid file could be loaded. This happens
if no file could be loaded or if the loaded content is no valid
supported content.
The function creates an error state movie stub using the new
SwfMovie::error_movie function (which uses a new default_error_header
function) and configures remaining variables with the
movie_not_available method.
One TODO in order for the error state to be completely implemented has
been added.
Since the error state of the MovieClip includes the final URL of the SWF
file obtained after any redirects, the load_error_swf and
movie_loader_error functions (now) take an swf_url attribute.
To get this URL in case no file could be loaded, the
NavigatorBackend::fetch method has been changed to return an
ErrorResponse struct (including the url and the actual error) in the
error case. The Response struct returned in the success case has been
renamed to SuccessResponse.
All fetch implementations have been adapted accordingly. Code has been
adjusted to return the actual error where that's needed.
Documentation has been added and improved.
If an SWF tries to load a movie that can't be loaded, the Flash Player
sets _framesloaded of the MovieClip to -1. This has previously not been
implemented in Ruffle.
A new movie_not_available function has been added to the MovieClip
class. It sets the cur_preload_frame variable to 0.
This function is now called in the movie_loader_error function of the
Loader class (if a movie can't be loaded).
The frames_loaded function (which returns _framesloaded of the
MovieClip, which is given to the SWF) now returns an i32 and not an u16
value. This allows it to return a negative value. Code has been adjusted
where an u16 value is expected (clamping negative values to 0).
frames_loaded now returns cur_preload_frame - 1. Previously, it has
returned cur_preload_frame - 1 and clamped it to zero, but since the -1,
in case cur_preload_frame is zero, is explicitly wanted, this has been
corrected.
* avm2: Do not remove an EditText's selection on unfocus (fix#9006)
There are significant differences between how selection and caret info
work between AVM1 and AVM2.
In AVM1, there is only a single, global selection, which applies to
whichever element is currently focused. Therefore changing the focus
necessarily erases any information of what was selected before. There
can also be no active selection at all.
In AVM2, every text field has its own independent selection info. It is
NOT optional, the default selection is just a caret at position 0. If a
field loses focus, the selection is not rendered, but it is still
present. Movies such as #9006 rely on the same selection still being
there once you give back focus to the field.
Ruffle's model of selections (an Option per text field) is different
from both the AVM1 model (an optional singleton) and the AVM2 model
(mandatory per text field). This fix does not change that, it is only a
narrow fix targeted at 9006.
* avm2: implement selectionBegin/selectionEnd/caretIndex and add a test.
The selection test validates a few situations, including the behaviour
when unfocusing that was fixed in the previous commit.
The test does not validate how the selection changes after replacing
text, as there are still some inaccuracies there.
* avm2: Additional selection fixes needed after merging with master.
1. The default caret for an AVM2 textbox is at the end of the textbox,
not the beginning.
2. Selection should not be changed when focusing on a textbox in AVM2.
3. Fixed a test whose output.txt didn't actually match the flash player
output.
* avm2: Make the selection AVM checks compatible with mixed AVM, and revert the on_focus_changed parameters
---------
Co-authored-by: Nathan Adams <dinnerbone@dinnerbone.com>
The 'gc_arena' dependency was only used to manipulate the `GcCell`s
containing the vertex and fragment shaders; replacing these by a
reference to a plain old `Cell` means tha the Context3D traits and
types do not need to interact with GC'd object anymore.
As a knock-on effect, we can also remove the `Activation` parameter
from most of the `Context3DObject` methods.
* core: add temporary, ruffle-internal copy of `gc-arena` crate
This will allow bumping the upstream `gc-arena` version while
reexporting our own version of the old `GcCell` API, so that
Ruffle's code can be gradually migrated.
Once the migration is done, this crate should be removed.
* core: bump `gc-arena` to kyren/gc-arena#56
Add back the removed `GcCell` to our internal facade crate
* core: bump `gc-arena` to current master
This bump renames `Gc::allocate` to `Gc::new`
* core: rename `GcCell::allocate` to `GcCell::new`, to match `Gc`
* core: bump gc-arena to (slighly after) v0.3.1
Add typedefs for old `*Context` names in the gc-arena facade crate
* core: replace uses of `CollectionContext<'_>` by `&Collection`
* core: Add `gc()` convenience method for `*Context` and `Activation` types
This allows shortening most instances of `[activation.]context.gc_context`
to `activation.gc()` or `context.gc()` (but not all instances, because of
borrowck) Note that this doesn't actually do these shortenings to avoid
major code churn.
The bind group layout only depends on the texture registers
(and 2D/cubemap type) accessed by the fragment shader, not on
the runtime texture bound with Context3D. This means that we can
build and cache it when we compile the AGAL program to a Naga
module.
Since the bind group layout is used for the overall pipeline, I've
refactored the shader caching code into `ShaderPairAgal`, which
holds both the vertex and fragment shader bytecode, and compiles
both in the `compile` function.
When using a 'Loader', properties on the 'contentLoaderInfo' become
set during specific events in the load sequence. In particular,
'LoaderInfo.bytesTotal' becomes available during the first 'progress'
event.
Also, 'LoaderInfo.parameters' is now properly set from the URL query
parameters. In Flash player, this work even with filesystem urls
(e.g. 'file:///some/path/to/file.txt?paramOne=valOne' will load
a file named 'file.txt', setting and expose the parameter 'paramOne'
with value 'valOne' in `LoaderInfo.parameters`). This required some
cleanup to the desktop and test NavigatorBackend impls to strip
out query parameters when loading a parameter from disk.
Previously, we would set `SwfMovie.parameters` manually from the url.
Now, the various `SwfMovie` constructors automatically extract
query parameters from the provided url. Outside of `SwfMovie`,
we only append *extra* parameters (e.g. those set from `flashvars`).
This makes CPMStar ads work, since the loaded SWF needs to access
`LoaderInfo.parameters`
The 'winding' argument and filling behavior described in the docs
are not yet implemented. However, this implementation is good enough
for Scratch to render its default cat image.
We were previously only ever checking children,
and not attributes.
In order to avoid matching both attributes and elements
with a given name in 'descendants', `E4xNode::matches_name`
now checks `is_attribute` on the provided `Multiname`. This
requries changing several other parts of the codebase to
properly set this flag on `Multinames` provided by ActionScript.
The only generic class is `Vector`, which only has a single parameter.
However, we currently store a list of type parameters in several places,
which requires redundant checks that the list only has one entry.
Instead, we now store an `Option` everwhere except for
`swf::Multiname::TypeName` (since the actual SWF format supports
multiple type parameters). We still perform the same check when loading
bytecode (the swf Multiname should only have at most one type
parameter), but the rest of the codebase can deal with an `Option`
instead.
In the special case where y_min==y_max==(height-1),
we would create a bad 'encompassing' region - subtracing
one from the max would make it smaller than the min,
causing `PixelRegion::encompassing_pixels` to treat it
as the minimum, and add one to `height - 1`
There's no work to do when x_min==x_max or y_min==y_max,
so we can also skip the sync and GC write in this case.
This works a bit differently from SWF videos, we do not retain decoded video frames or valid seek points indefinitely. This assumes code interacting with `NetStream` is at least mildly aware of how a video codec is supposed to work and what the performance implications of that are.
We don't actually process tags out of order, we're just tracking what tags have already been processed. FLV embeds critical video metadata in a ScriptData tag that we need to pull data out of and process.
`XML(someXMLObj)` and `XMLList(someXMLList)` perform the
normal cast behavior (returning the same object), instead
of creating a new object like other arguments do.
This also applies to `new XMLList(someXMLObj)`
and `XML(singleElemXMLList)`
* wpgu: Initial implementation of PixelBender shader execution
The implementation is split across four crates:
* `ruffle_render` now holds the main PixelBender bytecode parsing
implementation (previously, this was in `ruffle_core`).
* `ruffle_core` holds some helper functions for converting between
AVM2 `Value`s and the PixelBender vector types.
* `naga-pixelbender` (newly created) constructs a Naga `Module`
from parsed PixelBender bytecode
* `ruffle_render_wgpu` sets up the render pipeline for the shader
constructed by `naga-pixelbender`, and actually executes the shader.
The Actionscript-side shader parameters are passed in through uniforms.
This allows us to cache the compiled `naga::Module` and associated
wgpu types inside `ShaderData`, when it's first created. Each invocation
of a `ShaderJob` only needs to create a bind group and render pass.
Limitations:
* Only a few of the PixelBender opcodes are implemented - however, this is
enough to get Stemlands cannon rotation working, as well as a cool
"donut" shader that I found and included as a test.
* PixelBender matrix types are not supported.
* Only BitmapData is supported as an input/output type - Flash Player
also supports using Vector and ByteArray
* ShaderJob execution is always synchronous.
* Adjust comments
* Address review comments
We create a separate child domain, which is accessible
from the Stage and the root movieclip.
This prevents ActionScript from loading classes into the
special playerglobals domain (Domain.parentDomain is modified
to return null instead of the playerglobals domain when applicable),
so the native method lookup logic will never run for user code.
This lets us print the full error message and stack stacke
in contexts where an Activation is not available, including
the `Debug` impl.
The 'name' and 'error' fields are accessed using hardcodd slot
ids. This is pretty hacky, but will work until we have better
handling of slot properties.
We now support deleting named children, as well as attributes.
Additionally, I've fixed our handling of `XML.parent()` - we now
properly set the parent when a child is created, and clear the parent
when `delete` is used.
If a child named 'foo' is removed by the timeline (without
having been previous added/removed from the timeline by ActionScript),
then the 'foo' field in the parent will be set to null. This occurs
even if the 'foo' field in the parent is not currently set to
the child 'foo' (e.g. 'this.foo = someOtherObject' was executed by
ActionScript).
- we can now get a `&RawTable` from a `&HashSet`, meaning we can store a
`HashSet` directly in the interner;
- `RawTable::iter_hash` now allows the removal of already-yielded
elements during iteration, which simplifies `WeakSet::entry`
Previously `last_mouse_position` was updated irrespectively of
whether the dragged object was inside or outside the constraint
box. Change it follow mouse deltas, after clamping is performed.
Fixes#11254.
When we run a 'goto', a weird "nested frame" gets triggered.
Previously, we were only calling `construct_frame` on the target
MovieClip as part of this "nested frame". However, Flash Player
seems to treat this (in some ways) like a normal frame - *all*
objects on the Stage (and orphans) have `construct_frame` called.
In particular, `gotoAndStop`/`gotoAndPlay` is called during
an "enterFrame" event handler, then unrelated objects on the Stage
will have their children constructed during the execution of
`gotoAndStop`/`gotoAndPlay`. The same logic holds for frame scripts.
This fixes a bug in Steamlands, which relies on children on the main
timeline being constructed immediately following a call to `gotoAndStop`
on an orphan (originally triggered from an "enterFrame" handler).
When we iterate over the orphan list to run frame lifecyle methods,
the orphan list may be modified (e.g. an event listener creates a new
orphan object). To ensure that we iterate over all of the orphans
originally present, this commit wraps the orphan list in an `Rc`
(just like we do for the render list). Modifications to the list use
`Rc::make_mut`, and iteration operates on a clone of the `Rc`.
QName can contain characters like '.' and '<' as part of the
package or class name (though this requires editing the SWF
or using a nonstandard compiler). This broke our attempt to parse
generic type paramters by looking for '.<'
Instead, our 'Vector' special casing now operates on the unparsed
'AVMString' name, instead of attempting to construct a 'QName'.
This means that we don't need to handle generic paramters at
all for obfuscated names (which will never start with '__AS3__'
or 'Vector.<')
This fixes a bug in Red Ball 4v3, which has an obfuscated class
'!D.<H'
If we have two PlaceObject tags in the same frame with the same depth,
only the first one actually places an object. The second one is ignored
(Flash Player logs a warning).
There are only a few places where we actually need to use the
`caller_domain`, so we don't actually need it available for most native
method calls. This means that `Activation::from_nothing` can be used
in the vast majority of cases without causing a panic later on.
We now parse PixelBender bytecode, and populate the parameters
from the bytecode on `ShaderData`. This is enough to progress
Steamlands, which needs to access dynamically set properties
on `ShaderData`
Bytecode execution is not implemented yet.
When we create a DisplayObject from ActionScript, we should always
run `construct_frame` on it, regardless of what frame phase we
are currently in.
This fixes a regression in Fancy Pants World 4 Part 1, where entering
the first door produced an error.
Flash Player performs `x + width` and `y + height` as floating
point operations before `round_to_even`. This affects the extent
covered by a `Rectangle` in various BitmapData methods, as the sum
of two values might be large enough to be rounded up to a larger
value (when rounding `x` and `width` individually would have
produced a smaller overall extent).
These are directly set on the underlying navigator's HTTP
request type, and get printed out in our test navigator backend.
No validation of the header names is performed - on web, this will be
enforced by the browser.
When we iterate over a render list (in order to call
`enter_frame`, `construct_frame, etc.) we need to be sure
that we iterate over all of the original `DisplayObjects`
in the list, even if the list is modified during iteration
(e.g. some ActionScript code calls `parent.addChildAt`).
Previously, `RenderIter` would repeatedly call `child_by_index`
on the original `DisplayObjectContainer`, up to the original
child count. If new DisplayObjects were inserted into the list
during iteration, we could miss some of the original DisplayObjects
in the list (as they are now at a greater position in the list).
To solve this, we now store the render list as an `Rc<Vec>`,
and use `Rc::make_mut` to modify it. See the comments
for more details.
With SWF version < 30, Context3D.configureBackBuffer throws
an error with a less informative message when the width/height
are out of range. Additionally, it seems to special case
the case width=0, height=0, antiAlias=0. enableDepthAndStencil=false,
and does *not* throw an error. This is relied on by Sniper Team.
The Flash Player 'Matrix3D.recompose' method throws exceptions under
certain circumstances with the "quaternion" orientation style.
I haven't yet figured this out yet, so I've marked that case as a stub.
All of the implementations are based on the OpenFL code, with some
tweaks to match Flash Player's behavior.
TextFields have a very unusual behavior - if they are selectable
and have `was_static`, they *block* the dispatch of mouse events when
they're targeted (not even the Stage will receive the event).
This only occurs when the TextField is actually targeted
(which requires mouseEnabled=true). With mouseEnabled=false,
the event will be dispatched with an ancestor as the target,
following the usual logic.
Also, TextFields now properly propagate mouse picks to
their parent if mouseEnabled=false. Previously, setting
mouseEnabled=false for a TextField made all mouse picks
cause a miss on it. This was the cause of the Turbo Kids
regression.
Fixes#10245
This can actually affect runtime behavior - if the return type is
declared as 'int', then an instance of a custom class will get
coerced to 0 when returned by the function.
'Plants vs Zombies Demo' relies on this - it has a function which
incorrectly returns an object instead of an array index, but the
value gets silently coerced to 0 under Flash Player.
This is based on URLLoader, and doesn't actualy 'stream' data -
it all becomes available once the request finishes. However, this
is good enough to get Sniper Team working.
We now run all of the completion logic (including adding
the new DisplayObject as a child) in `Loader::movie_loader_complete`.
Previously, some of this logic was run from `Loader::preload_tick`,
which meant that loaded images did not have the logic run.
Also, `BitmapData` and `Bitmap` instances (with corresponding AVM2
objects) are now properly constructed for loaded images.
In a previous PR, I introduced an optimization that used
`copy_texture_to_texture` to copy directly from a BitmapData GPU
texture to a Stage3D GPU texture.
Unfortunately, this optimization is incorrect. A BitmapData GPU
texture can be modified at any time by normal AVM2 code - in
particular, in might be modified before we submit the encoded
`copy_texture_to_texture` command. This shows up in Sniper Team,
which re-uses BitmapData objects for multiple distinct textures.
The previous 'optimization' resulted in the wrong BitmapData contents
getting uploaded to a texture (since it was changed before the copy
command was submitted).
This matches the Flash Player documentation. Since we were
manually traversing the displayobject hierarching when firing
the event, we ended up firing duplicate events to parents
because bubbling was enabled.
wgpu requires buffer copy sizes and offsets to be 4-byte aligned.
Unfortunately, ActionScript can perform 2-byte aligned uploads
into an IndexBuffer3D.
To support this, we now keep a copy of the IndexBuffer3D on the CPU.
When performing an upload to the buffer, we round the offset down
and the size up to the nearest 4-byte aligned value. The cpu buffer
is used to fill out the write with existing data, so that we don't
corrupt the contents of the GPU buffer.
To avoid introducing a new RefCell, I've changed IndexBuffer3D
to use a `Box` instead of an `Rc` to store the trait object.
This allows us to pass a mutable reference down to the backend.
Early class construction is tricky - `Object` defines properties
that need to get copied into subclass instance vtables, but `Class`
defines `prototype`, which needs to be copied into the *class* vtable
of `Object`.
To accomplish this, I've split out instance vtable initialization
into a separate `init_instance_vtable`. We call
`object_class.init_instance_vtable` before
`class_class.init_instance_vtable`, but do things in the opposite
order for `into_finished_class` (`class_class.into_finished_class` is
called before `object_class.into_finished_class`)
The suggested changes to the navigate_to_url handling in the feedback to
the pull request have been implemented.
Therefore, this commit consists of multiple smaller changes:
1. The allow_javascript_calls variable has been removed (as a CLI
argument and in the navigator). Javascript calls are now always denied
on desktop. This is because setting the argument was useless; no
javascript was executed in any case, at most, just a browser tab opened.
Therefore, it makes no sense to include this option.
2. The NavigateWebsiteHandlingMode default value has been provisionally
changed from Confirm to Allow. In the future (after a GUI toolkit has
been added), the default confirmation windows should include a "Save
this preference" checkbox.
3. The NetworkingRestrictionMode enum has been renamed to
NetworkingAccessMode since the previous naming was counter-intuitive.
4. The NavigateWebsiteHandlingMode enum (and variables related to it)
have been renamed to OpenURLMode to simplify the name.
5. The documentation has been improved.
The networking API restrictions imposed by the allowNetworking parameter
& attribute have been added and partially implemented.
A new NetworkingRestrictionMode enum has been added to Ruffle (in Rust
and Typescript). It contains the values "All", "Internal" and "None" and
models the possible values of the allowNetworking parameter / attribute.
All means that all networking APIs are permitted in the SWF file,
Internal means that the SWF file may not call browser navigation or
browser interaction APIs and None means the same and that the SWF file
cannot use any SWF-to-SWF communication APIs either.
A respective allowNetworking variable has been added to the JS config.
Its default value is All.
Ruffle now recognises the allowNetworking parameter and attribute in the
SWF HTML object and parses it and sets the config variable
correspondingly if it's recognised.
Only if the variable is set to All, the external interface (responsible
for javascript calls in AS3) is created. Additionally, the variable is
given to the WebNavigatorBackend and saved in it. The navigator denies
all navigate_to_url calls if the variable hasn't been set to All.
Therefore, the API restrictions imposed by setting allowNetworking to
internal or none have been partially implemented.
Formatting has been improved.
New configuration options (changing the navigate_to_url call handling)
have been added. The default behaviour has been changed as well.
A NavigateWebsiteHandlingMode enum has been added to Ruffle (in Rust and
Typescript). It contains the values "Allow", "Confirm" and "Deny" and
describes how navigate_to_url website calls should be handled. Allow
means that all website calls are allowed, Confirm means that a
confirmation window opens with each website call and Deny means that all
website calls are denied.
A respective navigate_website_handling_mode variable has been added to
the desktop CLI and to the JS config. The default value is "Confirm" in
each. The variable is given to the navigator (ExternalNavigatorBackend
or WebNavigatorBackend, depending on the platform) and is saved in it.
On each navigate_to_url website call, the respective navigator is now
checking navigate_website_handling_mode and acts correspondingly (allows
it, opens a confirmation window or denies it).
This changes the default behaviour of Ruffle from allowing all website
calls to opening a confirmation window with each website call.
On Safari, the confirm window can cause the background music to stop,
but this seems to be an issue with Safari.
Closes#838.
Additionally, an allow_javascript_calls variable (which defaults to
false) has been added to the desktop CLI. The variable is given to the
desktop navigator and is saved in it.
If a navigate_to_url javascript call is executed on desktop, the
navigator is now checking allow_javascript_calls and acts
correspondingly (allows it or denies it).
This changes the default behaviour of Ruffle on desktop to not allowing
javascript calls.
Closes#9316.
`startDrag()` used to capture the offset between the mouse cursor
and dragged object. This is buggy when the dragged object position
is changed *while* being dragged, as the original offset no longer
holds.
Change the dragging mechanism to be based purely on mouse deltas,
eliminating said offset completely.
Fixes#10775.
Previously, `AsBroadcaster` was defined as a plain object. However,
it seems like in Flash it is defined as an empty function instead.
This means expressions like `new AsBroadcaster()` should return a
newly-created object. This is in opposition to the documentation
that says there is no constructor function for the `AsBroadcaster`
class.
Fixes#10673.
It's possible to have a DefineSprite tag with multiple frames,
but with a corresponding SymbolClass that directly extends
`Sprite` (and therefore does *not* extend `MovieClip`). When this
happens, Flash Player stops after the first frame.
Doing `super.someNonGetter` gives you back a function object.
We were previously attempting to call normal methods as though
they were getters. Additionally, we were failing to properly
get the property from the superclass vtable.
This ensures that we call the normal Array concat method
(or a method that overrides it). Some SWFs may define a *public*
concat method in an Array subclass, with a different signature.
Fixes#10552
1. toString and toLocaleString should only be defined on the prototype.
2. concat should only be defined as an as3 and proto property, not as an instace property.
3. Array doesn't have a valueOf defined directly on it.
This matches the Context3D docs. Calling 'present' swaps
the buffers.
I wasn't certain if we actually need a double-buffered depth
texture, but I included one just to be safe.
Now that most of the complicated Context3D methods have been
implemented, we can simplify the overall design. Instead of queueing
up commands and having `present` execute them in a loop, we
can execute each command immediately. The key insight is that
a `RenderPass` is only needed for `DrawTriangles`, so we don't
have to store it in `Context3D` and deal with complicated lifetime
issues.
The old behavior gave us implicit double-buffering behavior,
since nothing would get rendered until a 'present' call.
Now that a 'drawTriangles' call will immediately submit
a draw command, we need to implement actual double buffering.
This is done in the next commit.
The `Avm2::{dispatch, broadcast}_event` methods now log and swallow any
AVM2 error occuring during the dispatch, instead of repeating the
handling code for each caller.
This also introduces some behavioral changes:
- Errors messages are more consistent;
- For consistency with `broadcast_event`, `dispatch_event` panics if
given a non-event object to dispatch.
When we were using a fork of quick-xml, we modified the actual
unescape method. Now that we're using the crates.io release again,
we need to go through our `custom_unescape` function.
If a SWF contains multiple DefineFont tags with the same
font name (but different font IDs), the first tag will win
when a font is looked up by *name*. This affects the behavior
of EditText objects, which can have embedded HTML like
`<font face="MyFontName">` which performs a font lookup by name.
This fixes Fancy Pants World 4 Part 3, which contains two
DefineFont3 tags with the name FancyFont. The second font is
missing many glyphs, so using it causes us to be unable to
render the squiggle and life count text.
These functions are intentionally no-ops in Ruffle because it has
no concept of a player dirty region, so unmark them as stubs.
The only observable difference is that Flash Player will sometimes
not re-render a `Bitmap` instance on the stage immediately if it's
`BitmapData` is locked and changed, but this is only temporary and
depends on the redraw behavior of the Flash Player.
* `global_to_local` returns `None` if the object has zero scale.
* Adjust AVM `globalToLocal` methods to return the untransformed
point on failure.
* Add `DisplayObject::mouse_to_local` to handle AVM `mouseX`
and `mouseY` coordinates. For zero scale objects, these end up
returning values based on the twips-to-pixels scale,
divided by 20.
* Add `Matrix::determinant`.
* Rename `Matrix::invert` to `inverse`.
* `Matrix::inverse` return an `Option`, with `None` returned
for non-invertible matrices.
* AMV `Matrix::invert` duplicates the code as the behavior is
different (works in f64 and not twips, etc.)
I've moved our special entity handling logic into
a `custom_unescape` function. This lets us move off
of our fork of `quick-xml` back onto the crates.io release
When we receieve a nonzero 'antiAlias' parameter, we create
create a non-multisampled resolve buffer to use with WGPU.
Several tests were already requesting antialiasing, so their
output images are now anti-aliased without any changes to
the tests themselves.
Previously, the `ApplicationDomain` constructor ignored its argument,
instead of constructing a new domain with the specified domain as
the parent.
Additionally, we were incorrectly executing code with
`Activation::from_nothing` in several places, causing
`ApplicationDomain.currentDomain` to return the system domain
instead of the correct parent domain. I've introduced a new method
`Activation::from_domain`, which allows explicitly passing in the
domain. Internally, we now store an `Option<Domain>`, and panic
when calling `caller_domain` with a `None` domain. Several places
in the codebase have been adjusted to pass in the correct domain.
If you use a `Loader` to load an SWF containing a class that shadows
an already-defined class, the class definition from the Loader SWF
will be ignoredin favor of the already-defined class. This commit
applies this log to symbol classes as well - the symbol registry for the class
should continue to point to the existing MovieClip in the parent.
This results in the child SWF instantiating the class from the parent
SWF when the child places the affected movie clip on the timeline.
This fixes a bug in Fancy Pants World 4 Part 3, where the sub-level
SWF was replacing the symbol class entry for the parent 'shipInteract'
class with the dummy clip provided in the sub-level SWF (instead
of continuing to use the correct clip from the parent SWF).
Instead of queueing up these events in the `Activation`,
we can fire them immediately by making `AudioManager::update_sounds`
a freestanding method that takes in an `UpdateContext`
Avoid panic in ChildContainer.replace_at_depth() panic when previous child is not in render list.
---------
Co-authored-by: Gleb Piskunov <emgfc@ya.ru>
Previously, we were scaling down the source image to fit into
the smaller sourceRect, instead of cropping at the original scale.
This broke the background textures in Fancy Pants World 4 Part 2,
as the scaled-down output image resulted in a smaller rectangle
being returned from 'getColorBoundsRect'
We now crop the image by properly constructing the UV-coordinate
transformation matrix. We were also using the wrong value for the
'destPoint' y coordinate, which I fixed.
This slightly changes the image output of two tests - the new images
now more closely match the Flash output.
So far this just sticks the stream into the playback list and kicks off a download; we do not actually support decoding, seeking, or any of the other things that we expect `play` to do.
Calling `StreamManager::tick` advances all streams to the appropriate time. This is an unlocked timestep to support things like non-stage-FPS video and the like.
* Pixels with 0 alpha are not affected by color transforms.
* Color channels should be clamped to the 0-255 range.
* A color transform with only an alpha multiplier of >1 has no
effect.
These are used in the Rust handler, but were not correctly set in the AS bindings, leading to errors such as "Attempted to call flash::text::TextField::getTextFormat with 2 arguments (more than 0 is prohibited)"
In the process, I fixed a bug where we were clearing the depth
and stencil buffers with the incorrect value.
This makes Fancy Pants World 4 Part 1 playable to completion
(though there are still some rendering issues that need
to be fixed).
This factors out the early-resolution logic I added in `op_coerce`,
making it useable during paramter resolution as well. This lets
a static initializer reference the containing class in parameter
types, even though the ClassObject hasn't yet been initialized.
We were missing the initial 'set_skip_next_enter_frame(true)'
call, and we weren't properly clearing it in `enter_frame`.
Loaders appear to have the same behavior as MovieClips.
This makes us correctly run the first framescript for the loaded
SWF.
It was incorrectly declared as public method, which lead to
spurious 'missing override' errors in classes extending Array
that define a 'removeAt' method.
We don't need to perform a sync when getting the width/height,
getting or setting the 'disposed' status, or uploading to
a Context3D texture.
The Context3D change (using `copy_texture_to_texture` instead
of relying on the CPU pixels) has the added advantage of avoiding
a validation error when our source image row length isn't aligned
to `COPY_BYTES_PER_ROW_ALIGNMENT`
This dramatically speeds up the Fancy Pants World 4 loading time
(on a branch with my XML prs merged). Without this change, my
machine spends around 10 seconds on a blank white screen after
clicking 'Play'. With this change, the time spent on that screen
is reduced to around 1-2 seconds.
`SoundChannel.position` was being updated by the audio manager each
tick, but the Flash Player only updates and caches the position
when `SoundChannel.position` is accessed. In contrast, an AVM1
`Sound` will constantly update its position.
This means accessing `position` only once after a sound has
finished playing will return 0.
Fixes#9952.
Despite not being MovieClips, Loader instances appear to get
the same kind of orphan handling - you can instantiate a
Loader and call 'Loader.load' without ever adding it
to a parent, and the loader will still run.
I've changed the movie code to work with a new `DisplayObjectWeak`
enum. Currently, this just supports `MovieClip` and `Loader`,
but it can easily be extended if we ever need other weak display
objects.
This also fixes a bug where we were adding the loaded MovieClip
as a child of the Loader slightly too early.
This includes the 'GetDescendants' opcode, which is used by the
the 'xml..elementName' syntax. The 'XMLList.toXMLString()
impl makes it much easier to write tests for this.
None of these formats can currently be implemented
correctly with wgpu, so we just use Rgba8Unorm instead.
The handling of opaque compressed textures is a little
sketchy - it should work for 'normal' SWFs that upload
an opaque BitmapData, but we might need to manually
adjust the alpha values if
We only support values that are neither XML nor XMLList,
since we can't yet properly stringify those.
Attempting to modify an existing attribute throws an error.
* Bump bitflags to 2.0.0
* Sprinkle Clone, Copy, Eq, PartialEq, and Debug derives where needed
* Call `bits` on bitflags, as it is now a method
* Switch from `from_bits_truncate` to `from_bits_retain` on bitflags where needed
* Bump h263-rs for the bitflags 2.0.0 dependency
As part of porting to bitflags 2.0.0, see:
https://kodraus.github.io/rust/2022/10/07/bitflags2.html#upgrading-to-2x
We were ignoreing 'data32PerVertex'.
To make the code clearer, I've renamed the variable to
'data32_per_vertex', and made it a 'u8' (as it has a maximum of 64)
The XML call handler is implemented as 'new XML(arg)',
so we get all of the related string coercions for free.
Our various native tables are starting to get somewhat wasteful -
if we add any more, we might want to consider a more compact
representation.
When we skip running a frame for a MovieCilp, we skip all
of its children as well. However, this skip 'counts' as
a skip for any children that already wanted to skip their next
frame. For example, say we create three objects in ActionScript,
and arrange them like 'obj1 -> obj2 -> obj3'.
The first 'obj1.enter_frame' call will not run a new frame
for any of the objects, but next time, 'obj1.enter_frame'
will run a new frame for all of the objects.
This fixes jacksmith, which was missing a frame1-framescript
due to 'enter_frame' getting incorrectly run for a deeply
nested child.
If you call 'BitmapData.dispose()', any Bitmap objects using it will
continue to report the original 'width' and 'height' values to
ActionScript. The values only refresh if you explicitly do
'Bitmap.bitmapData = bitmapDataObject' (including with the same
object).
Fancy Pants Adventure World 4 relies on this - it calls
BitmapData.dispose(), and then uses the width and height from
a previously-constructed Bitmap object.
When a DisplayObject is removed from its parent by a RemoveTag, it still runs its framescript for the current frame (but with 'this.parent == null'). It then stops executing entirely, unlike ActionScript-removed orphans, which continue to execute indefinitely.
Additionally, objects created by ActionScript during a frame skip their next 'enterFrame' logic (but still receive an enterFrame event). This results in the currentFrame lagging one frame behind objects that were placed by the timeline during the same frame.
The combination of these two changes lets us greatly simplify frame lifecycle handling for orphan movies. Most of the orphan stages were unencessary, and the remaining ones run in the same phase as the normal Stage-descendant objects.
Webgl doesn't support BGRA textures, so this lets us use
Stage3D textures on the web backend. As a bonus, this speeds up
uploading an BitmapData to a Context3dTextureFormat.BGRA texture,
since we no longer need to change the format before copying.
This makes Solarmax2 playable on the web backend.
The stage alignment settings viewport_scale_factor should *not* be
applied to `Stage.transform.matrix`, which is only ever changed
as a result of explicit modification from ActionScript. Instead,
alignment and scaling are performed a separate step, which is
transparent to ActionScript.
I've implemented this through a new `viewport_matrix` field,
which is used during stage rendering and mouse coordinate
transformation.
This makes Stage3D instances properly scale - previously, they
would render unscaled. The linux standalone Flash Player doesn't
seem to use HiDPI mode, so I didn't realize that this was a bug
until now.
In the process of implementing this, I discovered and fixed a bug
with how we handle changing the viewport size under winit.
Calling `self.window.set_inner_size` does not immediately take
effect (at least on X11) - calling `self.window.inner_size()`
will report the old size until the next resize event.
Since build our Stage matrices from `self.window.inner_size()`
(and start running the SWF) immediately after `RuffleEvent::OnMetadata`,
we would run a few SWF frames with an incorrect viewport size. This
is visible to SWFs that have the scale mode set to "noScale", and
could break SWFs that expect the initial viewport size to be
the movie size. I've fixed this by delaying SWF execution until
we get a Resize event (if `self.window.inner_size()` does not
immediately report the size we set).
This makes #3294 (rollercoaster-creator-2) fully playable.
Missing is any (*) matching for child()/elements() and the existing attributes() method.
Also missing is support for number indexes with child().
When an XML object has simple content, you can call non-XML
methods directly on it - it will internally be stringified,
and the method will be called on the resulting string.
This lets `new XML("<p>Some content</p>".split(' ')` work.
Similarly, an XMLList object with a single XML child will
forward non-XML method calls to that object.
This PR implements this logic (based heavily on avmplus)
I've also renamed these methods to 'avm1_unload' and
'avm1_removed', to make it clear that they don't
apply to AVM2.
This was causing us to incorrectly skip mouse picks,
and remove masks.
I think this might have been broken by
https://github.com/ruffle-rs/ruffle/pull/9506, but we didn't have
proper test coverage.
If we execute a 'coerce' opcode for a class while it's being
initialized (which can happen by running a method from a static
initializer), we'll be unable to resolve the ClassObject using
`resolve_type`.
This is the only case where this can happen - any
superinterfaces/superclass will already be fully initialized
when we're running a class initializer. Therefore, we can
try to lookup the class from the `Domain`, and check if it
directly matches the class of the object we're coercing
(ignoring superclasses and interfaces).
This doesn't perfectly match Flash's behavior - I haven't been
able to reproduce the values produces when the DisplayObject
starts out with certain 'Matrix' values (a non-zero 'b' or 'd').
Howver, when the 'b' and 'd' matrix values are both 0, setting
'dobj.rotation = NaN' has no effect on the matrix, while
'dobj.scaleX = NaN' and 'dobj.scaleY = NaN' both treat 'NaN'
as 0 for the purposes of updating the matrix.
This fixes the tack shooter in Bloons Tower Defense 3, which
tries to set 'rotation = NaN' for spawned tacks.
This is necessary to make Steambirds get past the preloader screen.
All of the previous tests continue to pass with this change.
This commit modifies the existing test to start from within
the symbol_class constructor, instead of a frame script. In
this situation, a freshly-created orphan with a framescript will
run directly after the constructor returns, *before* an enterFrame
handler for the same orphan. I've verified that this modified test
fails without my change.
It's possible to call 'start()' on a timer
that has currentCount >= repeatCount. This will
cause the timer to tick exactly once, and then stop agian.
We were incorrectly reporting 'timer.running' in such a scenario:
'running' should be reported as 'true' up until just before the
'TimerEvent.TIMER_COMPLETE' is fired.
This fixes gaining money from bloon popping / level completion
in BTD5.
Depending on when loading completes, calling
`catchup_display_object_to_frame` might trigger an
`addedToStageEvent` inside the loaded SWF. The event listener
will expect the SWF content to have 'DisplayObject.stage' accessible,
so we need to make sure that we've added our loaded content as
a child of the `Loader` *before* any event handlers run.
I've been unable to come up with a self-contained test for this,
but it's necessary for Bloons Tower Defense 5
Previously, the Vector$ classes were only exported in the internal 'AS3.vec' namespace, which is used by older ActionScript code. However, newer ActionScript code can also access these classes through the public 'AS3.vec' namespace, via 'getDefintionByName'.
We now export these classes in both namespaces. In the public 'AS3.vec' namespace, they are exported like 'Vector.' instead of 'Vector$uint'
We still want to propagate these hits to the parent, which may
be able to handle them. My existing tests missed this case,
since all of the parent objects had content which was
behind the child content. When the only clickable content
comes from a child with 'mouseEnabled=false', we should
still fire an event targeting the parent (when applicable
based on the parent's flags).
This fixes dragging on the background (without any scenery present)
in Steambirds.
When a MovieClip is an 'orphan' (it has no parent),
it still has frames run (including frame scripts). Some SWFS
like SteamBirds and 'This is the Only Level TOO' rely on this behavior,
so we need to implement it.
The overall idea is straightforward - we keep a global list of
orphan movies, which we add to whenever we unset the parent for a movie.
This list stores weak references for consistency with Flash.
When we run a frame, we process entries in the root movie list,
in addition to the normal recursive processing from the `Stage`.
However, exactly matching Flash's output turned out to be quite tricky.
The particular sequence of calls I make in `run_all_phases_avm2` makes Ruffle
pass two complicated test cases, but there could still be lurking bugs.
This is enough to get SteamBirds to the first level (which doesn't
render due to a different error).
We were previously performing a redundant 'self.hit_test_shape'
call in 'avm2_mouse_pick'. All of the logic in that function
is handled in `avm2_mouse_pick.` Additionally, this call happened
before we tested out children, which would result in us targeting
a parent's drawing instead of aa child.
Surprisingly, Flash allows mutliple classes in an inheritance chain
to hav a linked `class_symbol`. When we instantiate a `DisplayObject`,
we need to stop at the first such `class_symbol` we find. This means
that any fields set from named children will *only* be set in the
first class we find, not in any of the parent classes (as their
corresponding library symbol will not be instantiated).
Previously, we would continue looping even after we found a
`class_symbol`, resulting in the furthest ancestor *winning*
the `set_object2` call.
This is a very large diff, but most of it comes from test files and
output.
This PR ads partial support for the following Stage3D shader features:
* Normal (square), rectangle, and cube textures
* Varying and temporary registers
* Lots of opcodes
The combination of these allows us to get a raytracing program
fully working in Ruffle. I've included it as image test.
Currently, this test is very slow (about 90 seconds on my machine),
as the code I'm using (https://github.com/saharan/OGSL) includes
its own shader language and compiler. THe raytracing demo
first compiles its own shader language to AGAL, and then starts
rendering the scene.
Limitations:
* Many opcodes are still unimplemented
* Most non-default texture options (e.g. mipmaps) are not implemented
We were previously calling `get_property` to determine if a `toJSON`
property exists, but that produces an error if the method is missing
on a sealed class.
Additionally, JSON serialization wasn't taking into account properties
from the vtable. All public properties (including fields, const fields,
and getter methods) get serialized.
Unfortunately, our vtable property order currently doesn't match
Flash's. I've hand-edited the test output for now (all of the actual
properties are there, just in a different order), and added a note
This includes all of the XML elements described in 'describeType' docs.
Unfortunately, the order of elements produced by Flash depends on
the iteration order of internal hashtables. As a result, the test
manually stringifies an XML object, sorting the stringified children,
to produce consistent output between Flash and Ruffle.
This requires the ability to do a limited 'set_property',
as well as `get_enumerant_value`.
To prevent modification of XMLLists derived from queries,
I've introduced a `target` field on `XMLList`. This is
`None` for lists created with `new XMLList()`, and `Some`
when the list was derived from a query on an existing `XML`
/`XMLList`. We only allow `set_property` when `target` is `None`:
this is enough for filtering to work, and prevents silent incorrect
execution when trying to modify an existing node.
Previously there were multiple implementations scattered across the
codebase. Unify them to a single place, in a more "Rusty" way (now
it's called via dot notation, rather than as a free function).