2

So i have a situation where i want to pass a object of a class say 'MyBigAwesomeClass' from a child to a parent. I import the class definition into both the parent and child.

Now, if i load the child swf from a location that is relative to the location of the parent , all is fine, however the moment i load it using a full absolute path, it treats the definitions for 'BigAwesomeClass' in parent and in child as different and does not allow an object of the type 'BigAwesomeClass' to be assigned to an object of the same in the parent class.

I am totally stumped, and have banged my head over ApplicationDomains, including using this code

loader.contentLoaderInfo.addEventListener(Event.COMPLETE,swfLoaded);
var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain); 
loader.load(new URLRequest(_file.url),context);

To absolutely no avail. Any ideas as to what can i do it fix this ?

Thanks in advance

grapefrukt
  • 27,016
  • 6
  • 49
  • 73
Khalid Bajwa
  • 173
  • 3
  • 11

4 Answers4

1

According to Adobe's documentation (Loader#securityDomain):

In order for import loading to succeed, the loaded SWF file's server must have a policy file trusting the domain of the loading SWF file.

The trick is telling Loader to check the crossdomain file when loading the swf, by passing true as the first parameter when creating the LoaderContext, eg:

var request:URLRequest = new URLRequest(_file.url);
var context:LoaderContext = new LoaderContext(true, null, SecurityDomain.currentDomain);
var loader:Loader = new Loader();
loader.load(request, context);

The accompanying cross-domain.xml should be located in the same location as the child SWF, or in one of its parent folders. Here's a non-restrictive cross-domain file according to documentation from Adobe:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <site-control permitted-cross-domain-policies="all"/>
    <allow-access-from domain="*" secure="false"/>
    <allow-http-request-headers-from domain="*" headers="*" secure="false"/>
</cross-domain-policy>

