Closed Bug 790558 Opened 8 years ago Closed 8 years ago

Add install/update API

Categories

(Core Graveyard :: DOM: Apps, defect)

defect
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: sicking, Unassigned)

References

(Blocks 1 open bug)

Details

(Keywords: dev-doc-needed, meta)

We might need to split this work into dependent bugs, but i'm filing this one to at least track the overall design.

We need to add the following API to the app objects returned from getSelf/mgmt.getAll

interface mozIDOMApplication {

 ...

 readonly attribute DOMString installState; // "pending", "installed", "updating"

 readonly attribute jsval lastUpdateCheck;
 DOMRequest checkForUpdate();  // Alternatively add a "checkingForUpdate" flag

 readonly attribute boolean downloadAvailable;
 readonly attribute boolean downloading;
 readonly attribute boolean readyToApplyDownload;
 readonly attribute long? downloadSize;
 readonly attribute long downloadProgress; // currently called progress
                                           // which is ok too.
 readonly attribute DOMError downloadError;

          attribute EventHandler onprogress;
          attribute EventHandler ondownloadavailable;
          attribute EventHandler ondownloaded;
          attribute EventHandler ondownloadapplied;

 void download();
 void cancelDownload();

 ...

}

Additionally we'd add an mgmt.applyDownload(mozIDOMApplication) function. The function is a no-op if it's passed an app object which doesn't have readyToApplyDownload set to true.

Eventually we should add a "pauseUpdate" function as well, but that's for later.


During installation of hosted apps *with* an appcache manifest:
When the app is first installed its .installState is "pending" and it's "downloadAvailable" and "downloading" properties are set to true. The downloadSize is set to null. As we download the appcache we fire "progress" events. Once the appcache has been downloaded we set .installState to "installed" and fire "downloaded" and "downloadapplied".

Attempting to launch the app while still in "pending" throws an error.

If there's an error while downloading the appcache we set .downloadError appropriately. .installState remains as "pending".

If the cancelDownload() function is called we delete all downloaded data (do we have that ability for appcache?) and set downloadprogress back to 0.


During installation of hosted apps *without* an appcache manifest:
When the app is first installed its .state is "installed" and it's "downloadAvailable" and "downloading" properties are set to false.

Attempting to launch the app will succeed.


During installation of a packaged app:
When the app is first installed its .installState is "pending" and it's "downloadAvailable" and "downloading" properties are set to true. This is done based on just the data in the minimanifest, before we download the actual package. The downloadSize is set to the size from the minimanifest.

As we download the package we fire "progress" events. Once the package has been downloaded we set .installState to "installed" and fire "downloaded" and "downloadapplied".

Attempting to launch the app while still in "pending" throws an error.

If there's an error while downloading the appcache we set .downloadError appropriately. .installState remains as "pending".

If the cancelDownload() function is called we delete all downloaded data and set downloadprogress back to 0.


During update of hosted apps *with* an appcache manifest:
Since the appcache can't separate "check for update" from "download and apply update" I think we are pretty limited here. I'm still looking into what we can make work here for v1.


During update of hosted apps *without* an appcache manifest:
When we detect that an updated manifest is availble we simply update the various properties on the app object and fire "downloadapplied"


During update of packaged app:
When we detect that an update is available we set "downloadAvailable" to true and fire "downloadavailable".

When the download() function is called, we set .installState to "updating" and start downloading and fire progress events as needed.

If .launch is called during this time I think we should allow the app to start. But it would be running the old version.

When we're done downloading the package we set readyToApplyDownload to true and fire "downloaded".

When mgmt.applyDownload() is called we swap out the package and fire "downloadapplied".

If cancelDownload() is called we reset .installState to "installed" and .downloadProgress to 0. We also delete all downloaded data for the new version.

If an error occurs we behave like for cancelDownload but also set .downloadError appropriately.


Does this sound ok? I'm not terribly happy with the naming. I originally went with s/download/update/g, but it felt weird to use updateXXX properties to describe the original download.
Anant - Can you review this and add this to the mozapps API spec?

