Re: Review of Web Application Manifest Format and Management APIs

Hi Marcos,

Thanks for the thorough review, this is extremely useful! Most things 
seems fixable fairly easily, for those that aren't, comments inline:

On 5/12/2012 4:32 PM, Marcos Caceres wrote:
>> default_locale: [Mandatory only if "locales" property is set]. The locale of the top-level name and description.
>
> Why not just take the first locale encountered?

The "locales" property is a dictionary, so there is no reliable way to 
pick the first from the list. We'd have to convert it to an array and 
add one more level of nesting which seemed a bit more complex compared 
to adding a property at the top-level.

>> launch_path: The path within the web application's origin that is loaded when the application is launched
> What happens if the "path" redirects? Are HTTP responses honoured?

Yes, all HTTP responses are honored, as long as the redirects are within 
the same origin, since it's the origin that identifies the app. The UA 
should enforce this policy, we should add some language to that effect.

>> icons: A map of icon sizes to URIs of the icons (which may be absolute or data URIs).
>
> Why not just use HTML's icons?

If we use HTML icons, app stores will have to load the app's home page 
in the background in order to extract the icon to display on their 
storefront. Since they have to fetch manifests when a developer submits 
their app to the store anyway, it seemed useful to include icons. It 
also helps user agents display icons for apps, in an "app dashboard", 
for example, without having to load a HTML pages in the background.

>> Icons must be square.
>
> Why? and what if they are not square? Seems like a fairly platform specific restriction?

Yes, this is a fairly whimsical restriction, when we were trying to get 
some consistency. There is no important reason they should be, I'll 
remove it.

>> locales: A map of locale specific overrides of data contained in the manifest. Each locale key is keyed on a locale tag [RFC4646], and contains a sparse representation of the manifest.
>
> I think you mean a "Language-Tag"? What happens if it's not a language tag? What happens if it's a "*"?

Language-Tag, correct. * is not allowed, and the manifest will be 
rejected as invalid. If you want to apply a set of values for any 
language, just skip the "locales" property entirely.

>> Any field in the locales property will override the corresponding property in the manifest. The locales, installs_allowed_from, and default_locale CANNOT be overridden.
>
> By the author or by the runtime?

By the author. What this means is that "locales", 
"installs_allowed_from" and "default" are invalid properties inside any 
object in the "locales" top-level property. If any of those three is 
found, the manifest is rejected as invalid.

>> If the locales property is set, the default_locale must also be set.
>
> What happens if it's not set? What happens if only locale data is set?

We have two options here. In our current implementation we simply ignore 
the "locales" property in this case and only use the top-level name and 
description. The other option is to reject the manifest as invalid. I 
have no strong feelings for one over the other.

>> installs_allowed_from: An array of origins that are allowed to trigger installation of this application. This field allows the developer to restrict installation of their application to specific sites. If the value is omitted, installs are allowed from any site.
>
> How are origins parsed?

I'm not sure what the question means, but origins are essentially a 
combination of [protocol]://[hostname]:[port]. Whenever an install is 
triggered, the UA must check if the origin of the page triggering the 
install is present in this array. * is a valid value for 
installs_allowed_from, in which case the UA may skip this check.

>> screen_size: This object may contain the min_height and min_width properties that describe the minimum height and width (in pixels) the application needs in order to render correctly. Interpretation of these values is left up to the runtime and/or app store.
>
> How does this play with CSS and media queries in particular? What's the use case?

These values do not interfere with runtime detection via media queries. 
The use case for these values is two-fold:
- An app store may prevent the user from installing an app on a device 
which doesn't meet this criteria
- A UA may prevent the user from launching an app on a device which 
doesn't meet this criteria

The primary goal in both cases is to let the developer declare what 
screen sizes their app is known to work correctly.

>> required_features: This array consists of a set of values that describes the mandatory features the application needs in order to run correctly. A full list of valid values is TBD.
>
> Interested to see what could be listed here… this is the area of greatest interop concern, IMO (hopefully it won't be needed at all, as it's only really useful on extension/proprietary platforms).

Examples include "touch", "webgl", "indexeddb". You're right that it's 
possible to use this for proprietary extensions; but there are use cases 
for standard web features. The idea is to let the store and runtime have 
some control over not allowing the user to purchase/install/launch an 
app that is known not to work on their current device.

For instance, if I am browsing for apps on my phone that doesn't support 
WebGL, the Mozilla App Marketplace won't let me install any apps that 
have that feature listed in required_features. Likewise, the runtime may 
prevent launching apps that are known to be incompatible with the 
current UA, quire similar to screen_size.

>> orientation: This value defines the orientation at which the application will initially start. Can be one of "portrait", "landscape", "portrait-secondary" or "landscape-secondary".
>
> Could this be handled on a "per page" basis using CSS device adaption?
> http://dev.w3.org/csswg/css-device-adapt/#the-lsquoorientationrsquo-descriptor

Yes, that can always be done at runtime; but this value is for the 
"initial start" as noted. It lets the UA set the orientation, and 
optionally, lock it in position before the app is launched. This is a 
better user experience than launching the app, runtime code detecting 
orientation and then flipping, because there is delay and flickering.

>> fullscreen: This value is set to "true" or "false" to describe whether the runtime should launch the application in fullscreen mode.
>
> There also a relationship here to CSS View Mode Media Feature:
> http://www.w3.org/TR/view-mode/

Same as above, we're trying to move what we can to launch time, instead 
of relying on run-time code for everything.


>> An application manifest must be served from the same origin that the application itself is served from.
> What happens if it is not?
>> There must only be one application per origin.
>
> what if there are more?

