зеркало из https://github.com/mozilla/gecko-dev.git
Bug 761045: Upload locally installed apps on first run; r=gps
This commit is contained in:
Родитель
3b2bac6d14
Коммит
d9769d1d70
|
@ -609,7 +609,7 @@ let DOMApplicationRegistry = {
|
|||
getAppById: function(aId) {
|
||||
if (!this.webapps[aId])
|
||||
return null;
|
||||
|
||||
|
||||
let app = this._cloneAppObject(this.webapps[aId]);
|
||||
return app;
|
||||
},
|
||||
|
|
|
@ -138,6 +138,11 @@ BrowserIDService.prototype = {
|
|||
_getEmails: function _getEmails(cb, options, sandbox) {
|
||||
let self = this;
|
||||
|
||||
if (!sandbox) {
|
||||
cb(new Error("Sandbox not created"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
function callback(res) {
|
||||
let emails = {};
|
||||
try {
|
||||
|
@ -382,8 +387,18 @@ BrowserIDService.prototype = {
|
|||
*/
|
||||
function Sandbox(cb, uri) {
|
||||
this._uri = uri;
|
||||
this._createFrame();
|
||||
this._createSandbox(cb, uri);
|
||||
|
||||
// Put in a try/catch block because Services.wm.getMostRecentWindow, called in
|
||||
// _createFrame will be null in XPCShell.
|
||||
try {
|
||||
this._createFrame();
|
||||
this._createSandbox(cb, uri);
|
||||
} catch(e) {
|
||||
this._log = Log4Moz.repository.getLogger("Service.AITC.BrowserID.Sandbox");
|
||||
this._log.level = Log4Moz.Level[PREFS.get("log")];
|
||||
this._log.error("Could not create Sandbox " + e);
|
||||
cb(null);
|
||||
}
|
||||
}
|
||||
Sandbox.prototype = {
|
||||
/**
|
||||
|
|
|
@ -191,7 +191,7 @@ AitcClient.prototype = {
|
|||
try {
|
||||
cb(null, apps);
|
||||
// Don't update lastModified until we know cb succeeded.
|
||||
this._appsLastModified = parseInt(req.response.headers["X-Timestamp"], 10);
|
||||
this._appsLastModified = parseInt(req.response.headers["x-timestamp"], 10);
|
||||
this._state.set("lastModified", "" + this._appsLastModified);
|
||||
} catch (e) {
|
||||
this._log.error("Exception in getApps callback " + e);
|
||||
|
@ -376,7 +376,7 @@ AitcClient.prototype = {
|
|||
// Set values from X-Backoff and Retry-After headers, if present.
|
||||
_setBackoff: function _setBackoff(req) {
|
||||
let backoff = 0;
|
||||
let successfulStatusCodes = [200, 201, 204, 304, 401];
|
||||
let statusCodesWithoutBackoff = [200, 201, 204, 304, 401];
|
||||
|
||||
let val;
|
||||
if (req.response.headers["Retry-After"]) {
|
||||
|
|
|
@ -28,14 +28,27 @@ function Aitc() {
|
|||
Preferences.get("services.aitc.dashboard.url")
|
||||
).prePath;
|
||||
|
||||
this._manager = new AitcManager(this._init.bind(this));
|
||||
let self = this;
|
||||
this._manager = new AitcManager(function managerDone() {
|
||||
CommonUtils.nextTick(self._init, self);
|
||||
});
|
||||
}
|
||||
Aitc.prototype = {
|
||||
// The goal of the init function is to be ready to activate the AITC
|
||||
// client whenever the user is looking at the dashboard.
|
||||
_init: function init() {
|
||||
// client whenever the user is looking at the dashboard. It also calls
|
||||
// the initialSchedule function on the manager.
|
||||
_init: function _init() {
|
||||
let self = this;
|
||||
|
||||
// Do an initial upload.
|
||||
this._manager.initialSchedule(function queueDone(num) {
|
||||
if (num == -1) {
|
||||
self._log.debug("No initial upload was required");
|
||||
return;
|
||||
}
|
||||
self._log.debug(num + " initial apps queued successfully");
|
||||
});
|
||||
|
||||
// This is called iff the user is currently looking the dashboard.
|
||||
function dashboardLoaded(browser) {
|
||||
let win = browser.contentWindow;
|
||||
|
|
|
@ -42,7 +42,7 @@ function AitcManager(cb, premadeClient, premadeToken) {
|
|||
this._tokenDuration = INITIAL_TOKEN_DURATION;
|
||||
this._premadeToken = premadeToken || null;
|
||||
this._invalidTokenFlag = false;
|
||||
|
||||
|
||||
this._lastEmail = null;
|
||||
this._dashboardWindow = null;
|
||||
|
||||
|
@ -67,13 +67,9 @@ function AitcManager(cb, premadeClient, premadeToken) {
|
|||
cb(null, true);
|
||||
return;
|
||||
}
|
||||
// Schedule them, but only if we can get a silent assertion.
|
||||
self._makeClient(function(err, client) {
|
||||
if (!err && client) {
|
||||
self._client = client;
|
||||
self._processQueue();
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Caller will invoke initialSchedule which will process any items in the
|
||||
// queue, if present.
|
||||
});
|
||||
}
|
||||
AitcManager.prototype = {
|
||||
|
@ -169,6 +165,71 @@ AitcManager.prototype = {
|
|||
this._dashboardWindow = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initial schedule for the manager. It is the responsibility of the
|
||||
* caller who created this object to call this function if it wants to
|
||||
* do an initial sync (i.e. upload local apps on a device that has never
|
||||
* communicated with AITC before).
|
||||
*
|
||||
* The callback will be invoked with the number of local apps that were
|
||||
* queued to be uploaded, or -1 if this client has already synced and a
|
||||
* local upload is not required.
|
||||
*
|
||||
* Try to schedule PUTs but only if we can get a silent assertion, and if
|
||||
* the queue in non-empty, or we've never done a GET (first run).
|
||||
*/
|
||||
initialSchedule: function initialSchedule(cb) {
|
||||
let self = this;
|
||||
|
||||
function startProcessQueue(num) {
|
||||
self._makeClient(function(err, client) {
|
||||
if (!err && client) {
|
||||
self._client = client;
|
||||
self._processQueue();
|
||||
return;
|
||||
}
|
||||
});
|
||||
cb(num);
|
||||
}
|
||||
|
||||
// If we've already done a sync with AITC, it means we've already done
|
||||
// an initial upload. Resume processing the queue, if there are items in it.
|
||||
if (Preferences.get("services.aitc.client.lastModified", "0") != "0") {
|
||||
if (this._pending.length) {
|
||||
startProcessQueue(-1);
|
||||
} else {
|
||||
cb(-1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DOMApplicationRegistry.getAllWithoutManifests(function gotAllApps(apps) {
|
||||
let done = 0;
|
||||
let appids = Object.keys(apps);
|
||||
let total = appids.length;
|
||||
self._log.info("First run, queuing all local apps: " + total + " found");
|
||||
|
||||
function appQueued(err) {
|
||||
if (err) {
|
||||
self._log.error("Error queuing app " + apps[appids[done]].origin);
|
||||
}
|
||||
|
||||
if (done == total) {
|
||||
self._log.info("Finished queuing all initial local apps");
|
||||
startProcessQueue(total);
|
||||
return;
|
||||
}
|
||||
|
||||
let app = apps[appids[done]];
|
||||
let obj = {type: "install", app: app, retries: 0, lastTime: 0};
|
||||
|
||||
done += 1;
|
||||
self._pending.enqueue(obj, appQueued);
|
||||
}
|
||||
appQueued();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Poll the AITC server for any changes and process them. It is safe to call
|
||||
* this function multiple times. Last caller wins. The function will
|
||||
|
@ -197,7 +258,7 @@ AitcManager.prototype = {
|
|||
this._processQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Do one GET soon, but only if user is active.
|
||||
let getFreq;
|
||||
if (this._state == this._ACTIVE) {
|
||||
|
@ -481,14 +542,14 @@ AitcManager.prototype = {
|
|||
cb(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Prompt user to login.
|
||||
self._makeClient(function(err, client) {
|
||||
if (err) {
|
||||
cb(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// makeClient sets an updated token.
|
||||
self._client = client;
|
||||
self._invalidTokenFlag = false;
|
||||
|
@ -541,7 +602,7 @@ AitcManager.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
let msg = err.name + " in _getToken: " + err.error;
|
||||
let msg = "Error in _getToken: " + err;
|
||||
this._log.error(msg);
|
||||
cb(msg, null);
|
||||
},
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
Cu.import("resource://services-common/preferences.js");
|
||||
Cu.import("resource://gre/modules/Webapps.jsm");
|
||||
|
||||
Cu.import("resource://services-aitc/client.js");
|
||||
Cu.import("resource://services-aitc/manager.js");
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
Cu.import("resource://services-common/preferences.js");
|
||||
|
||||
Cu.import("resource://testing-common/services-common/aitcserver.js");
|
||||
|
||||
const PREFS = new Preferences("services.aitc.");
|
||||
|
@ -77,6 +80,70 @@ function do_check_lt(a, b) {
|
|||
do_check_true(a < b);
|
||||
}
|
||||
|
||||
add_test(function test_manager_localapps() {
|
||||
// Install two fake apps into the DOM registry.
|
||||
let fakeApp1 = get_mock_app();
|
||||
fakeApp1.manifest = {
|
||||
name: "Appasaurus 1",
|
||||
description: "One of the best fake apps ever",
|
||||
launch_path: "/",
|
||||
fullscreen: true,
|
||||
required_features: ["webgl"]
|
||||
};
|
||||
|
||||
let fakeApp2 = get_mock_app();
|
||||
fakeApp2.manifest = {
|
||||
name: "Appasaurus 2",
|
||||
description: "The other best fake app ever",
|
||||
launch_path: "/",
|
||||
fullscreen: true,
|
||||
required_features: ["geolocation"]
|
||||
};
|
||||
|
||||
DOMApplicationRegistry.confirmInstall({app: fakeApp1});
|
||||
DOMApplicationRegistry.confirmInstall({app: fakeApp2});
|
||||
|
||||
// Create an instance of the manager and check if it put the app in the queue.
|
||||
// We put doInitialUpload in nextTick, because maanger will not be defined
|
||||
// in the callback. This pattern is used everywhere, AitcManager is created.
|
||||
let manager = new AitcManager(function() {
|
||||
CommonUtils.nextTick(doInitialUpload);
|
||||
});
|
||||
|
||||
function doInitialUpload() {
|
||||
manager.initialSchedule(function(num) {
|
||||
// 2 apps should have been queued.
|
||||
do_check_eq(num, 2);
|
||||
do_check_eq(manager._pending.length, 2);
|
||||
|
||||
let entry = manager._pending.peek();
|
||||
do_check_eq(entry.type, "install");
|
||||
do_check_eq(entry.app.origin, fakeApp1.origin);
|
||||
|
||||
// Remove one app from queue.
|
||||
manager._pending.dequeue(run_next_test);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_manager_alreadysynced() {
|
||||
// The manager should ignore any local apps if we've already synced before.
|
||||
Preferences.set("services.aitc.client.lastModified", "" + Date.now());
|
||||
|
||||
let manager = new AitcManager(function() {
|
||||
CommonUtils.nextTick(doCheck);
|
||||
});
|
||||
|
||||
function doCheck() {
|
||||
manager.initialSchedule(function(num) {
|
||||
do_check_eq(num, -1);
|
||||
do_check_eq(manager._pending.length, 1);
|
||||
// Clear queue for next test.
|
||||
manager._pending.dequeue(run_next_test);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_401_responses() {
|
||||
PREFS.set("client.backoff", "50");
|
||||
PREFS.set("manager.putFreq", 50);
|
||||
|
@ -89,19 +156,18 @@ add_test(function test_401_responses() {
|
|||
uid: "uid",
|
||||
duration: "5000"
|
||||
};
|
||||
|
||||
let server = get_server_with_user(username);
|
||||
let client = get_client_for_server(username, server);
|
||||
|
||||
server.mockStatus = {
|
||||
code: 401,
|
||||
method: "Unauthorized"
|
||||
}
|
||||
let client = get_client_for_server(username, server);
|
||||
let manager = new AitcManager(function () {}, client, premadeToken);
|
||||
// Assume first token is not out dated.
|
||||
manager._lastTokenTime = Date.now();
|
||||
};
|
||||
|
||||
let mockRequestCount = 0;
|
||||
let clientFirstToken = null;
|
||||
|
||||
server.onRequest = function mockstatus () {
|
||||
server.onRequest = function mockstatus() {
|
||||
mockRequestCount++;
|
||||
switch (mockRequestCount) {
|
||||
case 1:
|
||||
|
@ -121,7 +187,15 @@ add_test(function test_401_responses() {
|
|||
}
|
||||
}
|
||||
|
||||
manager.appEvent("install", get_mock_app());
|
||||
let manager = new AitcManager(function() {
|
||||
CommonUtils.nextTick(gotManager);
|
||||
}, client, premadeToken);
|
||||
|
||||
function gotManager() {
|
||||
// Assume first token is not outdated.
|
||||
manager._lastTokenTime = Date.now();
|
||||
manager.appEvent("install", get_mock_app());
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_client_exponential_backoff() {
|
||||
|
|
|
@ -3,7 +3,7 @@ head = ../../../common/tests/unit/head_global.js ../../../common/tests/unit/head
|
|||
tail =
|
||||
|
||||
[test_load_modules.js]
|
||||
[test_storage_queue.js]
|
||||
[test_storage_registry.js]
|
||||
[test_aitc_client.js]
|
||||
[test_aitc_manager.js]
|
||||
[test_aitc_manager.js]
|
||||
[test_storage_queue.js]
|
||||
[test_storage_registry.js]
|
Загрузка…
Ссылка в новой задаче