This implementation just returns the size of the current loaded movie. The test is also deliberately written to be loose on timings so that it likely won't see a partially loaded movie. (I don't want it to be a test of load events, so I just wait a few frames, rather than the correct way of waiting for `onLoadComplete`.)
Until we support streaming file loads, we can't faithfully support these properties. Still, it's better to have them, just in case.
This also adjusts `MovieClip.unloadMovie` to do just that, instead of removing the clip from the display list. We also have to unload clips when loading new movies into them, since `unloadMovie` desugars to loading `""` as the URL.
Interestingly, this constitutes an implementation of `AsBroadcaster`. It appears Macromedia decided to implement event handling on `MovieClipLoader` in a very similar fashion to `AsBroadcaster`, down to invoking `broadcastMessage` and searching a `_listeners` property for listeners.
*De*implement the free function versions of the above, as well as their `Num` variants, since they don't actually exist as callables. Instead, the ActionScript compiler treats them as preprocessor functions that represent various forms of `ActionGetURL`/`ActionGetURL2`.
This function is part of `Avm1`, rather than a hypothetical `LayerManager`, because we're going to need to eventually construct layers differently for AVM2.
This allows the formation of `'static` futures that can still interact with a player. Async code will need to upgrade the weak reference in order to be able to interact with the player.
When `_parent` is preloaded in a `DefineFunction2` action,
we previously resolve it on the scope chain. This could cause
double borrow panics as the parent object could already be
borrowed, and also this matches the behavior of the official Flash
player.
Addresses #398.
This is the smallest positive number, not the most negative value.
This is actually the smallest positive subnormal f64, which Rust
does not provide a constant for. This is ~5e-324.
Match Flash's more closely when converting number to string:
* NAN -> NaN
* inf -> Infinity
* -inf -> -Infinity
* Use exponential notation for very large/very small
This is a little bit of a cheat by using Rust's number-to-string
formatting for exponentials, and shoving a sign in front of the
exponent.
Setting a property such as _x to undefined or null should have no
effect. This was working for v7+ SWFs because it would coerce to
NaN and we toss out NaNs. But on v6 and below, these coerce to 0
and would end up setting the property to 0.
Explicitly check for undefined/null and bail out. Fixes#380.
Also adjust the _visible setter, since this actually coerces to a
number (because of its legacy from SWFv4). For example,
_visible = "" should have no effect.
If a property is not set on the object passed to Color.setTransform,
then that channel is left unmodified. This fixes invisible objects
in some games (fixes#369, addresses #380).
Also improve handling of wrapping/invalid values to better match the
behavior in the Flash Player (some work pending on #193).
If you goto past the final loaded frame of a timeline, for example,
with gotoAndStop(9999), this seeks to the final frame on the
timeline, but it doesn't run the actions on this frame.
MovieClip::goto_frame now will not run the final frame actions if
the target frame was not reached.
Use the same code path for the global GotoFrame2 action and
MovieClip.gotoAndX, which properly handles out-of-range and invalid
values like NaN.
Fixes Disorderly hanging on game start
(https://www.newgrounds.com/portal/view/121896)
Add these methods that will explicilty coerce a value to an int,
following the wrapping behavior in the ECMAScript specs (ToInt32,
ToUInt32, ToUInt16).
This also fixed an off-by-one error for negative numbers in the
previous implementation.
These will call `valueOf` if necessary. AVM code that requires an
integer will probably use one of these (`coerce_to_i32` usually).
When setting a variable in a function-local scope, if that variable
has not been defined in the function scope, it should be defined in
the executing movieclip's scope. Previously it would get defined
in the function's scope. Changed Scope::overwrite to Scope::set,
and modified the behavior to stop traversing and define the value
when it hits a movie clip Target scope.
Also, modified With scopes to properly add onto the end of the scope
chain.
I'm not entirely sure how to test this one - the list of errors that Flash kicks out for XML and the list of errors that `quick_xml` kicks out don't line up at all; so I just ensured that any error is a negative number (currently the one for OOM errors) and stuck whatever errors *did* match up together.
Consequently I don't know entirely *how* to write tests for this.
`idMap` is a strange property; it's only populated with nodes which had a given `id` *at the time of parsing*, and said nodes continue to be referenced even if the node is removed from the document. I have yet to find a way by which nodes can be deleted from `idMap`.
It also takes expandos, so this has to be a new retained object on the XML document. I originally considered not creating *another* `Object` impl and populating a regular `ScriptObject` with nodes, but that meant we couldn't lazy-instantiate their AVM1 side counterparts. Boo. :/
There is a bug in `quick_xml` - or at least, I *think* it's a bug - where the `unescaped` method of `BytesStart` yields a bunch of attributes if you have slightly invalid crap in your tag like `xmlns:`. To work around it, I turned off unescaping; we're instead using `name` and ignoring unescaping. This will probably fail somewhere.
I can't write proper tests for this because our underlying XML parsing technology doesn't let us do what AS2 XML does: just copy the first xml tag out of the document and into a string. No, seriously, anything at the start of the parsed XML that starts with `<?xml` and ends with `?>` gets copied into Flash's moral equivalent of `xml::Document` as a string. We parse the whole thing, which is wrong, so we'll need to additionally retain the original xmldecl string in order to pass the test I wrote for this.
Fixes a bug where new XML("<node />").childNode[0].parentNode did NOT refer to the overall document object, but to a phantom text node.
This is because the swap operation used to construct an XMLObject's node in-place was happening AFTER parsing, which means that referents already existed to the temporary XMLNode created by XMLObject::new. swap is not to be called after tree structure has been created; it does not update referents to the swapped nodes.
In the future I should examine the implications of explicitly reconstructing already existing nodes, e.g. through XML.apply(some_xml). Right now, the existing node will be swapped with a new one, and two nodes will exist pointing to the same script object, which is a huge problem with our overall design. We should, at the very least, disassociate swapped nodes from their script object, just in case they still have referents.
Ideally, we wouldn't have to swap nodes, but to avoid a swap, I'd have to instead have a second layer of indirection just to hold a rewritable pointer that every XMLObject points to. This isn't really worth it unless I HAVE to do it, so I'm not going to do it.