Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow page to designate a default presentation URL #26

Closed
markafoltz opened this issue Nov 4, 2014 · 19 comments
Closed

Allow page to designate a default presentation URL #26

markafoltz opened this issue Nov 4, 2014 · 19 comments

Comments

@markafoltz
Copy link
Contributor

Some user agents may wish to allow the user to initiate presentation from the browser instead of by triggering it via an action on the page. This is analogous to choosing "Print" from the browser menu instead of calling window.print().

In this scenario, the page should be able to designate a default presentation URL that will be shown when the user initiates presentation via the browser. This issue tracks the work to define the specification and the user-agent behavior around this URL.

@anssiko anssiko added the v2 label Dec 18, 2014
@avayvod
Copy link
Contributor

avayvod commented Mar 9, 2015

Hey everyone,

as I'd like to implement this bit of the API in Chrome, I'd like to try to get a consensus on the thread [1] and add the result to the spec if possible. As I see it, the viable extra specification would consist of two parts:

  1. and to allow the page to define and change the default presentation URL and id to be used by the UA in the UA-initiated-session scenario; basically any external user action that might start a presentation (e.g. clicking the present button in UA toolbar, tapping on an NFC tag, etc) would be equivalent to the page calling startSession(presentationUrl, presentationId) with the values from the tags.
  2. Upon successful connection and session initialization, |navigator.presentation.defaultsessionstart| event would be fired by the UA to all pages with the matching <presentation-url, presentation-id> pair containing the PresentationSession object allowing the page to communicate with the initiated session:

[
Constructor(DOMString type, optional DefaultSessionStartEventInit eventInitDict)
] interface DefaultSessionStartEvent : Event {
readonly attribute PresentationSession session;
};

Thoughts?

Thanks,
Anton.

[1] This was discussed in November last year, see http://lists.w3.org/Archives/Public/public-webscreens/2014Nov/0007.html

@avayvod
Copy link
Contributor

avayvod commented Mar 13, 2015

From mfoltz:

The |defaultsessionstart| event should only be fired on the top level frame/document in a given page.
If there are multiple declaring default presentations, there is no easy way for the user
agent to disambiguate the user intent to initiate presentation.

I think this is mostly a UI issue we should leave for the UAs to solve. The spec may mention it but leave it to the implementors to decide what to do (e.g. one could show a list of DPUs available and ask user to pick one).

If the |defaultsessionstart| event handler is attached to the |navigator| object, how do we ensure
that only the frame that declared the tags gets access the session?

Well, there might me more than one frame / page that declared the same DPU. It seems right to notify all of them; the page may ignore the event if its document is not in focus.

@avayvod
Copy link
Contributor

avayvod commented Mar 13, 2015

I now think that having a separate tag for the default presentation id is a bad idea. First, it's not a link, second it doesn't really make sense to have it without the default presentation URL. So I think it'd be better to parse an extra 'id' attribute out of the rel='default-presentation' tag.

If there're no objection, I'd like to start working on a pull request to add the default presentation info to the spec.

@markafoltz
Copy link
Contributor Author

Well, there might me more than one frame / page that declared the same DPU. It seems right to notify all of them; the page may ignore the event if its document is not in focus.

The navigator object is global and not attached to a specific window. It feels unintuitive from an API perspective for it to behave differently according to the values of <link> tags in a specific frame.

Dominik, Anssi, et. al., why was the presentation object attached to navigator originally? Would it make sense attached to window instead?

@anssiko
Copy link
Member

anssiko commented Mar 17, 2015

From @mfoltzgoogle:

Dominik, Anssi, et. al., why was the presentation object attached to navigator originally? Would it make sense attached to window instead?

The use of navigator is paving the cowpath set by other device-ish specs. We could certainly hang it off of other object if it makes more sense. There are quite many APIs on window such as applicationCache, crypto, history, indexedDB, localStorage, URL etc. also all the native constructors are global. On navigator we have geolocation, getBattery, getGamepads, serviceWorker etc.

From @avayvod:

If there're no objection, I'd like to start working on a pull request to add the default presentation info to the spec.

Please feel free to craft a PR with your proposal.

@avayvod
Copy link
Contributor

avayvod commented Mar 17, 2015

As I understand, having per-frame state in navigator is okay. Adding anything new to window is bad since it's abused by web developers (libraries use it and if the developer forgets to type var before the variable, it's added to window => someone defines |presentation| variable and disabling our API essentially).

@anssiko
Copy link
Member

anssiko commented Mar 18, 2015

Since the presentation attribute is defined as readonly in WebIDL, assignments to it are ignored. So that is not a concern.

partial interface Navigator {
  readonly attribute NavigatorPresentation presentation;
};

