New Proposal: Incorporating Resume, Event-based Discovery, MessagePort

Hi,

after the previous discussions and Anton's change proposal we
have been thinking about how to incorporate this into our spec.
Below you find our idea on how to incorporate:

- Discovery, including resuming existing sessions, event-based
- Handling of multiple displays (representation as a
  list/sequence)
- Moving from WindowProxy to MessagePorts for less
  tight coupling, relaxing implementation requirements

With that in mind, here is a new bit of example code showing how we
envision things to work, split into opener context and player side.

Your feedback and criticism on this proposal is highly welcome.
We put it out here on the list before starting to edit the spec draft.


### On the opener context side:


var discovery,
    playerURL = "http://example.com/player.html";

discovery = navigator.presentation.discoverScreens(playerURL);
discovery.addEventListener("discover", discovered);

function discovered(e) {
  // Here we receive at least one or a list of screens
  // in either "presenting" or "available" state for our playerURL.
  // - "presenting" here means, that there is an existing
  //   persistent session for playerURL on that screen.
  // - "available" means that using the connect() method the consumer
  //   of the API can launch the playerURL on this screen.

  // Discovery is complete, picking the first screen,
  // we proceed to the next step:
  resumeOrPresent(e.screens[0]);
}


This is the basic discovery procedure: We find screens that are suitable
or already showing a session for playerURL. After the asynchronous call
to discoverScreens() the screensDiscovered event handler is called when
there is at least one screen found.

Then, in this example, we pick the first screen and move forward to the
next step, which would be trying to present our playerURL content
on it or resume an existing session:

Open issue here: Since there is no good way to choose if there are
multiple results in e.screens, alternatively we could have only
one object here, selected by the user during the permission request
dialog, e.g. e.selectedScreen.


function resumeOrPresent(screen) {
  // Watch for state changes to either "disconnected" or
  // (see below) "presenting", if we haven't launched a sesion yet.
  screen.addEventListener("statechange", screenStateChange);

  if (screen.state == "presenting") {
    // Screen is already in state "presenting" for our playerURL.
    // We can resume communication with the session.
    communicate(screen);
  } else {
    // Screen is "available", we can launch our playerURL content on it.
    screen.present( /* Optionally with options
                       object { "persistent" : true }. */ );
  }
)


First, we register our event handler for getting notified about any state
changes, either to "presenting" or "disconnected.

Now two situations can occur:

In the case of existing session, the screen
is in state "presensting" and we can resume communications with it right
away.

The other possibility is that the screen is in "available" state, in
which case we would like to launch our player content on it. We do that
by calling present().


function screenStateChange(e) {
  if (screen.state == "presenting")
    communicate(screen);

  if (screen.state == "disconnected")
    console.log("Screen disconnected.");
}


If we previously were in state "available" and had called present(), we
now reach state "presenting" and can pick up communications with the screen.

In the case of disconnection or an error resulting from calling present()
we end up in state "disconnected". So we're no longer presenting on
the screen.


function communicate(screen) {
    // Communicates with presentation window.
    screen.port.postMessage(/*...*/);
    // Receiving messages.
    screen.port.onmessage = function(e) {
      console.log(e.data);
    };
}

The communicate function is just an illustration for web messaging between
the opener context and the screen.




### On the presentation window side, e.g. "player.html":

var portToOpenerContext;
navigator.presentation.addEventListener(
  "connect", function(e) {
    portToOpenerContext = e.port; };
portToOpenerContext.onmessage =
  function(e) { console.log(e.data); } // Receiving messages.
portToOpenerContext.postMessage(...); // Sending messages.

On the presentation window side, the consumer of the API has to register
for the connect Event on the navigator.presentation object to receive
the other end of the MessagePort and can then proceed to communicating
both ways.

An open issue here is: Do we need to notice disconnects in this
direction? Should this just be handled through an application defined
protocol which acknowledges messages through the MessagePort? Or do we
wrap the MessagePort here as well? Or should we have an additional
ondisconnect event?



Again, we'd be very happy to receive your feedback, criticism or improvement
proposals before we start editing the spec.

We believe the above code solves the combination of uses cases and improvement
requirements, especially the new discovery requirements quite well.

Dominik

Received on Tuesday, 21 January 2014 15:38:58 UTC