Turns out that the comment from `set_root_movie` was right all along!
`set_root_movie` should not be used when replacing the root movie,
because it will leave out unclosed resources.
This patch introduces `replace_root_movie`, which is dedicated to be
used when replacing (instead of just setting) the root movie.
The best solution would be to use RAII, e.g. destroy and recreate
the whole Player instance, but this patch is a first step towards
proper resource control.
Currently when clicking in a textbox, the cursor will only be placed
correctly if you click exactly on top of a letter. The basic purpose of
this commit is to make it so that clicking in the surrounding margin
will put the cursor in the closest available position, as in Flash Player.
The logic is a bit complex because a single row of text can contain
multiple layout boxes, each with a different Y offset (but all with the
same Y extent). To be accurate, we need to treat each layout box in the
row "as if" it had the same Y offset that the tallest box in its row has.
This commit also contains a fix for an issue where lower_from_text_spans
was passing the wrong text strings and indexes to fixup_line for
newlines, which needed to be fixed in order to be able to click to place
the cursor in an empty row that was created by newlines.
This has several advantages:
1. it allows using async variants of send and recv,
2. it adds consistency as until now Receiver was async,
and Sender was not.
We hit a pathological case in House
(https://github.com/ruffle-rs/ruffle/issues/15154),
where eagerly decoding bitmaps during preloading results in
over 10GB of ram being used.
With this PR, we store the compressed bitmap, and only decode it
each time we instantiate it. In order to support bitmap fills,
we store the decoded width/height and a lazily-initialized GPU handle
in `Character::Bitmap`
Platform Racing 3 relies on 'Socket.connected' reporting 'false'
immediately after calling 'Socket.connect' - it internally buffers
data made when 'Socket.connected' is false
This re-uses our existing infrastructure for displaying
AVM2 Class objector. One minor limitation of this approach
is the inability to view a `Class` that hasn't yet had its
`ClassObject` created - however, this should be rare in practice.
We show a collapsing header with all of the ClassObjects associated
with the given Class.
See the comments for details. Our previous implementation
was 'too good', and broke Bloons Tower Defense 5 by
generating `Number`s that Flash Player would never generate.
Among other things:
- resizing `colors` should also resize `alphas` and `ratios`
- shrinking `ratios` should also shrink `colors` and `alphas`, but
growing it doesn't change the size
This patch improves the logic of parsing and formatting HTML
for EditText, and adds support for SWF versions 6 and 7.
Examples of bugs fixed:
* invalid HTML: When generating HTML, Ruffle sometimes generated
mismatched tags, e.g. tried to close a tag which was never opened.
* text outside of tags: sometimes, especially in case of multiline
fields with multiple paragraphs, Ruffle generated proper tags,
but the text was placed outside of them.
* BR/SBR closing issues: When opened, Ruffle waited for a close tag
and ignored every other closing tag. BR/SBR do not need to be closed,
so Ruffle often waited indefinitely.
* P/LI behavior: P and LI have a very peculiar behavior, but a very
important one, because it influences the number of paragraphs/bullets
and thus newlines for multiline fields.
Support for SWF 6,7:
* whitespace in SWF 6,7: SWF versions 6,7 ignore witespace-only text.
This significantly influences the behavior of newlines and paragraphs.
* kerning in SWF 6,7: Enabling kerning in `<font>` works only for SWF 8+.
* multiline in SWF 6: FP 6 defines the `multiline` property, but it
completely ignores it, and the field behaves as if it's always multiline.
It seems that font styles in the default text format
are ignored when dealing with an HTML field.
This patch revisits the fix from feacbdc1 (#13615),
which assumed that `<font>` resets font style.
That does not seem to be the case, but rather the bug
was caused by the invalid default text format,
which forced the text to be bold, due to the bold
variant of the font being linked to the text field.
This patch reverts 2f84d468 (#1201), which assumed that
the default color for a text span has 100% alpha.
The test added here contradicts it and it seems that
the default color is in fact rgba(0,0,0,0).
Testing the original SWF suggests that the underlying problem
has been fixed since that time.
I've switched back to the original code for creating
the bitmap/bitmapdata, rather than relying on custom
initialization logic that we only used in loader.
To make sure that the Bitmap/BitmapData are only exposed
to ActionScript at the correct time, I've added a new flag
to control when 'LoaderInfo.content' becomes non-null
When ActionScript uses a ByteArray/Vector.<Number> as a shader input
or target, we create a temporary Rgba32Float texture, and copy the
input float32 bytes to/from the texture.
Unfortunately, wgpu doesn't seem to support an Rgb32Float (3-channel)
texture. When the shader uses 3 channels, we use a Rgba32Float
(4-channel) texture, and manually insert/remove padding for the
alpha channels. This isn't very efficient, but it's the simplest
solution.
The temporary textures themselves aren't cached anywhere - if this
becomes a performance issue, we could look into using some of our
existing wgpu texture/buffer pooling code.
This opens a searchable list (similar to what we have for display
objects), which shows a tree of Domains and their associated classes.
Currently, clicking on the domain/class buttons doesn't do anything.
In a follow-up, I'm planning to add additional windows to display
information about a class.
Some obfuscated SWFs may have invalid strings in their constant
pool - trying to immediately parse them as a utf-8 String throws
away information. Instead, we now store a `Vec<u8>`, which we
then use to construct an `AvmString` (or with `String::from_utf8_lossy`
for debug printing).
The handling of images in Loader.loaderBytes is similar to
the handling of SWFs - some of the data is exposed immediately
following the 'Loader.loadBytes' call, but the DisplayObject isn't
loaded until later.
This requires moving `set_root_movie` into `UpdateContext`.
Now, we preload the entire movieclip immediately - Flash Player
does this regardless of the size of the SWF.
The 'Loader::load_complete' is delayed to the end of the frame
(which is when the root class is constructed for the loaded clip).
When handling dynamic properties, avmplus will always try to
parse the string key name as a uint. If it succeeds, then the
key will be stored internally as a integer (via Atom), which is
observable by property iteration. The intention appears to have
been to support `obj[25] = someVal`, but it causes `obj["25"]`
and `obj[25]` to map to the same key (though iterating over the
object's keys will always produce a `number`).
This commit fixes issues with caret and selection rendering:
1. They had the wrong height and were rendered lower than expected
for some fonts and sizes.
2. The caret was not being rendered at all when there was no text,
but only when the text was set earlier and then deleted.
3. The selection was rendered with translate_x=-1,
which caused overlap over some glyphs.
Methods `onSetFocus` & `onKillFocus` are invoked when focus is changed
for `TextField`, `Button`, and `MovieClip`.
Multiple SWFs use these methods to listen to a focus change,
e.g. in order to implement placeholders for text fields.
We now validate the passed in profile, and return the selected profile
from 'Context3D.profile'. We don't yet alter the available
registers/textures based on the profile.
This is pretty straightforward, except for the fact that Flash
completely ignores the provided commands when the 'data' vector
is empty (if 'data' has even a single entry, then Flash will validate
that all of the commands have the correct amount of data to run).
One SWF that I tested relies on this behavior.
Casting the character to u8 and back to char caused some non-ASCII
non-control characters to be treated as control characters.
For instance the letter "ą" (U+0105) after casting to u8 and back
became ENQ (U+0005) which is a control character.
Some other letters worked, for instance the letter "ł" (U+0142)
became "B" (U+0042) and was not classified as a control character.
The test edittext_input was added to verify this behavior.
Our asc.jar doesn't seem to apply a version suffix to namespaces for
interface method definitions. This was causing these methods to
get marked as VM_INTERNAL when we loaded playerglobals, preventing SWF
from invoking these methods through the interface (e.g. having a
variable of type `IEventDispatcher`, and calling `dispatchEvent` on it)
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 was causing the `Object.prototype.toString` to throw error 1050,
instead of returning `[object Array]`, which was causing quite a few avmplus test failures.
The allocated-but-unconstructed object should be set on
the parent field before we construct the 'up state' object - this
is observable by ActionScript
We've now had two different bug reports involving Adobe AIR
SWFs, so I'm going to go ahead and start adding a framework
for AIR support.
This commit just adds a command-line option
`--player-runtime <flash-player|air>` (defaulting to `flash-player`),
and passes it along to the `Player`. The actual value is currently
unused - in a follow-up PR, I'm going to implement namespace versioning
for AIR.
This more closely aligns our code with the corresponding avmplus code.
A user-supplied index of '0' is special-cased, and we correctly
resume iteration when a public index mismatch is detected.
This ensures that this flag is set regardless of whether the
object is constructed by the timeline or from ActionScript
(it was previously only set when constructed by the timeline).
* core: Fix MorphShape inaccuracy on complex paths
It's inaccurate to interpolate the moveTo/lineTo deltas individually
because on complex paths they're often small integers, which won't
interpolate smoothly.
Instead, interpolate absolute positions.
This ensures that a re-entrant 'construct_frame' call (e.g. due
to a goto or AVM2 button) does not end up marking a MovieClip as
initialized too early.
This issue was causing us to run 'fire_init_and_complete_events' too
early, firing Loader events before we had actually finished (and
before the SWF had registered the relevant listeners).
Avmplus constructs a full `QName`, and uses the normal
Multiname matching logic. This would be a large refactor,
so I've just modified the existing method to properly
handle multiple namespaces.
I've also included a closely related fix - we should only treat
a multiname with the literal local name "@foo" as an attribute
when the namespace is the empty public namespace. We were incorrectly
reparsing multinames that contained multiple namespaces.
This preserves object identity across a serialization
round-trip. Unfortunately, we don't currently implement this
correctly in flash_lso, so I've added a stub message.
Once flash_lso is fixed, this code will start working. For now,
it just allows us to detect (via the stub) if this is actually
used by an SWF.
An Avm2 button appears to run a 'nested frame' during construction -
the same type of 'nested frame' performed by a goto. This will
run framescripts for all objects in the display hierarchy (including
orphans), resulting in an unusual child->parent framescript execution
order that DeathVsMonstars_decrypted.swf depends on.
This is a better default than using the lowest possible version,
as it allows things like AMF deserialization to see all properties
on the target object.
This only affects explicit Ruffle-side property setting - interpreted
code will continue to use the namespace version determined by the
SWF version.
This allows us to hide things like `MovieClip.isPlaying` from older
SWFs, which may define an `isPlaying` method without marking it
as an override.
This has the largest impact on public namespaces. There is no longer
just one public namespace - we now have a public namespace per API
version. Most callsites should use `Avm2.find_public_namespace()`,
which determines the public namespace based on the root SWF API version.
This ensures that explicit property access in Ruffle (e.g. calling
"toString") are able to see things defined in the user SWF.
This requires several changes other:
* A new `ApiVersion` enum is introduced, storing all of the api versions
defined in avmplus
* NamespaceData::Namespace now holds an `ApiVersion`. When we read a
namespace from a user-defined ABC file, we use the `ApiVersion` from
the root movie clip. This matches Flash Player's behavior - when a
child SWF is loaded through `Loader`, its API version gets overwritten
with the parent's API version, which affects definition visibility and
'override' requirements in the child SWF. Note that 'behavior changes'
in methods like `gotoAndPlay` are unaffected.
* The `PartialEq` impl for `Namespace` has been removed - each call site must now choose
to compare namespaces either by an exact version match, or by
'compatible' versions (a `<=` check) with `Namespace::matches_ns`
* `PropertyMap` now uses `Namespace::matches_ns` to check for a
compatible version when looking up a definition.
* When compiling our playerglobal, we pass in the `-apiversioning` flag,
which causes asc.jar to convert `[API]` metadata into a special
Unicode 'version mark' suffix in namespace URLs. We parse this
when loading namespaces to determine the API version to use for
playerglobal definitions (unmarked definitions use the lowest possible
version).
Unfortunately, this makes ffdec unable to decompile our
playerglobal.swc
I've going to file an upstream issue