I've got feedback on this, although I'm wondering if this is better discussed on dev.webapps. Should we start the discussion there or in this bug?
OS: Mac OS X → All
Hardware: x86 → All
Version: unspecified → Trunk
We'll treat this as a meta bug, so not blocking here. I'll make sure that we get the dependencies right instead.
blocking-basecamp: + → ---
blocking-kilimanjaro: + → ---
Keywords: meta
Blocks: 772364
Blocks: 772364
No longer depends on: 772364
No longer blocks: 772364
Depends on: 772364
No longer blocks: 790872
Depends on: 790872
Also, the mini manifest we'll use during installation and update of packaged apps will use a format like the following (copied from bug 789527 comment 0):

{
 "name" : "my app",
 "version" : "1.0a1",
 "size" : 1084562,
 "release_notes": "first release",
 "package_path": "http://...",
 "locales" {
   "fr_FR" : {
     "name" : "mon application"
   }
   "se_SE" : {
     "name" : "min balla app"
   }
 }
}
This looks great, Jonas. Very nice.

> Eventually we should add a "pauseUpdate" function as well, but that's for later.

* So we will not be able to handle pausing of downloads (for either installs or updates) in v1? Would the situation be any different for system updates?
* On app.install() we pull down the app manifest (either mini-manifest or web app manifest), and utilize the data therein to present the user with an install confirmation prompt. If the user declines the install, we delete the manifest, leaving no traces of app behind. If the user confirms the install, we take the steps you outline above (installState = "pending", "downloadAvailable" and "downloading" properties = true, etc). Do I have that flow correct?
* When app.install() is received we would like to first check for duplicates, and cancel the install process if the app is already installed. I believe Gaia will have everything it needs from platform to do this? Access to manifest + AppRegistry contents? (I realize this might be a better question for the Gaia devs)
* We've discussed "Download over WiFi only" options. Presumably that again is Gaia, and Gaia has what it will need from platform?
I dug through my notes again from a few days ago:

> * So we will not be able to handle pausing of downloads...
> * On app.install() we...
> We've discussed "Download over WiFi only" ...

Nevermind. I found the answers in our Tuesday notes.

> * When app.install() is received we would like to first check for duplicates, and cancel the install process if the app is already installed. I believe Gaia will have everything it needs from platform to do this? Access to manifest + AppRegistry contents? (I realize this might be a better question for the Gaia devs)