One other thing that might make this easier for you, is to pass the interface instead of the class, which would effectively bypass conflicting code. This will work because the child inherits the interface from the parent class by default at runtime (see Loader#applicationDomain point #1). The child class can then safely instance it's own version of each object as long as that object adheres to the interface. For example:

var applicationDomain:ApplicationDomain = loader.contextLoaderInfo.applicationDomain;
var classDefinition:Class = applicationDomain.getDefinition("MyBigAwesomeClass") as class;
var instance:IMyBigAwesomeInterface = new classDefinition() as IMyByAwesomeInterface;

The definition for MyBigAwesomeClass would then look something like this:

public class MyBigAwesomeClass implements IMyBigAwesomeInterface 
{
    ...
}
Luke Van In
  • 5,215
  • 2
  • 24
  • 45
  • My bad, should have clarified that this is an AIR App, loading from local file system therefore the policy files are irrelevant. The second approach i think should work, and i thought of this, but its too messy. I have atleast 3 different classes that i need to share between the two SWFs and there is a lot of communication going on in too many methods and it seems a very non-elegant and cumbersome approach to extract definitions from the child classes everytime. What i would want is a way for the child to be compatible with the definations in the parent. Any ideas ? – Khalid Bajwa Aug 13 '11 at 22:36
  • If you refer to link to the documentation for the different options for ApplicationDomain it says: " if the parent wishes to use the child's classes, it must call ApplicationDomain.getDefinition() to retrieve them", and "If the child attempts to define a class with the same name as a class already defined by the parent, the parent class is used and the child class is ignored.", and "The only way either side sees the other's classes is by calling the ApplicationDomain.getDefinition() method". So it seems getDefinition() is the only way to override a class defined in the child. – Luke Van In Aug 14 '11 at 10:47
  • And using the defination of the parent and ignoring that of the child is exactly what i wanted. The problem , i have discovered, as i mention in my own answer above, that it only works if the parent and child are in the same security sandbox. – Khalid Bajwa Aug 14 '11 at 12:25
1

Well it turns out its a Sandbox Issue. Files that are not part of the installer are placed in a different Security Sandbox, and since they reside in different sandboxes, the child when imported into the parent does not inherit the definitions of its Parent in the ApplicationDomain and two separate definitions exist which are incompatible. Sadly, there seems to be no direct way of resolving this. Adobe does allow communication between sandboxes via the SandBoxBridge, but that forces you to use Object type, which kind of defeats the purpose of the whole thing. As far as i can tell there is no way for classes in two different sandboxes to be compatible even though they are exactly the same. I guess its back the painfull world of no strict typing with Objects.

Khalid Bajwa
  • 173
  • 3
  • 11
  • Some links to help anyone with a similar issue. http://www.senocular.com/flash/tutorials/contentdomains/?page=2 A superb article on ApplicationDomains. In particular refer to the following sections:Same-definition Collisions,Application Domain Inheritance) Adobe's Description of sandbox restrictions in AIR and the way around that:http://livedocs.adobe.com/flex/3/html/help.html?content=security_5.html#1092959 – Khalid Bajwa Aug 14 '11 at 12:27
0

I would agree with you that the problem probably has to do with the parent & child's Application Domain, but in order to answer more accurately , it 'd be good to have some example use of the class you would like to share between the parent and the child.

In theory it seems the problem could be avoided if a class was defined for the child as well... Here's a very basic example that in my opinion should work wherever you load the child from.

package com.example.test
{
   public class Parent extends Sprite
   {
      private var child:Child;
      private var shared:SharedClass = new SharedClass();

      public function Parent()
      {
         loadChild();
      }

      private function loadChild():void
      {
         // load process
      }

      private function loadComplete(event:Event):void
      {
          child = event.currentTarget.content as Child;

          if( child != null )   
             shared = child.shared;
          // remove event etc...
      }
   }
}

package com.example.test
{
   public class Child extends Sprite
   {
      // I use a public var here , but you can use a getter...
      public var shared:SharedClass;

      public function Child()
      {
            shared = new SharedClass();
      }
   }
}

package com.example.test
{
   public class SharedClass
   {
      public function SharedClass()
      {
           trace('Hello from Shared Class');
      }
   }
}
PatrickS
  • 9,539
  • 2
  • 27
  • 31
  • This as a matter of fact is exactly what i am doing. But this line child = event.currentTarget.content as Child; Does not work because for some reason flash player is treating the defination for 'Child' class as being different for parent and child, which is weird, because according to Adobe's documentation should not be happening, in case of conflicts, the child's defination is discarded and the parent's is adopted. Any more ideas ? – Khalid Bajwa Aug 14 '11 at 00:10
  • what's the output if you trace event.currentTarget.content? – PatrickS Aug 14 '11 at 07:17
0

One option is to use implement a method in the child class to return an instance of the class using it's own applicationDomain, eg:

public class Child extends Sprite implements IMyBigAwesomeClassLoader
{
   public function getMyByigAwesomeClass():IMyBigAwesomeClass
   {
       var classDefinition:Class = applicationDomain.getDefinition("MyBigAwesomeClass");
       var instance:IMyBigAwesomeClass = new classDefinition() as IMyBigAwesomeClass;
       return instance;
   }
}

The IMyBigAwesomeClassLoader would look like this:

public interface IMyBigAwesomeClass 
{
  function getMyBigAwesomeClass():IMyBigAwesomeClass;
}

The parent clip would then use this to get the instance from the child when it's loaded:

public function loadCompleteHandler(event:Event):void
{
  var myBigAwesomeClassLoader:IMyBigAwesomeClassLoader = (event.target as Loader).content as IMyBigAwesomeClass;
  myBigAwesomeClass = myBigAwesomeClassLoader.getMyBigAwesomeClass();
}

The reason to use interfaces here is to decouple the class definition from its implementation. Even though the class in the parent and child SWFs have the same name, Flash treats them as different classes. The interface tells Flash that they can be used in the same way even though they are different.

Flash uses static typing, which means that once a class is defined it will never change, which means it will never over-write one class definition with another. So if there are two classes with the same name, it is necessary to use getDefinition to resolve the conflicting names. If you want to avoid this added complexity, you can use different names or name-spaces for the class in the parent and child SWFs.

Luke Van In
  • 5,215
  • 2
  • 24
  • 45