mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-31 19:47:32 +09:00
[PALEMOON] Bug 1129896 - Part 1 of 2 - Keep cached metadata for history downloads indefinitely
This commit is contained in:
parent
dfed6fdfe6
commit
b5c90dfea0
1 changed files with 69 additions and 76 deletions
|
|
@ -46,18 +46,16 @@ HistoryDownload.prototype = {
|
||||||
/**
|
/**
|
||||||
* Pushes information from Places metadata into this object.
|
* Pushes information from Places metadata into this object.
|
||||||
*/
|
*/
|
||||||
updateFromMetaData(aPlacesMetaData) {
|
updateFromMetaData(metaData) {
|
||||||
try {
|
try {
|
||||||
this.target.path = Cc["@mozilla.org/network/protocol;1?name=file"]
|
this.target.path = Cc["@mozilla.org/network/protocol;1?name=file"]
|
||||||
.getService(Ci.nsIFileProtocolHandler)
|
.getService(Ci.nsIFileProtocolHandler)
|
||||||
.getFileFromURLSpec(aPlacesMetaData.
|
.getFileFromURLSpec(metaData.targetFileSpec).path;
|
||||||
targetFileURISpec).path;
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
this.target.path = undefined;
|
this.target.path = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if ("state" in metaData) {
|
||||||
let metaData = JSON.parse(aPlacesMetaData.jsonDetails);
|
|
||||||
this.succeeded = metaData.state == nsIDM.DOWNLOAD_FINISHED;
|
this.succeeded = metaData.state == nsIDM.DOWNLOAD_FINISHED;
|
||||||
this.error = metaData.state == nsIDM.DOWNLOAD_FAILED
|
this.error = metaData.state == nsIDM.DOWNLOAD_FAILED
|
||||||
? { message: "History download failed." }
|
? { message: "History download failed." }
|
||||||
|
|
@ -74,7 +72,7 @@ HistoryDownload.prototype = {
|
||||||
// is refreshed, at which point these values may be updated.
|
// is refreshed, at which point these values may be updated.
|
||||||
this.target.exists = true;
|
this.target.exists = true;
|
||||||
this.target.size = metaData.fileSize;
|
this.target.size = metaData.fileSize;
|
||||||
} catch (ex) {
|
} else {
|
||||||
// Metadata might be missing from a download that has started but hasn't
|
// Metadata might be missing from a download that has started but hasn't
|
||||||
// stopped already. Normally, this state is overridden with the one from
|
// stopped already. Normally, this state is overridden with the one from
|
||||||
// the corresponding in-progress session download. But if the browser is
|
// the corresponding in-progress session download. But if the browser is
|
||||||
|
|
@ -517,94 +515,82 @@ DownloadsPlacesView.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map containing a metadata object for each download source URI found in
|
* This cache exists in order to optimize the load of the Downloads View, when
|
||||||
* Places annotations, or null if this cache must be rebuilt.
|
* Places annotations for history downloads must be read. In fact, annotations
|
||||||
|
* are stored in a single table, and reading all of them at once is much more
|
||||||
|
* efficient than an individual query.
|
||||||
|
*
|
||||||
|
* When this property is first requested, it reads the annotations for all the
|
||||||
|
* history downloads and stores them indefinitely.
|
||||||
|
*
|
||||||
|
* The historical annotations are not expected to change for the duration of
|
||||||
|
* the session, except in the case where a session download is running for the
|
||||||
|
* same URI as a history download. To ensure we don't use stale data, URIs
|
||||||
|
* corresponding to session downloads are permanently removed from the cache.
|
||||||
|
* This is a very small mumber compared to history downloads.
|
||||||
|
*
|
||||||
|
* This property returns a Map from each download source URI found in Places
|
||||||
|
* annotations to an object with the format:
|
||||||
|
*
|
||||||
|
* { targetFileSpec, state, endTime, fileSize, ... }
|
||||||
|
*
|
||||||
|
* The targetFileSpec property is the value of "downloads/destinationFileURI",
|
||||||
|
* while the other properties are taken from "downloads/metaData". Any of the
|
||||||
|
* properties may be missing from the object.
|
||||||
*/
|
*/
|
||||||
_cachedPlacesMetaData: null,
|
get _cachedPlacesMetaData() {
|
||||||
|
if (!this.__cachedPlacesMetaData) {
|
||||||
|
this.__cachedPlacesMetaData = new Map();
|
||||||
|
|
||||||
/**
|
// Read the metadata annotations first, but ignore invalid JSON.
|
||||||
* This method exists in order to optimize the first load of the Downloads
|
|
||||||
* View, when Places annotations for history downloads must be read. In fact,
|
|
||||||
* annotations are stored in a single table, and reading all of them at once
|
|
||||||
* is much more efficient than an individual query.
|
|
||||||
*
|
|
||||||
* When this metod is first called, after the Places result container has been
|
|
||||||
* invalidated, it reads the annotations for all the history downloads.
|
|
||||||
*
|
|
||||||
* At this point, as metadata for the various elements is requested by the
|
|
||||||
* view, it is pulled from the cache and returned as an object.
|
|
||||||
*
|
|
||||||
* This works efficiently because the metadata is requested only for newly
|
|
||||||
* created element shells that contain a history download but don't have a
|
|
||||||
* matching session download, and at most one such element shell is created
|
|
||||||
* for each source URI.
|
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
* URI string of the Places node for which metadata is requested.
|
|
||||||
*
|
|
||||||
* @return Object of the form { targetFileURISpec, jsonDetails }.
|
|
||||||
* Any of the properties may be missing from the object.
|
|
||||||
*/
|
|
||||||
_getCachedPlacesMetaDataFor(url) {
|
|
||||||
if (!this._cachedPlacesMetaData) {
|
|
||||||
this._cachedPlacesMetaData = new Map();
|
|
||||||
|
|
||||||
// Use the destination file annotation to build the initial Map of items.
|
|
||||||
for (let result of PlacesUtils.annotations.getAnnotationsWithName(
|
|
||||||
DESTINATION_FILE_URI_ANNO)) {
|
|
||||||
this._cachedPlacesMetaData.set(result.uri.spec, {
|
|
||||||
targetFileURISpec: result.annotationValue,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the string with the JSON details from the metadata annotation.
|
|
||||||
for (let result of PlacesUtils.annotations.getAnnotationsWithName(
|
for (let result of PlacesUtils.annotations.getAnnotationsWithName(
|
||||||
DOWNLOAD_META_DATA_ANNO)) {
|
DOWNLOAD_META_DATA_ANNO)) {
|
||||||
let metadata = this._cachedPlacesMetaData.get(result.uri.spec);
|
try {
|
||||||
|
this.__cachedPlacesMetaData.set(result.uri.spec,
|
||||||
|
JSON.parse(result.annotationValue));
|
||||||
|
} catch (ex) {}
|
||||||
|
}
|
||||||
|
|
||||||
// The destination file annotation is expected to be present for all
|
// Add the target file annotations to the metadata.
|
||||||
// the downloads with the metadata annotation. If this is not the case,
|
for (let result of PlacesUtils.annotations.getAnnotationsWithName(
|
||||||
// we simply ignore the item, as if it has no JSON details at all.
|
DESTINATION_FILE_URI_ANNO)) {
|
||||||
if (metadata) {
|
let metaData = this.__cachedPlacesMetaData.get(result.uri.spec);
|
||||||
metadata.jsonDetails = result.annotationValue;
|
if (!metaData) {
|
||||||
|
metaData = {};
|
||||||
|
this.__cachedPlacesMetaData.set(result.uri.spec, metaData);
|
||||||
}
|
}
|
||||||
|
metaData.targetFileSpec = result.annotationValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let metadata = this._cachedPlacesMetaData.get(url);
|
return this.__cachedPlacesMetaData;
|
||||||
this._cachedPlacesMetaData.delete(url);
|
|
||||||
|
|
||||||
// In the rare case where a history download is added again, but there is no
|
|
||||||
// corresponding session download, read the metadata from the database. This
|
|
||||||
// could potentially cause an inefficient database access for very old
|
|
||||||
// downloads with no metadata to begin with, but these should have been
|
|
||||||
// already deleted from history on a recent browser profile.
|
|
||||||
return metadata || this._getPlacesMetaDataFor(url);
|
|
||||||
},
|
},
|
||||||
|
__cachedPlacesMetaData: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the latest version of the Places metadata for the given URI.
|
* Reads current metadata from Places annotations for the specified URI, and
|
||||||
|
* returns an object with the format:
|
||||||
*
|
*
|
||||||
* @param url
|
* { targetFileSpec, state, endTime, fileSize, ... }
|
||||||
* URI string of the Places node for which metadata is requested.
|
|
||||||
*
|
*
|
||||||
* @return Object of the form { targetFileURISpec, jsonDetails }.
|
* The targetFileSpec property is the value of "downloads/destinationFileURI",
|
||||||
* Any of the properties may be missing from the object.
|
* while the other properties are taken from "downloads/metaData". Any of the
|
||||||
|
* properties may be missing from the object.
|
||||||
*/
|
*/
|
||||||
_getPlacesMetaDataFor(url) {
|
_getPlacesMetaDataFor(spec) {
|
||||||
let metadata = {};
|
let metaData = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let uri = NetUtil.newURI(url);
|
let uri = NetUtil.newURI(spec);
|
||||||
metadata = {
|
try {
|
||||||
targetFileURISpec: PlacesUtils.annotations.getPageAnnotation(
|
metaData = JSON.parse(PlacesUtils.annotations.getPageAnnotation(
|
||||||
uri, DESTINATION_FILE_URI_ANNO),
|
uri, DOWNLOAD_META_DATA_ANNO));
|
||||||
};
|
} catch (ex) {}
|
||||||
metadata.jsonDetails = PlacesUtils.annotations.getPageAnnotation(
|
metaData.targetFileSpec = PlacesUtils.annotations.getPageAnnotation(
|
||||||
uri, DOWNLOAD_META_DATA_ANNO);
|
uri, DESTINATION_FILE_URI_ANNO);
|
||||||
} catch (ex) {}
|
} catch (ex) {}
|
||||||
|
|
||||||
return metadata;
|
return metaData;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -642,6 +628,18 @@ DownloadsPlacesView.prototype = {
|
||||||
this._downloadElementsShellsForURI.set(downloadURI, shellsForURI);
|
this._downloadElementsShellsForURI.set(downloadURI, shellsForURI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When a session download is attached to a shell, we ensure not to keep
|
||||||
|
// stale metadata around for the corresponding history download. This
|
||||||
|
// prevents stale state from being used if the view is rebuilt.
|
||||||
|
//
|
||||||
|
// Note that we will eagerly load the data in the cache at this point, even
|
||||||
|
// if we have seen no history download. The case where no history download
|
||||||
|
// will appear at all is rare enough in normal usage, so we can apply this
|
||||||
|
// simpler solution rather than keeping a list of cache items to ignore.
|
||||||
|
if (sessionDownload) {
|
||||||
|
this._cachedPlacesMetaData.delete(sessionDownload.source.url);
|
||||||
|
}
|
||||||
|
|
||||||
let newOrUpdatedShell = null;
|
let newOrUpdatedShell = null;
|
||||||
|
|
||||||
// Trivial: if there are no shells for this download URI, we always
|
// Trivial: if there are no shells for this download URI, we always
|
||||||
|
|
@ -685,7 +683,8 @@ DownloadsPlacesView.prototype = {
|
||||||
// because it will not be obscured by the session download.
|
// because it will not be obscured by the session download.
|
||||||
let historyDownload = null;
|
let historyDownload = null;
|
||||||
if (aPlacesNode) {
|
if (aPlacesNode) {
|
||||||
let metaData = this._getCachedPlacesMetaDataFor(aPlacesNode.uri);
|
let metaData = this._cachedPlacesMetaData.get(aPlacesNode.uri) ||
|
||||||
|
this._getPlacesMetaDataFor(aPlacesNode.uri);
|
||||||
historyDownload = new HistoryDownload(aPlacesNode);
|
historyDownload = new HistoryDownload(aPlacesNode);
|
||||||
historyDownload.updateFromMetaData(metaData);
|
historyDownload.updateFromMetaData(metaData);
|
||||||
}
|
}
|
||||||
|
|
@ -955,12 +954,6 @@ DownloadsPlacesView.prototype = {
|
||||||
if (!aContainer.containerOpen)
|
if (!aContainer.containerOpen)
|
||||||
throw new Error("Root container for the downloads query cannot be closed");
|
throw new Error("Root container for the downloads query cannot be closed");
|
||||||
|
|
||||||
// When the container is invalidated, it means we are about to re-read all
|
|
||||||
// the information about history downloads from the database, discarding the
|
|
||||||
// download element shells, thus we fully rebuild the Places annotation
|
|
||||||
// cache with the latest values.
|
|
||||||
this._cachedPlacesMetaData = null;
|
|
||||||
|
|
||||||
let suppressOnSelect = this._richlistbox.suppressOnSelect;
|
let suppressOnSelect = this._richlistbox.suppressOnSelect;
|
||||||
this._richlistbox.suppressOnSelect = true;
|
this._richlistbox.suppressOnSelect = true;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue