mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-25 08:37:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
365
dom/downloads/DownloadsAPI.jsm
Normal file
365
dom/downloads/DownloadsAPI.jsm
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Downloads.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageBroadcaster");
|
||||
|
||||
/**
|
||||
* Parent process logic that services download API requests from the
|
||||
* DownloadAPI.js instances in content processeses. The actual work of managing
|
||||
* downloads is done by Toolkit's Downloads.jsm. This module is loaded by B2G's
|
||||
* shell.js
|
||||
*/
|
||||
|
||||
function debug(aStr) {
|
||||
#ifdef MOZ_DEBUG
|
||||
dump("-*- DownloadsAPI.jsm : " + aStr + "\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
function sendPromiseMessage(aMm, aMessageName, aData, aError) {
|
||||
debug("sendPromiseMessage " + aMessageName);
|
||||
let msg = {
|
||||
id: aData.id,
|
||||
promiseId: aData.promiseId
|
||||
};
|
||||
|
||||
if (aError) {
|
||||
msg.error = aError;
|
||||
}
|
||||
|
||||
aMm.sendAsyncMessage(aMessageName, msg);
|
||||
}
|
||||
|
||||
var DownloadsAPI = {
|
||||
init: function() {
|
||||
debug("init");
|
||||
|
||||
this._ids = new WeakMap(); // Maps toolkit download objects to ids.
|
||||
this._index = {}; // Maps ids to downloads.
|
||||
|
||||
["Downloads:GetList",
|
||||
"Downloads:ClearAllDone",
|
||||
"Downloads:Remove",
|
||||
"Downloads:Pause",
|
||||
"Downloads:Resume",
|
||||
"Downloads:Adopt"].forEach((msgName) => {
|
||||
ppmm.addMessageListener(msgName, this);
|
||||
});
|
||||
|
||||
let self = this;
|
||||
Task.spawn(function () {
|
||||
let list = yield Downloads.getList(Downloads.ALL);
|
||||
yield list.addView(self);
|
||||
|
||||
debug("view added to download list.");
|
||||
}).then(null, Components.utils.reportError);
|
||||
|
||||
this._currentId = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a unique id for each download, hashing the url and the path.
|
||||
*/
|
||||
downloadId: function(aDownload) {
|
||||
let id = this._ids.get(aDownload, null);
|
||||
if (!id) {
|
||||
id = "download-" + this._currentId++;
|
||||
this._ids.set(aDownload, id);
|
||||
this._index[id] = aDownload;
|
||||
}
|
||||
return id;
|
||||
},
|
||||
|
||||
getDownloadById: function(aId) {
|
||||
return this._index[aId];
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a download object into a plain json object that we'll
|
||||
* send to the DOM side.
|
||||
*/
|
||||
jsonDownload: function(aDownload) {
|
||||
let res = {
|
||||
totalBytes: aDownload.totalBytes,
|
||||
currentBytes: aDownload.currentBytes,
|
||||
url: aDownload.source.url,
|
||||
path: aDownload.target.path,
|
||||
contentType: aDownload.contentType,
|
||||
startTime: aDownload.startTime.getTime(),
|
||||
sourceAppManifestURL: aDownload._unknownProperties &&
|
||||
aDownload._unknownProperties.sourceAppManifestURL
|
||||
};
|
||||
|
||||
if (aDownload.error) {
|
||||
res.error = aDownload.error;
|
||||
}
|
||||
|
||||
res.id = this.downloadId(aDownload);
|
||||
|
||||
// The state of the download. Can be any of "downloading", "stopped",
|
||||
// "succeeded", finalized".
|
||||
|
||||
// Default to "stopped"
|
||||
res.state = "stopped";
|
||||
if (!aDownload.stopped &&
|
||||
!aDownload.canceled &&
|
||||
!aDownload.succeeded &&
|
||||
!aDownload.DownloadError) {
|
||||
res.state = "downloading";
|
||||
} else if (aDownload.succeeded) {
|
||||
res.state = "succeeded";
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
/**
|
||||
* download view methods.
|
||||
*/
|
||||
onDownloadAdded: function(aDownload) {
|
||||
let download = this.jsonDownload(aDownload);
|
||||
debug("onDownloadAdded " + uneval(download));
|
||||
ppmm.broadcastAsyncMessage("Downloads:Added", download);
|
||||
},
|
||||
|
||||
onDownloadRemoved: function(aDownload) {
|
||||
let download = this.jsonDownload(aDownload);
|
||||
download.state = "finalized";
|
||||
debug("onDownloadRemoved " + uneval(download));
|
||||
ppmm.broadcastAsyncMessage("Downloads:Removed", download);
|
||||
this._index[this._ids.get(aDownload)] = null;
|
||||
this._ids.delete(aDownload);
|
||||
},
|
||||
|
||||
onDownloadChanged: function(aDownload) {
|
||||
let download = this.jsonDownload(aDownload);
|
||||
debug("onDownloadChanged " + uneval(download));
|
||||
ppmm.broadcastAsyncMessage("Downloads:Changed", download);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (!aMessage.target.assertPermission("downloads")) {
|
||||
debug("No 'downloads' permission!");
|
||||
return;
|
||||
}
|
||||
|
||||
debug("message: " + aMessage.name);
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "Downloads:GetList":
|
||||
this.getList(aMessage.data, aMessage.target);
|
||||
break;
|
||||
case "Downloads:ClearAllDone":
|
||||
this.clearAllDone(aMessage.data, aMessage.target);
|
||||
break;
|
||||
case "Downloads:Remove":
|
||||
this.remove(aMessage.data, aMessage.target);
|
||||
break;
|
||||
case "Downloads:Pause":
|
||||
this.pause(aMessage.data, aMessage.target);
|
||||
break;
|
||||
case "Downloads:Resume":
|
||||
this.resume(aMessage.data, aMessage.target);
|
||||
break;
|
||||
case "Downloads:Adopt":
|
||||
this.adoptDownload(aMessage.data, aMessage.target);
|
||||
break;
|
||||
default:
|
||||
debug("Invalid message: " + aMessage.name);
|
||||
}
|
||||
},
|
||||
|
||||
getList: function(aData, aMm) {
|
||||
debug("getList called!");
|
||||
let self = this;
|
||||
Task.spawn(function () {
|
||||
let list = yield Downloads.getList(Downloads.ALL);
|
||||
let downloads = yield list.getAll();
|
||||
let res = [];
|
||||
downloads.forEach((aDownload) => {
|
||||
res.push(self.jsonDownload(aDownload));
|
||||
});
|
||||
aMm.sendAsyncMessage("Downloads:GetList:Return", res);
|
||||
}).then(null, Components.utils.reportError);
|
||||
},
|
||||
|
||||
clearAllDone: function(aData, aMm) {
|
||||
debug("clearAllDone called!");
|
||||
Task.spawn(function () {
|
||||
let list = yield Downloads.getList(Downloads.ALL);
|
||||
list.removeFinished();
|
||||
}).then(null, Components.utils.reportError);
|
||||
},
|
||||
|
||||
remove: function(aData, aMm) {
|
||||
debug("remove id " + aData.id);
|
||||
let download = this.getDownloadById(aData.id);
|
||||
if (!download) {
|
||||
sendPromiseMessage(aMm, "Downloads:Remove:Return",
|
||||
aData, "NoSuchDownload");
|
||||
return;
|
||||
}
|
||||
|
||||
Task.spawn(function() {
|
||||
yield download.finalize(true);
|
||||
let list = yield Downloads.getList(Downloads.ALL);
|
||||
yield list.remove(download);
|
||||
}).then(
|
||||
function() {
|
||||
sendPromiseMessage(aMm, "Downloads:Remove:Return", aData);
|
||||
},
|
||||
function() {
|
||||
sendPromiseMessage(aMm, "Downloads:Remove:Return",
|
||||
aData, "RemoveError");
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
pause: function(aData, aMm) {
|
||||
debug("pause id " + aData.id);
|
||||
let download = this.getDownloadById(aData.id);
|
||||
if (!download) {
|
||||
sendPromiseMessage(aMm, "Downloads:Pause:Return",
|
||||
aData, "NoSuchDownload");
|
||||
return;
|
||||
}
|
||||
|
||||
download.cancel().then(
|
||||
function() {
|
||||
sendPromiseMessage(aMm, "Downloads:Pause:Return", aData);
|
||||
},
|
||||
function() {
|
||||
sendPromiseMessage(aMm, "Downloads:Pause:Return",
|
||||
aData, "PauseError");
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
resume: function(aData, aMm) {
|
||||
debug("resume id " + aData.id);
|
||||
let download = this.getDownloadById(aData.id);
|
||||
if (!download) {
|
||||
sendPromiseMessage(aMm, "Downloads:Resume:Return",
|
||||
aData, "NoSuchDownload");
|
||||
return;
|
||||
}
|
||||
|
||||
download.start().then(
|
||||
function() {
|
||||
sendPromiseMessage(aMm, "Downloads:Resume:Return", aData);
|
||||
},
|
||||
function() {
|
||||
sendPromiseMessage(aMm, "Downloads:Resume:Return",
|
||||
aData, "ResumeError");
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Receive a download to adopt in the same representation we produce from
|
||||
* our "jsonDownload" normalizer and add it to the list of downloads.
|
||||
*/
|
||||
adoptDownload: function(aData, aMm) {
|
||||
let adoptJsonRep = aData.jsonDownload;
|
||||
debug("adoptDownload " + uneval(adoptJsonRep));
|
||||
|
||||
Task.spawn(function* () {
|
||||
// Verify that the file exists on disk. This will result in a rejection
|
||||
// if the file does not exist. We will also use this information for the
|
||||
// file size to avoid weird inconsistencies. We ignore the filesystem
|
||||
// timestamp in favor of whatever the caller is telling us.
|
||||
let fileInfo = yield OS.File.stat(adoptJsonRep.path);
|
||||
|
||||
// We also require that the file is not a directory.
|
||||
if (fileInfo.isDir) {
|
||||
throw new Error("AdoptFileIsDirectory");
|
||||
}
|
||||
|
||||
// We need to create a Download instance to add to the list. Create a
|
||||
// serialized representation and then from there the instance.
|
||||
let serializedRep = {
|
||||
// explicit initializations in toSerializable
|
||||
source: {
|
||||
url: adoptJsonRep.url
|
||||
// This is where isPrivate would go if adoption supported private
|
||||
// browsing.
|
||||
},
|
||||
target: {
|
||||
path: adoptJsonRep.path,
|
||||
},
|
||||
startTime: adoptJsonRep.startTime,
|
||||
// kPlainSerializableDownloadProperties propagations
|
||||
succeeded: true, // (all adopted downloads are required to be completed)
|
||||
totalBytes: fileInfo.size,
|
||||
contentType: adoptJsonRep.contentType,
|
||||
// unknown properties added/used by the DownloadsAPI
|
||||
currentBytes: fileInfo.size,
|
||||
sourceAppManifestURL: adoptJsonRep.sourceAppManifestURL
|
||||
};
|
||||
|
||||
let download = yield Downloads.createDownload(serializedRep);
|
||||
|
||||
// The ALL list is a DownloadCombinedList instance that combines the
|
||||
// PUBLIC (persisted to disk) and PRIVATE (ephemeral) download lists..
|
||||
// When we call add on it, it dispatches to the appropriate list based on
|
||||
// the 'isPrivate' field of the source. (Which we don't initialize and
|
||||
// defaults to false.)
|
||||
let allDownloadList = yield Downloads.getList(Downloads.ALL);
|
||||
|
||||
// This add will automatically notify all views of the added download,
|
||||
// including DownloadsAPI instances and the DownloadAutoSaveView that's
|
||||
// subscribed to the PUBLIC list and will save the download.
|
||||
yield allDownloadList.add(download);
|
||||
|
||||
debug("download adopted");
|
||||
// The notification above occurred synchronously, and so we will have
|
||||
// already dispatched an added notification for our download to the child
|
||||
// process in question. As such, we only need to relay the download id
|
||||
// since the download will already have been cached.
|
||||
return download;
|
||||
}.bind(this)).then(
|
||||
(download) => {
|
||||
sendPromiseMessage(aMm, "Downloads:Adopt:Return",
|
||||
{
|
||||
id: this.downloadId(download),
|
||||
promiseId: aData.promiseId
|
||||
});
|
||||
},
|
||||
(ex) => {
|
||||
let reportAs = "AdoptError";
|
||||
// Provide better error codes for expected errors.
|
||||
if (ex instanceof OS.File.Error && ex.becauseNoSuchFile) {
|
||||
reportAs = "AdoptNoSuchFile";
|
||||
} else if (ex.message === "AdoptFileIsDirectory") {
|
||||
reportAs = ex.message;
|
||||
} else {
|
||||
// Anything else is unexpected and should be reported to help track
|
||||
// down what's going wrong.
|
||||
debug("unexpected download error: " + ex);
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
sendPromiseMessage(aMm, "Downloads:Adopt:Return",
|
||||
{
|
||||
promiseId: aData.promiseId
|
||||
},
|
||||
reportAs);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
DownloadsAPI.init();
|
||||
Loading…
Add table
Add a link
Reference in a new issue