I have two SWFs, call them A and B. They will never be deployed to a website, and are being used for tools. B depends on A - some of the classes in B extend classes in A.
I am now creating a 3rd SWF, call it X. X is attempting to load A and B using a flash.display.Loader and flash.net.URLRequest. A and B paths are pushed into an array, and then called in a loadLibrary function like so:
public class LibraryLoader
{
private static const CLASS_NAME:String = "LibraryLoader";
private var _libraries:DisplayObjectContainer;
...
public function loadLibrary(callback:Function, libName:String):void
{
trace("loadLibrary('" + libName + "')");
var loader:Loader = new Loader();
loader.name = libName;
var listener:Function = function(e:Event):void
{
trace("finished loading '" + libName + "', event: " + e);
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, listener);
_libraries.addChild(loader);
callback();
}
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, listener);
loader.load(new URLRequest(libName));
}
Problem is, when I load B, it throws an error. Here's the output:
loadLibrary('C:\path\to\A.swf')
finished loading 'C:\path\to\A.swf', event: [Event type="complete" bubbles=false cancelable=false eventPhase=2]
loadLibrary('C:\path\to\B.swf')
[Fault] exception, information=VerifyError: Error #1014: Class a.class.in::A could not be found.
This class is inside A, and B depends on it.
I Googled around and found information about Security permissions and sandboxes - perhaps I need to set up some trust between these SWFs. That's fine, but I can't seem to figure out how to do it properly.
For one, I tried setting up a LoaderContext like so (when loading both SWFs):
var context:LoaderContext = new LoaderContext();
context.applicationDomain=ApplicationDomain.currentDomain;
loader.load(new URLRequest(libName), context);
No dice, there; same error. In addition, attempting to set context.securityDomain throws out:
[Fault] exception, information=SecurityError: Error #2142: Security sandbox violation: local SWF files cannot use the LoaderContext.securityDomain property. file:///C|/path/to/X.swf was attempting to load file:///C:/path/to/A.swf.
In case it makes a difference, A and B are being compiled using compc.exe from the Flex SDK (3.6). I generate both a SWF and a SWC for each - SWFs for runtime, and SWCs for compiling - using compc. Here is the command line for compc:
compc.exe -output C:\temp\dir -source-path -include-sources C:\path\to\A\source -directory=true -incremental=true -debug=true -use-network=false
compc.exe -output C:\path\to\A.swc -source-path -include-sources C:\path\to\A\source -incremental=true -debug=true -use-network=false
compc.exe -output C:\temp\dir -source-path -include-sources C:\path\to\B\source -directory=true -incremental=true -debug=true -external-library-path+=C:\path\to\A.swc -use-network=false
After the first and 3rd compilations, a "library.swf" file is dropped into the temporary directory listed. I take those SWFs out and rename them to A.swf and B.swf, dropping them where I want them.
My project for X is built in FlashDevelop 4.0.1 for Flash Player 10.1.
I know that a.class.in::A is included in SWF A. I am loading these SWFs in a Scaleform runtime with no issues, and am therefore convinced there is some kind of issue with how FlashPlayer is doing things.
How can I get B to see the classes inside A when I load A and B from X?
There are three comments:
You were right, when you specified the applicationDomain to ApplicationDomain.currentDomain; without this loader loads swf to separate child domains with common parent, so they don't see classes of each other.
With common application domain, classes from A must be accessible from B, so I see only one possibility for the error: A really hasn't class some.class.in.A. Try to check is this class is included to A (for example by Sothink SWF Decompiler) or simply check if this class is used explicitly in the code in A. if it isn't used it not be included, nut you can force to include this class by adding it in the code: simply add some.class.in.A some where in the A without new statement.
context.securityDomain is for other purposes, it is used only when swf files loaded by http, and don't applies locally.
Related
How can I patch actionscript without constantly rebuilding sfw?
There is a fairly large actionscript project that I need to modify and resulting swf is used on a live site. The problem I have is that I need to make quick small updates to the swf and it's not acceptable to update the swf on live site ten time a day (I don't control that part, I need to ask another person to put the result on live site).
What options do I have to workaround that issue? I'm a complete noob when it comes to actionscript and all flash related stuff and I'm not even sure what is possible and what isn't. I'm thinking about the following approaches, which ones are possible/acceptable?
Imagine that live site is on www.livesite.com/game.html and this page loads www.livesite.com/flashgame.swf. In that flashgame.swf among many others there is a class com/livesite/Magic.as that gets instantiated and instance of that class has a member variable xxx123 of class com/livesite/MagicWork.as. I only need to modify this MagicWork class. Now, I simply modify it, build and ask to put updated flashgame.swf live. So, I want to avoid that manual step.
All my ideas can be split in two basic approaches: 1) keep flashgame.swf totally unmodified and then load flashgame.mod.swf that contains alternative implementation of that MagicWork class, then using javascript access internals of instance of that Magic class and update its xxx123 member to be an instance of MagicWork class from flashgame.mode.swf. I'd need to modify game.html to load my javascript so that my js file would load flashgame.mod.swf and patch code inside flashgame.swf. By patching I mean javascript-style overwriting of Magic.xxx123 to a new value. flashgame.mode.swf would ideally reside on my own host that I control. Is that kind of stuff possible, if not what's not possible?
2) I could make one-time change in flashgame.swf so that it would effectively load itself my own code at runtime and patch it's xxx123 member. Is that possible?
I had already written a note about loading runtime shared libraries previously. I'll put the most essential parts of the process here, and add a link to the full article at the end.
You need to tag your main application entry point in the following manner.
[Frame(factoryClass="Preloader")]
public class Main extends Sprite
{
}
Then create a class called Preloader.
public class Preloader
{
public function Preloader()
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.loader_completeHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, this.loader_ioErrorHandler);
var request:URLRequest = new URLRequest("math.swf");
var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
loader.load(request, context);
}
private function loader_completeHandler(event:Event):void
{
var mainClass:Class = getDefinitionByName("Main") as Class;
var mainInstance:Main = new mainClass();
this.addChild(mainInstance);
}
}
The full implementation of the Main class is like this.
[Frame(factoryClass="Preloader")]
public function Main()
{
var integer:IntegerArithmetic = new IntegerArithmetic(); // Type declared in math.swf
var operand1:int = 10;
var operand2:int = 10;
var result:int = integer.add(operand1, operand2);
}
Deploying Runtime Shared Libraries
The confusing bit about using a runtime shared library is realizing that the SWF has to be extracted from the SWC at the time of deploying the application. This was not immediately obvious and I ended up spending days placing a compiled SWC file in various locations and wondering why the application was unable to load it at runtime. An obscure article on the Adobe website made explicit this particular step and set things straight.
The full article along with the same example is available at http://www.notadesigner.com/runtime-shared-libraries-with-plain-actionscript/.
What i have
Large amount of swfs without it's sources (so i couldn't modify its)
What i need
To load and play this swfs with my AIR app.
The problem
The problem is that this swfs seems having
Security.allowDomain('*')
in their source, so they would throw
SecurityError: Error #3207: Application-sandbox content cannot access
this feature.
after i load it. I know that Air doesn't need to use this line, but instead of ignoring or warning on it my full app would stop to executing after loading one of this swfs. If only i could try/catch this string, but as i said i don't have an source of that swfs, so the only thing i could do is to modify my AIR app.
What i tried
What i already tried is to catch all errors inside loader by doning
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loaderIOErrorHandler);
private function loaderIOErrorHandler(e:IOErrorEvent):void {
e.preventDefault();
}
but it seems it isn't catch errors inside loader at all
Update
I couldn't share one of this swfs, but here is simulation i made that reproduce problem https://www.dropbox.com/s/0spbdzijfpboi47/problematicSwf.swf?dl=0
Here it's init code
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
Security.allowDomain('*');
tf = new TextField();
tf.text = 'Me loaded!';
addChild(tf);
}
As you could see it is crashing on allowDomain inside loaded swf.
And here is how i load it
var ctx:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
loader.load(new URLRequest(path), ctx);
This is a typical security restriction but it's a very strict one and it's purpose is to make sure the served swf will never be used outside of what it was made for in the first place.
So the short answer to your problem is this: externally loaded swf that are sandboxed with "Security.allowDomain('*');" will not allow a sandboxed AIR app to interact with them in anyway. Instead those swf will be able to interact with the parent AIR app under restrictions via a sandbox bridge.
If truly you cannot modify those swf then you will never be able to add them to a display list in a AIR app or call any of their methods. A sandbox bridge will also be of no use to you.
It's not the answer you want to hear I bet but it's the only one you'll get.
I have a .swf file that uses the following code:
private function onClick(e:MouseEvent):void {
var popUpClass:Class = getDefinitionByName(e.currentTarget.popUp) as Class;
popUpContent = new popUpClass();
}
However when I load that swf into another "container" swf I am getting a Reference Error #1065. I have been reading several posts on stack overflow (for example, this one and this one) but I am still confused as to what to do.
For example, do I change the code in my "loaded swf" to reference it's own application domain (not the domain of the container swf?) and if so, how do I do this exactly?
OR do I load the swf file and assign it it's own application domain in the code of the container? (not the code for the loaded swf?)
Also, my "e.currentTarget.popUp" just has a short name "PopUpWhateverName" should I be giving it a full package name like "com.MyLoadedSwf.Assets.PopUpWhateverName"?
Yes , full package name : getDefinitionByName("com.MyLoadedSwf.Assets.PopUpWhateverName")
but You can also try :
var popUpClass:Class = e.currentTarget.popUp.constructor;
popUpContent = new popUpClass();
I'm loading as2 swf into AIR application. It works properly when loaded from file. But when loaded from bytes, it is broken in some way (it reacts to mouse, but some elements are inactive)
var bytes:ByteArray = ... //loaded from resources
var loader:Loader = new Loader();
var context:LoaderContext = new LoaderContext(false);
context.allowCodeImport = true; //this is neccessary
// Method 1 - blocks some scripts in loaded SWF
//context.applicationDomain = new ApplicationDomain();
// (application domain has no effect with as2 swf)
//context.securityDomain = SecurityDomain.currentDomain; //gives error 2114: securityDomain must be null
loader.loadBytes(bytes, context);
// Method 2 - loads properly
//loader.load(new URLRequest(file.url));
So why not just load it from file? My resources are protected with encryption and I can't dump them to disk - they must still be protected.
What tricks may exist to load from bytes properly?
There is similar question, but in my case as2 causes more problems.
AS2 and AS3 use different runtimes (bytecode is different) so you won't be able to properly execute any AS2 bytecode in the AS3 runtime. you are basically injecting AS2 code into your AS3 application, so it ain't gonna work :/
According the the documentation for LoaderContext you should only use the applicationDomain property only when loading ActionScript 3.0 SWFs. Try dropping that parameter (or setting it to null) and see what happens.
It seems that old SWF movies (AS1 and AS2, which require AVM1) loaded into an AIR app with load get put in their own domains, but those loaded with loadBytes share a domain. So if you have multiple AVM1 SWFs loaded with loadBytes their _global properties will clobber each other. This affects the Flash MX UI components (ca. 2002).
I can't be the only one trying to package ancient Flash files in AIR apps, so I figure this info may be useful to someone.
I have two SWF files which I shall call container and slave. The slave file contains a movieclip that extends from a class I shall call base. base extends MovieClip and is compiled into an SWC. slave includes this SWC as a runtime library, while container includes it as merged (and does reference it so it should be compiled into the container SWF).
The container loads the slave like so:
bgURLRequest = new URLRequest(slaveUrl);
var context:LoaderContext = new LoaderContext(false, new ApplicationDomain( null ), SecurityDomain.currentDomain);
bgLoader.load(bgURLRequest, context);
When this loading happens, I get the error that class base was not found. I suspect this has something to do with the presence of the ApplicationDomain in there. I'm not sure what it does exactly, since I didn't write this loading code myself (but I do know that it's there for a reason so it can't be simply removed).
How to fix?
This is the solution
new LoaderContext(false, new ApplicationDomain( ApplicationDomain.currentDomain ), SecurityDomain.currentDomain);
Use ApplicationDomain.currentDomain and track the available classes with the super getDefinitionNames available at: http://etcs.ru/pre/getDefinitionNamesSource/
At least you'll know whats available in the loaded swf.