This question still holds.
(In reply to Josh Carpenter [:jcarpenter] from comment #5)

> > * When app.install() is received we would like to first check for duplicates, and cancel the install process if the app is already installed. I believe Gaia will have everything it needs from platform to do this? Access to manifest + AppRegistry contents? (I realize this might be a better question for the Gaia devs)

From an API point of view, calling install() on an already existing app is treated as a reinstall - the install parameters can change though (currently this is the payment information).

Gaia can't prevent the install() call to run, but can abort installation silently instead of prompting the user. This means that we would have downloaded the manifest anyway, and that the user could have gone through a payment flow already.
Fabrice pointed out that we also need to fire an error if there's an error while downloading. The suggestion is to fire "downloaderror" if download fails, and rename to "downloaded" to "downloadsuccess" when download succeeds.

The "downloadavailable" stays as-is since that's just fired when we detect that we can start a download.
Thanks Fabrice. I am going to hold off updating the specs until I've gotten a report out of the Identity + Payment architecture meetings happening this week. I've been told that they have some opinions on this topic.
> Fabrice pointed out that we also need to fire an error if there's an error while downloading. The suggestion is to fire "downloaderror" if download fails, and rename to "downloaded" to "downloadsuccess" when download succeeds.

Makes sense.
Tracking front end implementation here: 

https://github.com/mozilla-b2g/gaia/issues/4948
> Fabrice pointed out that we also need to fire an error if there's an error while downloading. The suggestion is to fire "downloaderror" if download fails, and rename to "downloaded" to "downloadsuccess" when download succeeds.

Guys, did this end up being implemented? If so, is the API complete and in place? Fabrice indicated on Thursday that most of work had been done. \ o / I want to confirm before I update my App specs accordingly.
> From an API point of view, calling install() on an already existing app is treated as a reinstall - the install parameters can change though (currently this is the payment information).

What does reinstall entail? Re: downloading the contents of the app? Overwriting original install's user data?

> Gaia can't prevent the install() call to run, but can abort installation silently instead of prompting the user. This means that we would have downloaded the manifest anyway, and that the user could have gone through a payment flow already.

Instead of failing silently, could we open a "app is already installed" prompt? We need _some_ sort of indication to user that their install has failed, and the reason why.
(In reply to Josh Carpenter [:jcarpenter] from comment #12)
> > From an API point of view, calling install() on an already existing app is treated as a reinstall - the install parameters can change though (currently this is the payment information).
> 
> What does reinstall entail? Re: downloading the contents of the app?
> Overwriting original install's user data?
> 
> > Gaia can't prevent the install() call to run, but can abort installation silently instead of prompting the user. This means that we would have downloaded the manifest anyway, and that the user could have gone through a payment flow already.
> 
> Instead of failing silently, could we open a "app is already installed"
> prompt? We need _some_ sort of indication to user that their install has
> failed, and the reason why.

Why would the reason of "app is already installed" be the reason why the install failed? The way we call install on a web app for reinstalling wouldn't deem an error.

If there was error reporting needed btw for a failure to install, that would be a responsibility of the mozapps API to throw an error callback that the install failed with the reason and for marketplace to capture the error, handling it appropriately, and provide a "useful" error message to the user.
"reinstalling" an app basically just entails re-downloading the application code itself. It will only do anything if the files containing the application code has somehow gotten corrupted, which is extremely rare.

It does not remove any application data, or reset any application state. So if the application has gotten itself into a broken state then "reinstalling" won't do anything.

Also note that reinstallation will only happen if there's a bug in the marketplace code. The marketplace has the ability to check if an app is already installed, and if it is, it has the ability to launch that app.

So the marketplace *should* replace the "install" button with a "launch" button if the app is already installed.
Ok, here's the current situation:

I don't think we should worry about reinstalls too much. Reinstalls can happen in three scenarios:

1. There's a bug in the marketplace code. I.e. it calls install on an app even though it can detect that the app is already installed.

2. The marketplace wants to update payment information for an app. I don't know of any scenarios when this is needed.

3. The user goes first to marketplace A to install a hosted app, then goes to marketplace B and install the same hosted app. In this case marketplace B can't detect that the app is installed before calling the install() function.


It seems like these scenarios are realistic enough that we need to handle them. But none seem common enough that we should worry if the UI isn't perfect.
V4 App Install specs are here: https://www.dropbox.com/sh/b0kyykhzcfkpm8b/ReFTX_E72X

Per your feedback, changes as follows:

* Removed: “Check for Duplicates” step, per Jonas’s Oct 1 comments in bug #790558: https://bugzilla.mozilla.org/show_bug.cgi?id=790558#c14. He explained that reinstalls will be low-risk edge cases. Marketplace will have mechanisms for preventing, and in event they do happen (if user reinstalls an existing app from a non-Marketplace source, for example), reinstall will simply re-download app code, leaving user data and app state untouched.
* Added: notes on why we check for sufficient storage space before downloading.
* Updated: “Not enough space” prompt, with instructions on how to try installation at later time.
Jonas - Are we done here? All dependencies are closed.
Whiteboard: [FIXED?]
Whiteboard: [FIXED?]
I think this bug fixed now. Closing. If I'm wrong, reopen.
Status: NEW → RESOLVED
Closed: 8 years ago
Resolution: --- → FIXED
Product: Core → Core Graveyard
You need to log in before you can comment on or make changes to this bug.