mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1067380 - Refactor webide to use app actor front. r=jryans
This commit is contained in:
parent
b431b29868
commit
9fb4871b0d
@ -954,9 +954,12 @@ let Cmds = {
|
||||
|
||||
|
||||
let runtimeappsHeaderNode = document.querySelector("#panel-header-runtimeapps");
|
||||
let sortedApps = AppManager.webAppsStore.object.all;
|
||||
let sortedApps = [];
|
||||
for (let [manifestURL, app] of AppManager.apps) {
|
||||
sortedApps.push(app);
|
||||
}
|
||||
sortedApps = sortedApps.sort((a, b) => {
|
||||
return a.name > b.name;
|
||||
return a.manifest.name > b.manifest.name;
|
||||
});
|
||||
let mainProcess = AppManager.isMainProcessDebuggable();
|
||||
if (AppManager.connection.status == Connection.Status.CONNECTED &&
|
||||
@ -991,16 +994,16 @@ let Cmds = {
|
||||
let app = sortedApps[i];
|
||||
let panelItemNode = document.createElement("toolbarbutton");
|
||||
panelItemNode.className = "panel-item";
|
||||
panelItemNode.setAttribute("label", app.name);
|
||||
panelItemNode.setAttribute("label", app.manifest.name);
|
||||
panelItemNode.setAttribute("image", app.iconURL);
|
||||
runtimeAppsNode.appendChild(panelItemNode);
|
||||
panelItemNode.addEventListener("click", () => {
|
||||
UI.hidePanels();
|
||||
AppManager.selectedProject = {
|
||||
type: "runtimeApp",
|
||||
app: app,
|
||||
app: app.manifest,
|
||||
icon: app.iconURL,
|
||||
name: app.name
|
||||
name: app.manifest.name
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
|
@ -14,11 +14,10 @@ const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
|
||||
const {TextEncoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
|
||||
const {AppProjects} = require("devtools/app-manager/app-projects");
|
||||
const WebappsStore = require("devtools/app-manager/webapps-store");
|
||||
const TabStore = require("devtools/webide/tab-store");
|
||||
const {AppValidator} = require("devtools/app-manager/app-validator");
|
||||
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
|
||||
const AppActorFront = require("devtools/app-actor-front");
|
||||
const {AppActorFront} = require("devtools/app-actor-front");
|
||||
const {getDeviceFront} = require("devtools/server/actors/device");
|
||||
const {getPreferenceFront} = require("devtools/server/actors/preference");
|
||||
const {setTimeout} = require("sdk/timers");
|
||||
@ -46,9 +45,6 @@ exports.AppManager = AppManager = {
|
||||
this.onConnectionChanged = this.onConnectionChanged.bind(this);
|
||||
this.connection.on(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
|
||||
|
||||
this.onWebAppsStoreready = this.onWebAppsStoreready.bind(this);
|
||||
this.webAppsStore = new WebappsStore(this.connection);
|
||||
this.webAppsStore.on("store-ready", this.onWebAppsStoreready);
|
||||
this.tabStore = new TabStore(this.connection);
|
||||
this.onTabNavigate = this.onTabNavigate.bind(this);
|
||||
this.onTabClosed = this.onTabClosed.bind(this);
|
||||
@ -69,25 +65,18 @@ exports.AppManager = AppManager = {
|
||||
this.trackSimulatorRuntimes();
|
||||
|
||||
this.onInstallProgress = this.onInstallProgress.bind(this);
|
||||
AppActorFront.on("install-progress", this.onInstallProgress);
|
||||
|
||||
this.observe = this.observe.bind(this);
|
||||
Services.prefs.addObserver(WIFI_SCANNING_PREF, this, false);
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
AppActorFront.off("install-progress", this.onInstallProgress);
|
||||
this._unlistenToApps();
|
||||
this.selectedProject = null;
|
||||
this.selectedRuntime = null;
|
||||
this.untrackUSBRuntimes();
|
||||
this.untrackWiFiRuntimes();
|
||||
this.untrackSimulatorRuntimes();
|
||||
this._runningApps.clear();
|
||||
this.runtimeList = null;
|
||||
this.webAppsStore.off("store-ready", this.onWebAppsStoreready);
|
||||
this.webAppsStore.destroy();
|
||||
this.webAppsStore = null;
|
||||
this.tabStore.off("navigate", this.onTabNavigate);
|
||||
this.tabStore.off("closed", this.onTabClosed);
|
||||
this.tabStore.destroy();
|
||||
@ -136,14 +125,25 @@ exports.AppManager = AppManager = {
|
||||
|
||||
if (this.connection.status != Connection.Status.CONNECTED) {
|
||||
console.log("Connection status changed: " + this.connection.status);
|
||||
this._runningApps.clear();
|
||||
this._unlistenToApps();
|
||||
if (this._appsFront) {
|
||||
this._appsFront.off("install-progress", this.onInstallProgress);
|
||||
this._appsFront.unwatchApps();
|
||||
this._appsFront = null;
|
||||
}
|
||||
this._listTabsResponse = null;
|
||||
} else {
|
||||
this.connection.client.listTabs((response) => {
|
||||
this._listenToApps();
|
||||
let front = new AppActorFront(this.connection.client,
|
||||
response);
|
||||
front.on("install-progress", this.onInstallProgress);
|
||||
front.watchApps(() => this.checkIfProjectIsRunning())
|
||||
.then(() => front.fetchIcons())
|
||||
.then(() => {
|
||||
this._appsFront = front;
|
||||
this.checkIfProjectIsRunning();
|
||||
this.update("runtime-apps-found");
|
||||
});
|
||||
this._listTabsResponse = response;
|
||||
this._getRunningApps();
|
||||
this.update("list-tabs-response");
|
||||
});
|
||||
}
|
||||
@ -151,64 +151,26 @@ exports.AppManager = AppManager = {
|
||||
this.update("connection");
|
||||
},
|
||||
|
||||
get apps() {
|
||||
if (this._appsFront) {
|
||||
return this._appsFront.apps;
|
||||
} else {
|
||||
return new Map();
|
||||
}
|
||||
},
|
||||
|
||||
onInstallProgress: function(event, details) {
|
||||
this.update("install-progress", details);
|
||||
},
|
||||
|
||||
onWebAppsStoreready: function() {
|
||||
this.update("runtime-apps-found");
|
||||
},
|
||||
|
||||
_runningApps: new Set(),
|
||||
_getRunningApps: function() {
|
||||
let client = this.connection.client;
|
||||
if (!this._listTabsResponse.webappsActor) {
|
||||
return;
|
||||
}
|
||||
let request = {
|
||||
to: this._listTabsResponse.webappsActor,
|
||||
type: "listRunningApps"
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
if (res.error) {
|
||||
this.reportError("error_listRunningApps");
|
||||
console.error("listRunningApps error: " + res.error);
|
||||
}
|
||||
for (let m of res.apps) {
|
||||
this._runningApps.add(m);
|
||||
}
|
||||
});
|
||||
this.checkIfProjectIsRunning();
|
||||
},
|
||||
_listenToApps: function() {
|
||||
let client = this.connection.client;
|
||||
client.addListener("appOpen", (type, { manifestURL }) => {
|
||||
this._runningApps.add(manifestURL);
|
||||
this.checkIfProjectIsRunning();
|
||||
});
|
||||
|
||||
client.addListener("appClose", (type, { manifestURL }) => {
|
||||
this._runningApps.delete(manifestURL);
|
||||
this.checkIfProjectIsRunning();
|
||||
});
|
||||
|
||||
client.addListener("appUninstall", (type, { manifestURL }) => {
|
||||
this._runningApps.delete(manifestURL);
|
||||
this.checkIfProjectIsRunning();
|
||||
});
|
||||
},
|
||||
_unlistenToApps: function() {
|
||||
// Is that even possible?
|
||||
// connection.client is null now.
|
||||
},
|
||||
|
||||
isProjectRunning: function() {
|
||||
if (this.selectedProject.type == "mainProcess" ||
|
||||
this.selectedProject.type == "tab") {
|
||||
return true;
|
||||
}
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
return manifest && this._runningApps.has(manifest);
|
||||
|
||||
let app = this._getProjectFront(this.selectedProject);
|
||||
return app && app.running;
|
||||
},
|
||||
|
||||
checkIfProjectIsRunning: function() {
|
||||
@ -260,12 +222,10 @@ exports.AppManager = AppManager = {
|
||||
}
|
||||
return this.getTarget().then(target => {
|
||||
target.activeTab.reload();
|
||||
});
|
||||
}, console.error.bind(console));
|
||||
},
|
||||
|
||||
getTarget: function() {
|
||||
let client = this.connection.client;
|
||||
|
||||
if (this.selectedProject.type == "mainProcess") {
|
||||
return devtools.TargetFactory.forRemoteTab({
|
||||
form: this._listTabsResponse,
|
||||
@ -278,13 +238,11 @@ exports.AppManager = AppManager = {
|
||||
return this.tabStore.getTargetForTab();
|
||||
}
|
||||
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
if (!manifest) {
|
||||
console.error("Can't find manifestURL for selected project");
|
||||
return promise.reject();
|
||||
let app = this._getProjectFront(this.selectedProject);
|
||||
if (!app) {
|
||||
return promise.reject("Can't find app front for selected project");
|
||||
}
|
||||
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
return Task.spawn(function* () {
|
||||
// Once we asked the app to launch, the app isn't necessary completely loaded.
|
||||
// launch request only ask the app to launch and immediatly returns.
|
||||
@ -292,21 +250,18 @@ exports.AppManager = AppManager = {
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try {
|
||||
let target = yield AppActorFront.getTargetForApp(client, actor, manifest);
|
||||
// Success
|
||||
return target;
|
||||
return yield app.getTarget();
|
||||
} catch(e) {}
|
||||
let deferred = promise.defer();
|
||||
setTimeout(deferred.resolve, 500);
|
||||
yield deferred.promise;
|
||||
}
|
||||
|
||||
AppManager.reportError("error_cantConnectToApp", manifest);
|
||||
AppManager.reportError("error_cantConnectToApp", app.manifest.manifestURL);
|
||||
throw new Error("can't connect to app");
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
getProjectManifestURL: function(project) {
|
||||
let manifest = null;
|
||||
if (project.type == "runtimeApp") {
|
||||
@ -324,6 +279,14 @@ exports.AppManager = AppManager = {
|
||||
return manifest;
|
||||
},
|
||||
|
||||
_getProjectFront: function(project) {
|
||||
let manifest = this.getProjectManifestURL(project);
|
||||
if (manifest && this._appsFront) {
|
||||
return this._appsFront.apps.get(manifest);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_selectedProject: null,
|
||||
set selectedProject(value) {
|
||||
// A regular comparison still sees a difference when equal in some cases
|
||||
@ -445,23 +408,19 @@ exports.AppManager = AppManager = {
|
||||
if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
|
||||
return promise.reject("attempting to launch a non-runtime app");
|
||||
}
|
||||
let client = this.connection.client;
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
return AppActorFront.launchApp(client, actor, manifest);
|
||||
let app = this._getProjectFront(this.selectedProject);
|
||||
return app.launch();
|
||||
},
|
||||
|
||||
launchOrReloadRuntimeApp: function() {
|
||||
if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
|
||||
return promise.reject("attempting to launch / reload a non-runtime app");
|
||||
}
|
||||
let client = this.connection.client;
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
if (!this.isProjectRunning()) {
|
||||
return AppActorFront.launchApp(client, actor, manifest);
|
||||
let app = this._getProjectFront(this.selectedProject);
|
||||
if (!app.running) {
|
||||
return app.launch();
|
||||
} else {
|
||||
return AppActorFront.reloadApp(client, actor, manifest);
|
||||
return app.reload();
|
||||
}
|
||||
},
|
||||
|
||||
@ -488,22 +447,20 @@ exports.AppManager = AppManager = {
|
||||
return;
|
||||
}
|
||||
|
||||
let client = self.connection.client;
|
||||
let actor = self._listTabsResponse.webappsActor;
|
||||
let installPromise;
|
||||
|
||||
if (project.type != "packaged" && project.type != "hosted") {
|
||||
return promise.reject("Don't know how to install project");
|
||||
}
|
||||
|
||||
let response;
|
||||
if (project.type == "packaged") {
|
||||
let {appId} = yield AppActorFront.installPackaged(client,
|
||||
actor,
|
||||
project.location,
|
||||
response = yield self._appsFront.installPackaged(project.location,
|
||||
project.packagedAppOrigin);
|
||||
|
||||
// If the packaged app specified a custom origin override,
|
||||
// we need to update the local project origin
|
||||
project.packagedAppOrigin = appId;
|
||||
project.packagedAppOrigin = response.appId;
|
||||
// And ensure the indexed db on disk is also updated
|
||||
AppProjects.update(project);
|
||||
}
|
||||
@ -516,15 +473,13 @@ exports.AppManager = AppManager = {
|
||||
origin: origin.spec,
|
||||
manifestURL: project.location
|
||||
};
|
||||
yield AppActorFront.installHosted(client,
|
||||
actor,
|
||||
appId,
|
||||
response = yield self._appsFront.installHosted(appId,
|
||||
metadata,
|
||||
project.manifest);
|
||||
}
|
||||
|
||||
let manifest = self.getProjectManifestURL(project);
|
||||
if (!self._runningApps.has(manifest)) {
|
||||
let {app} = response;
|
||||
if (!app.running) {
|
||||
let deferred = promise.defer();
|
||||
self.on("app-manager-update", function onUpdate(event, what) {
|
||||
if (what == "project-is-running") {
|
||||
@ -532,20 +487,17 @@ exports.AppManager = AppManager = {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
yield AppActorFront.launchApp(client, actor, manifest);
|
||||
yield app.launch();
|
||||
yield deferred.promise;
|
||||
|
||||
} else {
|
||||
yield AppActorFront.reloadApp(client, actor, manifest);
|
||||
yield app.reload();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
stopRunningApp: function() {
|
||||
let client = this.connection.client;
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
return AppActorFront.closeApp(client, actor, manifest);
|
||||
let app = this._getProjectFront(this.selectedProject);
|
||||
return app.close();
|
||||
},
|
||||
|
||||
/* PROJECT VALIDATION */
|
||||
|
@ -26,7 +26,6 @@
|
||||
let appmgr = win.AppManager;
|
||||
ok(appmgr.connection, "App Manager connection ready");
|
||||
ok(appmgr.runtimeList, "Runtime list ready");
|
||||
ok(appmgr.webAppsStore, "WebApps store ready");
|
||||
|
||||
// test error reporting
|
||||
let nbox = win.document.querySelector("#notificationbox");
|
||||
|
@ -84,15 +84,15 @@ function zipDirectory(zipFile, dirToArchive) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function uploadPackage(client, webappsActor, packageFile) {
|
||||
function uploadPackage(client, webappsActor, packageFile, progressCallback) {
|
||||
if (client.traits.bulk) {
|
||||
return uploadPackageBulk(client, webappsActor, packageFile);
|
||||
return uploadPackageBulk(client, webappsActor, packageFile, progressCallback);
|
||||
} else {
|
||||
return uploadPackageJSON(client, webappsActor, packageFile);
|
||||
return uploadPackageJSON(client, webappsActor, packageFile, progressCallback);
|
||||
}
|
||||
}
|
||||
|
||||
function uploadPackageJSON(client, webappsActor, packageFile) {
|
||||
function uploadPackageJSON(client, webappsActor, packageFile, progressCallback) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let request = {
|
||||
@ -107,7 +107,7 @@ function uploadPackageJSON(client, webappsActor, packageFile) {
|
||||
let bytesRead = 0;
|
||||
|
||||
function emitProgress() {
|
||||
emitInstallProgress({
|
||||
progressCallback({
|
||||
bytesSent: bytesRead,
|
||||
totalBytes: fileSize
|
||||
});
|
||||
@ -163,7 +163,7 @@ function uploadPackageJSON(client, webappsActor, packageFile) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function uploadPackageBulk(client, webappsActor, packageFile) {
|
||||
function uploadPackageBulk(client, webappsActor, packageFile, progressCallback) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let request = {
|
||||
@ -190,7 +190,7 @@ function uploadPackageBulk(client, webappsActor, packageFile) {
|
||||
NetUtil.asyncFetch(packageFile, function(inputStream) {
|
||||
let copying = copyFrom(inputStream);
|
||||
copying.on("progress", (e, progress) => {
|
||||
emitInstallProgress(progress);
|
||||
progressCallback(progress);
|
||||
});
|
||||
copying.then(() => {
|
||||
console.log("Bulk upload done");
|
||||
@ -212,7 +212,14 @@ function removeServerTemporaryFile(client, fileActor) {
|
||||
client.request(request);
|
||||
}
|
||||
|
||||
function installPackaged(client, webappsActor, packagePath, appId) {
|
||||
/**
|
||||
* progressCallback argument:
|
||||
* Function called as packaged app installation proceeds.
|
||||
* The progress object passed to this function contains:
|
||||
* * bytesSent: The number of bytes sent so far
|
||||
* * totalBytes: The total number of bytes to send
|
||||
*/
|
||||
function installPackaged(client, webappsActor, packagePath, appId, progressCallback) {
|
||||
let deferred = promise.defer();
|
||||
let file = FileUtils.File(packagePath);
|
||||
let packagePromise;
|
||||
@ -225,7 +232,7 @@ function installPackaged(client, webappsActor, packagePath, appId) {
|
||||
packagePromise = promise.resolve(file);
|
||||
}
|
||||
packagePromise.then((zipFile) => {
|
||||
uploadPackage(client, webappsActor, zipFile)
|
||||
uploadPackage(client, webappsActor, zipFile, progressCallback)
|
||||
.then((fileActor) => {
|
||||
let request = {
|
||||
to: webappsActor,
|
||||
@ -260,16 +267,6 @@ function installPackaged(client, webappsActor, packagePath, appId) {
|
||||
}
|
||||
exports.installPackaged = installPackaged;
|
||||
|
||||
/**
|
||||
* Emits numerous events as packaged app installation proceeds.
|
||||
* The progress object contains:
|
||||
* * bytesSent: The number of bytes sent so far
|
||||
* * totalBytes: The total number of bytes to send
|
||||
*/
|
||||
function emitInstallProgress(progress) {
|
||||
exports.emit("install-progress", progress);
|
||||
}
|
||||
|
||||
function installHosted(client, webappsActor, appId, metadata, manifest) {
|
||||
let deferred = promise.defer();
|
||||
let request = {
|
||||
@ -396,6 +393,8 @@ function App(client, webappsActor, manifest) {
|
||||
|
||||
// This attribute is managed by the AppActorFront
|
||||
this.running = false;
|
||||
|
||||
this.iconURL = null;
|
||||
}
|
||||
|
||||
App.prototype = {
|
||||
@ -427,6 +426,48 @@ App.prototype = {
|
||||
});
|
||||
return this._target = target;
|
||||
});
|
||||
},
|
||||
|
||||
launch: function () {
|
||||
return launchApp(this.client, this.webappsActor,
|
||||
this.manifest.manifestURL);
|
||||
},
|
||||
|
||||
reload: function () {
|
||||
return reloadApp(this.client, this.webappsActor,
|
||||
this.manifest.manifestURL);
|
||||
},
|
||||
|
||||
close: function () {
|
||||
return closeApp(this.client, this.webappsActor,
|
||||
this.manifest.manifestURL)
|
||||
},
|
||||
|
||||
getIcon: function () {
|
||||
if (this.iconURL) {
|
||||
return promise.resolve(this.iconURL);
|
||||
}
|
||||
|
||||
let deferred = promise.defer();
|
||||
|
||||
let request = {
|
||||
to: this.webappsActor,
|
||||
type: "getIconAsDataURL",
|
||||
manifestURL: this.manifest.manifestURL
|
||||
};
|
||||
|
||||
this.client.request(request, res => {
|
||||
if (res.error) {
|
||||
deferred.reject(res.message || res.error);
|
||||
} else if (res.url) {
|
||||
this.iconURL = res.url;
|
||||
deferred.resolve(res.url);
|
||||
} else {
|
||||
deferred.reject("Unable to fetch app icon");
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
@ -439,8 +480,10 @@ function AppActorFront(client, form) {
|
||||
this.actor = form.webappsActor;
|
||||
|
||||
this._clientListener = this._clientListener.bind(this);
|
||||
this._onInstallProgress = this._onInstallProgress.bind(this);
|
||||
|
||||
this._listeners = [];
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
AppActorFront.prototype = {
|
||||
@ -475,7 +518,7 @@ AppActorFront.prototype = {
|
||||
* (and cache it per AppActorFront object)
|
||||
*/
|
||||
_getApp: function (manifestURL) {
|
||||
let app = this._apps.get(manifestURL);
|
||||
let app = this._apps ? this._apps.get(manifestURL) : null;
|
||||
if (app) {
|
||||
return promise.resolve(app);
|
||||
} else {
|
||||
@ -487,7 +530,9 @@ AppActorFront.prototype = {
|
||||
return this.client.request(request)
|
||||
.then(res => {
|
||||
let app = new App(this.client, this.actor, res.app);
|
||||
if (this._apps) {
|
||||
this._apps.set(manifestURL, app);
|
||||
}
|
||||
return app;
|
||||
}, e => {
|
||||
console.error("Unable to retrieve app", manifestURL, e);
|
||||
@ -500,33 +545,31 @@ AppActorFront.prototype = {
|
||||
* Needs to be called before using `apps` or `runningApps` attributes.
|
||||
*/
|
||||
watchApps: function (listener) {
|
||||
this._listeners.push(listener);
|
||||
// Fixes race between two references to the same front
|
||||
// calling watchApps at the same time
|
||||
if (this._loadingPromise) {
|
||||
return this._loadingPromise;
|
||||
}
|
||||
|
||||
// Only call watchApps for the first listener being register,
|
||||
// for all next ones, just send fake appOpen events for already
|
||||
// opened apps
|
||||
if (this._listeners.length > 1) {
|
||||
if (this._apps) {
|
||||
this.runningApps.forEach((app, manifestURL) => {
|
||||
listener("appOpen", app);
|
||||
});
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
let client = this.client;
|
||||
let f = this._clientListener;
|
||||
client.addListener("appOpen", f);
|
||||
client.addListener("appClose", f);
|
||||
client.addListener("appInstall", f);
|
||||
client.addListener("appUninstall", f);
|
||||
|
||||
// First retrieve all installed apps and create
|
||||
// related `App` object for each
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "getAll"
|
||||
};
|
||||
return this.client.request(request)
|
||||
return this._loadingPromise = this.client.request(request)
|
||||
.then(res => {
|
||||
delete this._loadingPromise;
|
||||
this._apps = new Map();
|
||||
for (let a of res.apps) {
|
||||
let app = new App(this.client, this.actor, a);
|
||||
@ -556,16 +599,80 @@ AppActorFront.prototype = {
|
||||
})
|
||||
.then(() => {
|
||||
// Finally ask to receive all app events
|
||||
return this._listenAppEvents(listener);
|
||||
});
|
||||
},
|
||||
|
||||
fetchIcons: function () {
|
||||
// On demand, retrieve apps icons in order to be able
|
||||
// to synchronously retrieve it on `App` objects
|
||||
let promises = [];
|
||||
for (let [manifestURL, app] of this._apps) {
|
||||
promises.push(app.getIcon());
|
||||
}
|
||||
return promise.all(promises)
|
||||
.then(null, () => {}); // Ignore any failure
|
||||
},
|
||||
|
||||
_listenAppEvents: function (listener) {
|
||||
this._listeners.push(listener);
|
||||
|
||||
if (this._listeners.length > 1) {
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
let client = this.client;
|
||||
let f = this._clientListener;
|
||||
client.addListener("appOpen", f);
|
||||
client.addListener("appClose", f);
|
||||
client.addListener("appInstall", f);
|
||||
client.addListener("appUninstall", f);
|
||||
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "watchApps"
|
||||
};
|
||||
return this.client.request(request);
|
||||
});
|
||||
},
|
||||
|
||||
_unlistenAppEvents: function (listener) {
|
||||
let idx = this._listeners.indexOf(listener);
|
||||
if (idx != -1) {
|
||||
this._listeners.splice(idx, 1);
|
||||
}
|
||||
|
||||
// Until we released all listener, we don't ask to stop sending events
|
||||
if (this._listeners.length != 0) {
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
let client = this.client;
|
||||
let f = this._clientListener;
|
||||
client.removeListener("appOpen", f);
|
||||
client.removeListener("appClose", f);
|
||||
client.removeListener("appInstall", f);
|
||||
client.removeListener("appUninstall", f);
|
||||
|
||||
// Remove `_apps` in order to allow calling watchApps again
|
||||
// and repopulate the apps Map.
|
||||
delete this._apps;
|
||||
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "unwatchApps"
|
||||
};
|
||||
return this.client.request(request);
|
||||
},
|
||||
|
||||
_clientListener: function (type, message) {
|
||||
|
||||
let { manifestURL } = message;
|
||||
|
||||
// Reset the app object to get a fresh copy when we (re)install the app.
|
||||
if (type == "appInstall" && this._apps && this._apps.has(manifestURL)) {
|
||||
this._apps.delete(manifestURL);
|
||||
}
|
||||
|
||||
this._getApp(manifestURL).then((app) => {
|
||||
switch(type) {
|
||||
case "appOpen":
|
||||
@ -573,8 +680,23 @@ AppActorFront.prototype = {
|
||||
break;
|
||||
case "appClose":
|
||||
app.running = false;
|
||||
break;
|
||||
case "appInstall":
|
||||
// The call to _getApp is going to create App object
|
||||
|
||||
// This app may have been running while being installed, so check the list
|
||||
// of running apps again to get the right answer.
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "listRunningApps"
|
||||
};
|
||||
this.client.request(request)
|
||||
.then(res => {
|
||||
if (res.apps.indexOf(manifestURL) !== -1) {
|
||||
app.running = true;
|
||||
this._notifyListeners("appOpen", app);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "appUninstall":
|
||||
// Fake a appClose event if we didn't got one before uninstall
|
||||
@ -588,6 +710,7 @@ AppActorFront.prototype = {
|
||||
return;
|
||||
}
|
||||
this._notifyListeners(type, app);
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
@ -598,31 +721,101 @@ AppActorFront.prototype = {
|
||||
},
|
||||
|
||||
unwatchApps: function (listener) {
|
||||
let idx = this._listeners.indexOf(listener);
|
||||
if (idx != -1) {
|
||||
this._listeners.splice(idx, 1);
|
||||
}
|
||||
return this._unlistenAppEvents(listener);
|
||||
},
|
||||
|
||||
// Until we released all listener, we don't ask to stop sending events
|
||||
if (this._listeners.length != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let request = {
|
||||
to: this.actor,
|
||||
type: "unwatchApps"
|
||||
/*
|
||||
* Install a packaged app.
|
||||
*
|
||||
* Events are going to be emitted on the front
|
||||
* as install progresses. Events will have the following fields:
|
||||
* * bytesSent: The number of bytes sent so far
|
||||
* * totalBytes: The total number of bytes to send
|
||||
*/
|
||||
installPackaged: function (packagePath, appId) {
|
||||
let request = () => {
|
||||
return installPackaged(this.client, this.actor, packagePath, appId,
|
||||
this._onInstallProgress)
|
||||
.then(response => ({
|
||||
appId: response.appId,
|
||||
manifestURL: "app://" + response.appId + "/manifest.webapp"
|
||||
}));
|
||||
};
|
||||
this.client.request(request);
|
||||
return this._install(request);
|
||||
},
|
||||
|
||||
let client = this.client;
|
||||
let f = this._clientListener;
|
||||
client.removeListener("appOpen", f);
|
||||
client.removeListener("appClose", f);
|
||||
client.removeListener("appInstall", f);
|
||||
client.removeListener("appUninstall", f);
|
||||
_onInstallProgress: function (progress) {
|
||||
this.emit("install-progress", progress);
|
||||
},
|
||||
|
||||
_install: function (request) {
|
||||
let deferred = promise.defer();
|
||||
let finalAppId = null, manifestURL = null;
|
||||
let installs = {};
|
||||
|
||||
// We need to resolve only once the request is done *AND*
|
||||
// once we receive the related appInstall message for
|
||||
// the same manifestURL
|
||||
let resolve = app => {
|
||||
this._unlistenAppEvents(listener);
|
||||
installs = null;
|
||||
deferred.resolve({ app: app, appId: finalAppId });
|
||||
};
|
||||
|
||||
// Listen for appInstall event, in order to resolve with
|
||||
// the matching app object.
|
||||
let listener = (type, app) => {
|
||||
if (type == "appInstall") {
|
||||
// Resolves immediately if the request has already resolved
|
||||
// or just flag the installed app to eventually resolve
|
||||
// when the request gets its response.
|
||||
if (app.manifest.manifestURL === manifestURL) {
|
||||
resolve(app);
|
||||
} else {
|
||||
installs[app.manifest.manifestURL] = app;
|
||||
}
|
||||
}
|
||||
};
|
||||
this._listenAppEvents(listener)
|
||||
// Execute the request
|
||||
.then(request)
|
||||
.then(response => {
|
||||
finalAppId = response.appId;
|
||||
manifestURL = response.manifestURL;
|
||||
|
||||
// Resolves immediately if the appInstall event
|
||||
// was dispatched during the request.
|
||||
if (manifestURL in installs) {
|
||||
resolve(installs[manifestURL]);
|
||||
}
|
||||
}, deferred.reject);
|
||||
|
||||
return deferred.promise;
|
||||
|
||||
},
|
||||
|
||||
/*
|
||||
* Install a hosted app.
|
||||
*
|
||||
* Events are going to be emitted on the front
|
||||
* as install progresses. Events will have the following fields:
|
||||
* * bytesSent: The number of bytes sent so far
|
||||
* * totalBytes: The total number of bytes to send
|
||||
*/
|
||||
installHosted: function (appId, metadata, manifest) {
|
||||
let manifestURL = metadata.manifestURL ||
|
||||
metadata.origin + "/manifest.webapp";
|
||||
let request = () => {
|
||||
return installHosted(this.client, this.actor, appId, metadata,
|
||||
manifest)
|
||||
.then(response => ({
|
||||
appId: response.appId,
|
||||
manifestURL: manifestURL
|
||||
}));
|
||||
};
|
||||
return this._install(request);
|
||||
}
|
||||
}
|
||||
|
||||
exports.AppActorFront = AppActorFront;
|
||||
EventEmitter.decorate(exports);
|
||||
|
||||
|
@ -13,8 +13,11 @@ Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const {require} = devtools;
|
||||
const {AppActorFront} = require("devtools/app-actor-front");
|
||||
|
||||
let gClient, gActor;
|
||||
let gClient, gActor, gActorFront;
|
||||
|
||||
function connect(onDone) {
|
||||
// Initialize a loopback remote protocol connection
|
||||
@ -28,6 +31,7 @@ function connect(onDone) {
|
||||
gClient.connect(function onConnect() {
|
||||
gClient.listTabs(function onListTabs(aResponse) {
|
||||
gActor = aResponse.webappsActor;
|
||||
gActorFront = new AppActorFront(gClient, aResponse);
|
||||
onDone();
|
||||
});
|
||||
});
|
||||
|
@ -1,10 +1,6 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const {require} = devtools;
|
||||
const AppActorFront = require("devtools/app-actor-front");
|
||||
const {installHosted, installPackaged} = AppActorFront;
|
||||
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
|
||||
let gAppId = "actor-test";
|
||||
@ -183,15 +179,15 @@ add_test(function testFileUploadInstall() {
|
||||
|
||||
let progressDeferred = promise.defer();
|
||||
// Ensure we get at least one progress event at the end
|
||||
AppActorFront.on("install-progress", function onProgress(e, progress) {
|
||||
gActorFront.on("install-progress", function onProgress(e, progress) {
|
||||
if (progress.bytesSent == progress.totalBytes) {
|
||||
AppActorFront.off("install-progress", onProgress);
|
||||
gActorFront.off("install-progress", onProgress);
|
||||
progressDeferred.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
let installed =
|
||||
installPackaged(gClient, gActor, packageFile.path, gAppId)
|
||||
gActorFront.installPackaged(packageFile.path, gAppId)
|
||||
.then(function ({ appId }) {
|
||||
do_check_eq(appId, gAppId);
|
||||
}, function (e) {
|
||||
@ -212,15 +208,15 @@ add_test(function testBulkUploadInstall() {
|
||||
|
||||
let progressDeferred = promise.defer();
|
||||
// Ensure we get at least one progress event at the end
|
||||
AppActorFront.on("install-progress", function onProgress(e, progress) {
|
||||
gActorFront.on("install-progress", function onProgress(e, progress) {
|
||||
if (progress.bytesSent == progress.totalBytes) {
|
||||
AppActorFront.off("install-progress", onProgress);
|
||||
gActorFront.off("install-progress", onProgress);
|
||||
progressDeferred.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
let installed =
|
||||
installPackaged(gClient, gActor, packageFile.path, gAppId)
|
||||
gActorFront.installPackaged(packageFile.path, gAppId)
|
||||
.then(function ({ appId }) {
|
||||
do_check_eq(appId, gAppId);
|
||||
}, function (e) {
|
||||
@ -242,15 +238,14 @@ add_test(function testInstallHosted() {
|
||||
name: "My hosted app",
|
||||
csp: "script-src: http://foo.com"
|
||||
};
|
||||
installHosted(gClient, gActor, gAppId, metadata, manifest).then(
|
||||
function ({ appId }) {
|
||||
gActorFront.installHosted(gAppId, metadata, manifest)
|
||||
.then(function ({ appId }) {
|
||||
do_check_eq(appId, gAppId);
|
||||
run_next_test();
|
||||
},
|
||||
function (e) {
|
||||
do_throw("Failed installing hosted app: " + e.error + ": " + e.message);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function testCheckHostedApp() {
|
||||
|
Loading…
Reference in New Issue
Block a user