Cross Domain Skinning and Styling

Overview

There are two scenarios where Gumbo applications do not work:

  1. Using multiple modules and having a component in these modules that is not in the base application. For example, a simple base application that loads two modules, and each module contains a List (the base Application does not contain a List).
  2. Using a module and runtime CSS. If the module contains a component that is not in the main application, runtime CSS will not be able to style it.

Both of these scenarios end up throwing an RTE with a cryptic message like: "Type spark.controls.HScrollBar (0x012844) is not assignable to spark.controls.HScrollBar".

Details

The Gumbo skinning architecture causes problems when skins and components are in different application domains. The problems are due to strong typing between the component and skin, specifically the hostComponent property on skins and [SkinPart] declarations on components.

The problems arise when loading multiple modules or using runtime CSS and modules. By default, modules (including runtime CSS) are loaded into separate application domains. If the module contains a type that is not already present in the main application domain, it loads a local version of the type, which can end up with the same type loaded by multiple modules. Even though the class is the same (ie mx.components.FxHScrollBar), the types are not assignable or interchangeable across module boundaries. This results in a cryptic RTE.

Loading a single module at a time, or using runtime CSS without modules does not cause any problems.

There are four different ways this problem can be solved.

Solution #1 - Weak Typing

Reference between component and skin are typed as "Object". There is no compiler or runtime checking to see if types are compatible. All access is done with dynamic lookup. If the lookup fails, an RTE is thrown.

Pros:

  • Easy to implement
  • Does not introduce any requirements/dependencies on the main application.

Cons:

  • No code hinting or compile-time checking makes development much more difficult and error prone. We could have the compiler and/or tools help out here.
  • Debugging is harder. Examples include:
    • Incompatibilities may not be immediately obvious. For example, you may need to disable a component before you find a bug in the skin.
    • Unintentional dependencies. With strong types, you know right away if you are accessing a valid property or method. With weak typing, you could code something that accidentally "works" with one skin, but then fails when a new skin is loaded.

Solution #2 - Strong Typing with interfaces and/or base classes

Use interfaces or core base classes for contracts between components and skins. These interfaces/classes must be loaded into the main application domain before any modules are loaded.

Pros:

  • Preserves strong typing - code hinting, compiler checking, immediate RTE when trying to assign incompatible types, etc.
  • Incompatible skins throw an immediate (and descriptive) RTE rather than an obscure RTE sometime later.

Cons:

  • More work for component development. As each component is developed, you need to make sure any skin parts only use the pre-defined types. There would be no compiler help here.
  • Any access to the skin parts beyond the base types require dynamic access (ie myPart["someProperty"]).
  • Shared types need to be loaded into the main application domain. This is a manageable problem for skin parts, but probably not manageable for hostComponent.

Solution #3 - Use RSLs for shared types

Keep our strong typing with "heavy" types. If you are using multiple modules, or modules and runtime CSS, you need to load the framework as an RSL, which automatically makes all classes available in the main application domain.

The current plan is to use RSLs by default.

Pros:

  • No dev time, and does not impact Spark component development.

Cons:

  • Requires shared types to be in the main app domain (either explicitly loaded, or included in an RSL).
  • Complicates development of custom components in multi-module or module+runtime CSS applications. Specifically, if the custom component has any skin contracts that utilize non-framework types.

The main drawback with this approach is 3rd party component development. For example, if we Sparkified the iLog components, they would not be usable in the scenarios mentioned at the top of this document. The only solution is to create an RSL of the components, and load it by the application, or to create a module containing the components and explicitly load it into the main app. Both of these solutions require the main app to have knowledge of all components loaded by modules, and this may not be possible in a portal environment.

Another consideration is memory management. All shared types need to be loaded into the main app domain, which means more memory will be required to run the applications.

Solution #4 - Load styles on a per-module basis

When loading a module, any styles associated with the module are scoped to that module only (currently, all styles are globally scoped to all modules). If you load multiple modules you may end up with multiple copies of some styles.

When loading a runtime CSS module, the styles and types are loaded into all loaded modules in addition to being loaded into the main application styles. This ensures that all modules have a unique (and valid) reference to all required types.

This solution has been prototyped. Details [here].

Pros:

  • Does not impact component development. Once this solution is implemented, all components will benefit.
  • Fixes another issue with modules. Currently, if a loaded module introduces new style selectors, that module cannot be unloaded. If we do this change, it will be possible to fully unload any module.
  • This is the only solution that scales up for large app development.

Cons:

  • Time to implement, test, and debug.
  • Requires API changes to StyleManager.

Recommendation

Stick with #3 for Flex 4.0, and schedule #4 for a dot release. Solution #4 does involve some API changes to the style manager. These will be done in a backward-compatible manner by deprecating the old methods and providing new alternates that include a style "context".

You must be logged in to comment.