The concern is (as @avayvod noted) that if there are web sites out there that are using a global variable named presentation (fair to assume so) those would break in UAs that would implement this API if presentation is expose on window.

If we'd move to global, we should rename presentation to something less generic. Unless there are technical limitations in using navigator for retaining per-frame state I think sticking with navigator would be preferred. My expectation is @avayvod will investigate this.

@avayvod
Copy link
Contributor

avayvod commented Mar 31, 2015

I can confirm we should stick with |navigator|.

@markafoltz
Copy link
Contributor Author

[@mfoltzgoogle's comment moved into its own issue: https://github.com//issues/79]

@markafoltz
Copy link
Contributor Author

Pull request #77 addressed the interface changes and definitions for this behavior. I would like to see an algorithm in section 6.4, "Interface NavigatorPresentation" to define the steps the UA should take to initiate a presentation in this way. I'll open a new issue for that, should be a minor change.

@anssiko
Copy link
Member

anssiko commented May 20, 2015

ACTION: @schien to write a proposed idl change for that [recorded in http://www.w3.org/2015/05/20-webscreens-minutes.html#action06]

@anssiko anssiko reopened this May 20, 2015
@anssiko
Copy link
Member

anssiko commented May 20, 2015

PROPOSED RESOLUTION: review the two proposals and evaluate them against the use cases and requirements considering developers and implementors view points

See relevant discussion during Berlin F2F: http://www.w3.org/2015/05/20-webscreens-minutes.html#item07

@avayvod
Copy link
Contributor

avayvod commented Jul 2, 2015

Hey there, we've come up with the following after discussing with Mozilla and getting @schien's approval:

partial interface NavigatorPresentation {
    attribute PresentationRequest defaultRequest = null;
};

[Constructor(DOMString presentationUrl)]
interface PresentationRequest {
    readonly attribute DOMString url;
    attribute EventHandler onpresentationstart;
    Promise<PresentationSession> joinPresentation(DOMString presentationId);
    Promise<PresentationSession> startPresentation();
    Promise<Availability> getAvailability();
};

So the sender methods have moved to a separate interface, PresentationRequest. The page can create one or more instances of the interface for different presentation URLs and also use each request more than once. If the request is assigned to the defaultRequest, the user agent is able to initiate a presentation session on behalf of the page and fire onpresentationstart event on the appropriate request instance. The event is also fired when start/joinPresentation() succeed along with the promise being resolved to ease receiving the new PresentationSession object by the page.

The shortest way to start a presentation would be:

presBtn.onclick = function(e) {
    new PresentationRequest(“example.com/presentation.html”)
        .startPresentation().then(handleSession);
}

The full example would look like:

var presUrl = “www.example.com/presentation.html”;
var mySession;

// One PresentationRequest(presentationUrl) is constructed per each 
// PresentationSession the page wants to receive.
var request = PresentationRequest(presUrl);

// event will be fired if the presentation defined by this request is
// started somehow, by the browser or as a result of startPresentation().
// that way if the web author doesn’t care which way the session was 
// started, it can be handled in one place.
request.onpresentationstart = function(evt) {
    handleSession(evt.session);
};

// the browser will use this request to start browser initiated presentation
// (unless user disables it)
navigator.presentation.defaultrequest = request;

// try to join an existing presentation known to the user agent
// and the page via local storage
request.joinPresentation(localStorage["presId"])
    .catch(closeSession);

request.getAvailability().then(function(availability) {
    ... 
}).catch (function() {
    ... 
});

document.getElementById("presBtn").onclick = function(e) {
    request.startPresentation()
        .catch(closeSession);
};

var handleSession = function(session) {
  // in case we handle start/joinPresentation promise resolution:
  // if (session === mySession) return;

  // close existing session, if any
  closeSession();
  if (!session)
    return;

  // save presId in localStorage
  localStorage && (localStorage["presId"] = session.presentationId);

  // monitor channel's state
  session.onstatechange = function () {
    if (channel.state == "disconnected")
      closeChannel();
  };

  // register message handler
  session.onmessage = function(evt) {
    console.log("receive message", evt.data);
  };

  // send message to presentation page
  session.send("say hello");
  mySession = session;
};

var closeSession = function() {
  // close old session if exists
  mySession && mySession.close() && mySession = null;
  // remove old presId from localStorage if exists
  localStorage && delete localStorage["presId"];
};

@avayvod
Copy link
Contributor

avayvod commented Jul 2, 2015

I also wanted to summarize the use cases and requirements and thoughts that led to the proposal above.

Use cases

  1. the user interacts with the present button in user agent’s interface (or causes the browser to start a presentation in some other way, e.g. by tapping with the phone on an NFC pairing tag), the page overrides browser’s default behavior and starts a presentation on the device selected by the user (or joins an existing remote presentation if it’s already running on the selected device).
  2. the user agent is already connected to a second screen (i.e is mirroring the same tab to the screen), the page chooses at some point to provide its own content to present on the screen instead of the default behavior
  3. the user interacts with the present button in user agent’s interface, the user agents mirrors the page to the selected screen since the page chose not to override user agent’s behavior
    the user wants to mirror page without allowing it to override the user agent’s behavior, it should be possible

Requirements

  1. the page should be able to gain control over the browser initiated presentation
  2. the page should be able to provide a different presentation URL each time when the page wants to override the default behavior (use cases 1 and 2).
  3. the page should be able to join an existing presentation instead of starting a new one
    in the use case 1 the list of the screens shown by the user agent to the user should be consistent with the list shown if the user triggers the presentation from the page
  4. the user agent should show only mirroring capable screens if the page doesn’t want to override the default behavior
  5. the page should be able to choose whether to override the default behavior (mirroring) or provide the presentation url to use by the user agent when user interacts with the user agent’s interface
  6. the user agent’s interface shouldn’t be available if there’s no screen capable of presenting the page’s content if it overriden the default mirroring behavior
  7. user should be able to choose whether to let the page override the default mirroring behavior or not

Implications

  1. Requirement 1 implies an event (Promise wouldn’t really work because it could hang forever and never resolve) that would provide an initiated PresentationSession to the page
  2. Requirement 2 implies a special property, method or a DOM element (i.e. link or meta) for specifying the presentation URL to use for the browser initiated presentation
  3. Requirement 3 implies the user agent should allow the user to pick screens that have a presentation running
  4. Requirement 4 implies the same URL should be specified for the default presentation and when starting a new session by the page; ideally it should be specified in one place
  5. Requirements 5 and 6 imply that there should be a way for the page to opt in to overrided the behavior (or alternatively opt out)
  6. Requirement 7 implies that the default presentation URL should be set before the event is sent to the user agent
  7. Requirement 8 is satisfied through some website or generic setting that would disable any related events for the origin.

@avayvod
Copy link
Contributor

avayvod commented Jul 2, 2015

I'd like to create a PR to change the spec based on the proposal and feedback from the group if there's any.

@tidoust
Copy link
Member

tidoust commented Jul 2, 2015

Hi @avayvod,

That proposal looks good to me.

I have a couple of questions:

  • Where would getSession and getSessions appear with that proposal? Are they still defined on the NavigatorPresentation interface? Or do they move to a separate interface as discussed in Separate interface for controlling page and presenting page #91?
  • Would the page be able to trigger the default behavior (tab casting)? In other words, would navigator.presentation.defaultRequest be null unless the page sets it or would a user agent supporting tab mirroring set it to a request instance that the page could start with a call to startSession? If not, why not? (not sure what the url property would contain or how to reset things to the default behavior if that's possible, though)

@avayvod
Copy link
Contributor

avayvod commented Jul 2, 2015

Hi @tidoust

Thanks for the feedback.

getSession[s]() are agnostic to the proposal so could be whenever we want them to be.

Tab mirroring by my definition is showing the tab exactly as it is on the second screen modulo screen characteristics (e.g. color and physical resolution) and therefore the tab shouldn't know it's being mirrored.

There's no information for the tab to provide to the user agent and nothing to control except for stopping the mirroring. So this could already be implemented by the user agent without any API.

Do you think there's a use case for the tab to initiate mirroring rather than a presentation?

@tidoust
Copy link
Member

tidoust commented Jul 2, 2015

Hi @avayvod

There's no information for the tab to provide to the user agent and nothing to control except for stopping the mirroring. So this could already be implemented by the user agent without any API.

The communication channel would not mean anything in that case, indeed.

Do you think there's a use case for the tab to initiate mirroring rather than a presentation?

Hmm. I think my reaction was more triggered by the lack of symmetry in your proposal than by a specific use case: when the user presses the "present" button in the UA chrome, it will mirror the tab by default, or present a URL, whereas when the user presses a "present" button in the page, it can only present a URL.

While preparing the charter of the group, I remember a few exchanges with people who were surprised that content mirroring was not in scope. It seems that many people think about content mirroring first when they think about projecting content onto a second screen (and are also sure that it is way easier to achieve than presenting different content but that's another story).

That is all very subjective and does not really create a use case that would require that feature to be exposed at the API level (well, and it's out of scope for the group as just alluded to).

@mounirlamouri
Copy link
Member

This was solved with defaultRequest. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants