This project has moved. For the latest updates, please go here.
DesktopGap’s extension loading mechanism is dominated by MEF – Micrsoft’s Managed Extensibility
Framework
: By deriving from the corresponding base
classes the container takes care of the instantiaton (the behavior is determined by attributes
on each class) and injects additional resources into each AddIn. These base classes share
some aspects:
  • A "Name" property for identification
  • Methods which are called before loading (OnBeforeLoad()) and unloading (OnBeforeUnload()) the AddIn
  • Disposability ensures a cleanup after unloading
This also implies the following: Names need to be unique and do not include the public key
token, which makes them easily exchange- and filterable. Since "shared" instantiation means
that an AddIn executes singleton behavior, "non-shared" AddIns are created for each HTML
document – meaning that each iframe has its own set of "non-shared" instances, but the same
"shared" ones. Thus, for a shared instance, the OnBeforeLoad(), OnBeforeUnload() handlers
will be called each time it is (un)loaded for a document.
The two types of extensions Events and Services have more specialized base classes:
ExternalEventBase and ExternalAddInBase. These base classes provide additional resources
to the AddIns, such as resolving resource handles used by JavaScript instead of file handles,
an improved web client to download things, and access to certain events and data offered by
the browser (e.g. when a Pop Up is opened).


public abstract class AddInBase : IDisposable, IEquatable<AddInBase>
{
  public abstract String Name { get; }
  public virtual void OnBeforeLoad (HtmlDocumentHandle document)
  {
  }

  public virtual void OnBeforeUnload (HtmlDocumentHandle document)
  {
  }
// ... 

External Events

In order to get type-safe, customizable events that are passed through to the correct document
in JavaScript, some thoughts are required:
  • How to store callbacks?
  • How to identify the callback’s associated document?
  • What arguments should be passed to the callback?
  • If an event fires, should all subscribed callbacks be notified?
  • How to announce custom events to the container?
The first question is answered by the technology itself: Functions are to be called by their
names, hence these can be used for identification; the association with a document is also implicitly
solved, by using one EventDispatcher (which also handles subscription) per document.
Arguments that should be passed to the callback are JSON-serializable objects, a necessary
step because passing COM-visible objects would only allow simple data types. Furthermore,
when registering, every subscriber can pass in an arbitrary object that the AddIn can check
before allowing to call back, thus making callbacks under certain conditions available (such as
drag and drop on specified elements). Lastly, publishing the events to the EventDispatcher
is done with a specific delegate signature for the AddIn’s events and the EventDispatcher to
subscribe using the Visitor pattern.

[InheritedExport (typeof (ExternalEventBase))]
public abstract class ExternalEventBase : EventAddInBase
{
  [Import (typeof (IResourceManager))]
  public override IResourceManager ResourceManager { get; protected set; }
}

Example

[PartCreationPolicy (CreationPolicy.Shared)]
public class MessageEvent : ExternalEventBase {
  private event ScriptEvent MessageReceived;
 
  public MessageEvent () {
    MessageHandler.MessageReceived += (s, e) => {
        if (MessageReceived == null)
          return;
        MessageReceived (this, "MessageReceived", new MessageData { Message = e.Message });
      };
  }

  public override string Name {
    get { return "com.example.MessageEvent"; }
  }
  public override void Dispose () { }

  public override bool CheckRaiseCondition (Condition argument) {
    return true;
  }

  public override void RegisterEvents (IEventHost eventHost) {
    eventHost.RegisterEvent (this, ref MessageReceived, "MessageReceived");
  }

  public override void UnregisterEvents (IEventHost eventHost) {
    eventHost.UnregisterEvent (this, ref MessageReceived, "MessageReceived");
  }
}

External Services

Services are much simpler than events: Thanks to COM, exposing public methods as functions
in JavaScript is effortless (only add the ComVisible attribute as in the example below). Firstly
however, the AddIn object has to be passed to JavaScript by retrieving it by its name: DesktopGap’s
specialized ObjectForScripting offers a GetService() method (or from JavaScript
perspective window.external.GetService()) which returns the corresponding object to the
passed name. Afterwards every public method is exposed and can be called.

[InheritedExport (typeof (ExternalServiceBase))]
[ComVisible (true)]
public abstract class ExternalServiceBase : ServiceAddInBase
{
  [Import (typeof (IResourceManager))]
  public override IResourceManager ResourceManager { get; protected set; }
}

Example

[ComVisible (true)]
[PartCreationPolicy (CreationPolicy.Shared)]
public class GuidService : ExternalServiceBase {
  public GuidService () { }
  public override string Name {
    get { return "com.example.GuidService"; }
  }

  // ...
  public string CreateGuid () {
    return Guid.NewGuid().ToString();
  }
}

Last edited Jun 28, 2013 at 1:18 PM by clma, version 1

Comments

No comments yet.