This is an interesting question - as an app is identified by the origin 
- a single UA can't have two apps from the same origin. The second 
install() call will be treated as a re-install/upgrade of the first app 
which will be replaced. It is technically possible to have two apps on 
the same origin and submit each one to different stores. If a single 
users installs both apps from both stores, it will cause the latter to 
replace the former, so there is no reason for a developer to do this.

>> interface AppObject {
>> readonly attribute DOMString origin;
>> readonly attribute Object manifest;
>> readonly attribute DOMString installOrigin;
>> readonly attribute unsigned long installTime;
>
> What's the use case?

An AppObject is returned by the UA when any of the management APIs are 
called (i.e. getSelf(), getInstalled() and getAll()). The consumer of 
this object is the UA, not the app. The UA uses these fields for a 
variety of things, identifying the app, synchronizing apps across 
multiple devices, updating the manifest, etc.

>> installOrigin of type DOMString, readonly
>> The origin of the page from which the application was installed.
>> installTime of type unsigned long, readonly
>> Time in milliseconds since epoch at which the application was installed.
>
> Can this be useful to launch an attack ("oh, I see you have the old unpatched version…. muahahah!")? What would a developer do with this info?

Only the app itself or a privileged caller has access to this info. An 
"app dashboard" is an example of a privileged caller, and technically it 
can use this information to launch an attack. We must be cautious on how 
we mark a given domain as a privileged caller (it obviously needs user 
consent, which can be at run-time, just like Geolocation).

>> manifest of type Object, readonly
>> A JavaScript object as returned by JSON.parse invoked on the manifest.
>
> What happens if this results in an error?

An AppObject is only created when an application has been successfully 
installed, so this case is not possible.

>> parameters of type Object, readonly
>> Parameters that were provided at install time.
>
> Not sure where these parameters came from? Where they URL params?

No, these are provided by the page that triggered the install at 
install-time:

navigator.mozApps.install("/path/to/manifest", {foo: "bar"});

The value of "foo" will then be available via this property. The primary 
use case for this is receipts (which I should also add text to explain, 
but in the meantime we have a description of our implementation here: 
https://wiki.mozilla.org/Apps/WebApplicationReceipt). This can also be 
used as a way to let a store pass information along to the application.

>> launch
>> Launches this application (behaviour is runtime dependent).
>
> Bit confused… isn't the application already running? Or is this whole API exposed in some other execution context (e.g., some kind of application control page)?

In the case the AppObject was retrieved by the application itself via 
getSelf(), this method will be a no-op. However, for other privileged 
callers like an app dashboard, this is useful, as they will want to call 
this method when the user clicks on the icon for an app, for example.

>> getInstalled
>> Retrives application records for all applications that have been installed so far. This call is available to privileged callers only.
>
> What's a privileged caller? Also, if this is a getter, then it should be an attribute. Only use getXXX() for async getters.

A privileged caller is an origin which has been authorized by the user 
to manage applications on their behalf. Privileged callers have the 
capability to enumerate all apps the user has installed, launch them, 
and uninstall them.

The goal is to provide a uniform interface for app dashboards across 
run-times, and to let people write their own, instead of locking 
everyone to a UA specific app management interface.

This is an asynchronous getter:

let op = navigator.apps.mgmt.getInstalled();
op.onsuccess = function() { console.log(JSON.stringify(op.result)); }

>> getSelf
>> Retrieves the application record for the origin from which the call is made.
>
> As above. This should just be an attribute called "self". Still not sure what this is though? or what it is used for?

This is also an asynchronous call, and will be used by apps to retrieve 
their own AppObject. This is useful when an app wants to retrieve the 
receipt for their app (that was set by the app store when install() was 
called), and verify, for example, that the current user has paid for 
their app.

>> The User-Agent must also persist the parameters provided (if any) which may later be retrived from the AppObject representing this application.
>
> These are URL parameters?

No, install-time parameters. There is definitely scope for better 
wording here!

>> Return type: EventTarget
>> Application Events
>>
>> Most of the methods described above return an EventTarget object to which success and error handlers may be attached using standard DOM methods like addEventListener or attributes like onsuccess and onerror.
> This pattern seems weird. Or maybe I'm not getting it. Example would be great here.

I'll add some more examples to the spec, meanwhile, there are a couple here:

// Dashboard gets list of all installed apps to display
let op = navigator.apps.mgmt.getAll();
op.onsuccess = function() {
   let apps = op.result;
   for (let origin in apps) {
     // render name and icon for apps[origin]
   }
}

// App retrieves the receipt to verify payment
let op = navigator.apps.getSelf();
op.onsuccess = function() {
   let receipt = op.result.parameters.receipt;
   // XHR receipt to server and verify it
}

>> interface AppEventTarget : EventTarget {
>> attribute EventListener onsuccess;
>> attribute EventListener onerror;
>
> See other example I gave… should be defined as a callback… though this whole AppEventTarget thing seems strange.

Our old API used callbacks, but we moved to an event model since that 
seems to be more common in Web APIs these days. I don't have strong 
feeling either way, though I do have a personal preference for the 
callback pattern over event targets.

>> void addEventListener (DOMString event, EventListener e);
>> void removeEventListener (DOMString event, EventListener e);
>> readonly attribute Object result;
>
> I would be better if this followed a more standard programming pattern found in other Web platform interfaces.

That's a really interesting comment, because we started out with 
callbacks but moved to events because we thought that was the pattern in 
other Web APIs :)

Thanks again for your in-depth feedback, it is very much appreciated!

Regards,
-Anant

Received on Sunday, 13 May 2012 16:47:56 UTC