mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Bug 1231445 - Part 3: Support for removing IndexedDB records in storage inspector r=mratcliffe
This commit is contained in:
parent
23366bd7b8
commit
c8316a5361
@ -845,16 +845,20 @@ StorageUI.prototype = {
|
||||
|
||||
/**
|
||||
* Fires before a cell context menu with the "Delete" action is shown.
|
||||
* If the current storage actor doesn't support removing items, prevent
|
||||
* If the currently selected storage object doesn't support removing items, prevent
|
||||
* showing the menu.
|
||||
*/
|
||||
onTablePopupShowing: function (event) {
|
||||
if (!this.getCurrentActor().removeItem) {
|
||||
let selectedItem = this.tree.selectedItem;
|
||||
let type = selectedItem[0];
|
||||
let actor = this.getCurrentActor();
|
||||
|
||||
// IndexedDB only supports removing items from object stores (level 4 of the tree)
|
||||
if (!actor.removeItem || (type === "indexedDB" && selectedItem.length !== 4)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
let [type] = this.tree.selectedItem;
|
||||
let rowId = this.table.contextMenuRowId;
|
||||
let data = this.table.items.get(rowId);
|
||||
let name = addEllipsis(data[this.table.uniqueId]);
|
||||
@ -878,13 +882,20 @@ StorageUI.prototype = {
|
||||
let selectedItem = this.tree.selectedItem;
|
||||
|
||||
if (selectedItem) {
|
||||
// this.currentActor() would return wrong value here
|
||||
let actor = this.storageTypes[selectedItem[0]];
|
||||
let type = selectedItem[0];
|
||||
let actor = this.storageTypes[type];
|
||||
|
||||
// The delete all (aka clear) action is displayed for IndexedDB object stores
|
||||
// (level 4 of tree) and for the whole host (level 2 of tree) of other storage
|
||||
// types (cookies, localStorage, ...).
|
||||
let showDeleteAll = actor.removeAll &&
|
||||
(selectedItem.length === (type === "indexedDB" ? 4 : 2));
|
||||
|
||||
let showDeleteAll = selectedItem.length == 2 && actor.removeAll;
|
||||
this._treePopupDeleteAll.hidden = !showDeleteAll;
|
||||
|
||||
let showDeleteDb = selectedItem.length == 3 && actor.removeDatabase;
|
||||
// The action to delete database is available for IndexedDB databases, i.e.,
|
||||
// at level 3 of the tree.
|
||||
let showDeleteDb = actor.removeDatabase && selectedItem.length === 3;
|
||||
this._treePopupDeleteDatabase.hidden = !showDeleteDb;
|
||||
if (showDeleteDb) {
|
||||
let dbName = addEllipsis(selectedItem[2]);
|
||||
@ -904,12 +915,15 @@ StorageUI.prototype = {
|
||||
* Handles removing an item from the storage
|
||||
*/
|
||||
onRemoveItem: function () {
|
||||
let [, host] = this.tree.selectedItem;
|
||||
let [, host, ...path] = this.tree.selectedItem;
|
||||
let actor = this.getCurrentActor();
|
||||
let rowId = this.table.contextMenuRowId;
|
||||
let data = this.table.items.get(rowId);
|
||||
|
||||
actor.removeItem(host, data[this.table.uniqueId]);
|
||||
let name = data[this.table.uniqueId];
|
||||
if (path.length > 0) {
|
||||
name = JSON.stringify([...path, name]);
|
||||
}
|
||||
actor.removeItem(host, name);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -919,10 +933,10 @@ StorageUI.prototype = {
|
||||
// Cannot use this.currentActor() if the handler is called from the
|
||||
// tree context menu: it returns correct value only after the table
|
||||
// data from server are successfully fetched (and that's async).
|
||||
let [type, host] = this.tree.selectedItem;
|
||||
let [type, host, ...path] = this.tree.selectedItem;
|
||||
let actor = this.storageTypes[type];
|
||||
|
||||
actor.removeAll(host);
|
||||
let name = path.length > 0 ? JSON.stringify(path) : undefined;
|
||||
actor.removeAll(host, name);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1393,6 +1393,30 @@ StorageActors.createActor({
|
||||
return this.removeDB(host, principal, name);
|
||||
}),
|
||||
|
||||
removeAll: Task.async(function* (host, name) {
|
||||
let [db, store] = JSON.parse(name);
|
||||
|
||||
let win = this.storageActor.getWindowFromHost(host);
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
let principal = win.document.nodePrincipal;
|
||||
this.clearDBStore(host, principal, db, store);
|
||||
}),
|
||||
|
||||
removeItem: Task.async(function* (host, name) {
|
||||
let [db, store, id] = JSON.parse(name);
|
||||
|
||||
let win = this.storageActor.getWindowFromHost(host);
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
let principal = win.document.nodePrincipal;
|
||||
this.removeDBRecord(host, principal, db, store, id);
|
||||
}),
|
||||
|
||||
getHostName(location) {
|
||||
if (!location.host) {
|
||||
return location.href;
|
||||
@ -1553,13 +1577,16 @@ StorageActors.createActor({
|
||||
};
|
||||
},
|
||||
|
||||
onDatabaseRemoved(host, name) {
|
||||
if (this.hostVsStores.has(host)) {
|
||||
this.hostVsStores.get(host).delete(name);
|
||||
onItemUpdated(action, host, path) {
|
||||
// Database was removed, remove it from stores map
|
||||
if (action === "deleted" && path.length === 1) {
|
||||
if (this.hostVsStores.has(host)) {
|
||||
this.hostVsStores.get(host).delete(path[0]);
|
||||
}
|
||||
}
|
||||
|
||||
this.storageActor.update("deleted", "indexedDB", {
|
||||
[host]: [ JSON.stringify([name]) ]
|
||||
this.storageActor.update(action, "indexedDB", {
|
||||
[host]: [ JSON.stringify(path) ]
|
||||
});
|
||||
},
|
||||
|
||||
@ -1574,6 +1601,8 @@ StorageActors.createActor({
|
||||
this.getValuesForHost = indexedDBHelpers.getValuesForHost;
|
||||
this.getObjectStoreData = indexedDBHelpers.getObjectStoreData;
|
||||
this.removeDB = indexedDBHelpers.removeDB;
|
||||
this.removeDBRecord = indexedDBHelpers.removeDBRecord;
|
||||
this.clearDBStore = indexedDBHelpers.clearDBStore;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1585,14 +1614,12 @@ StorageActors.createActor({
|
||||
setupParent: "setupParentProcessForIndexedDB"
|
||||
});
|
||||
|
||||
this.getDBMetaData =
|
||||
callParentProcessAsync.bind(null, "getDBMetaData");
|
||||
this.getDBNamesForHost =
|
||||
callParentProcessAsync.bind(null, "getDBNamesForHost");
|
||||
this.getValuesForHost =
|
||||
callParentProcessAsync.bind(null, "getValuesForHost");
|
||||
this.removeDB =
|
||||
callParentProcessAsync.bind(null, "removeDB");
|
||||
this.getDBMetaData = callParentProcessAsync.bind(null, "getDBMetaData");
|
||||
this.getDBNamesForHost = callParentProcessAsync.bind(null, "getDBNamesForHost");
|
||||
this.getValuesForHost = callParentProcessAsync.bind(null, "getValuesForHost");
|
||||
this.removeDB = callParentProcessAsync.bind(null, "removeDB");
|
||||
this.removeDBRecord = callParentProcessAsync.bind(null, "removeDBRecord");
|
||||
this.clearDBStore = callParentProcessAsync.bind(null, "clearDBStore");
|
||||
|
||||
addMessageListener("storage:storage-indexedDB-request-child", msg => {
|
||||
switch (msg.json.method) {
|
||||
@ -1605,9 +1632,9 @@ StorageActors.createActor({
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "onDatabaseRemoved": {
|
||||
let [host, name] = msg.json.args;
|
||||
this.onDatabaseRemoved(host, name);
|
||||
case "onItemUpdated": {
|
||||
let [action, host, path] = msg.json.args;
|
||||
this.onItemUpdated(action, host, path);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1639,13 +1666,13 @@ var indexedDBHelpers = {
|
||||
});
|
||||
},
|
||||
|
||||
onDatabaseRemoved: function (host, name) {
|
||||
onItemUpdated(action, host, path) {
|
||||
let mm = Cc["@mozilla.org/globalmessagemanager;1"]
|
||||
.getService(Ci.nsIMessageListenerManager);
|
||||
|
||||
mm.broadcastAsyncMessage("storage:storage-indexedDB-request-child", {
|
||||
method: "onDatabaseRemoved",
|
||||
args: [ host, name ]
|
||||
method: "onItemUpdated",
|
||||
args: [ action, host, path ]
|
||||
});
|
||||
},
|
||||
|
||||
@ -1666,9 +1693,9 @@ var indexedDBHelpers = {
|
||||
|
||||
success.resolve(this.backToChild("getDBMetaData", dbData));
|
||||
};
|
||||
request.onerror = () => {
|
||||
request.onerror = ({target}) => {
|
||||
console.error(
|
||||
`Error opening indexeddb database ${name} for host ${host}`);
|
||||
`Error opening indexeddb database ${name} for host ${host}`, target.error);
|
||||
success.resolve(this.backToChild("getDBMetaData", null));
|
||||
};
|
||||
return success.promise;
|
||||
@ -1683,24 +1710,24 @@ var indexedDBHelpers = {
|
||||
},
|
||||
|
||||
removeDB: Task.async(function* (host, principal, name) {
|
||||
let request = require("indexedDB").deleteForPrincipal(principal, name);
|
||||
|
||||
let result = new promise(resolve => {
|
||||
let request = require("indexedDB").deleteForPrincipal(principal, name);
|
||||
|
||||
request.onsuccess = () => {
|
||||
resolve({});
|
||||
this.onDatabaseRemoved(host, name);
|
||||
this.onItemUpdated("deleted", host, [name]);
|
||||
};
|
||||
|
||||
request.onblocked = () => {
|
||||
console.error(
|
||||
`Deleting indexedDB database ${name} for host ${host} is blocked`);
|
||||
console.warn(`Deleting indexedDB database ${name} for host ${host} is blocked`);
|
||||
resolve({ blocked: true });
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
console.error(
|
||||
`Error deleting indexedDB database ${name} for host ${host}`);
|
||||
resolve({ error: request.error });
|
||||
let { error } = request;
|
||||
console.warn(
|
||||
`Error deleting indexedDB database ${name} for host ${host}: ${error}`);
|
||||
resolve({ error: error.message });
|
||||
};
|
||||
|
||||
// If the database is blocked repeatedly, the onblocked event will not
|
||||
@ -1712,6 +1739,70 @@ var indexedDBHelpers = {
|
||||
return this.backToChild("removeDB", yield result);
|
||||
}),
|
||||
|
||||
removeDBRecord: Task.async(function* (host, principal, dbName, storeName, id) {
|
||||
let db;
|
||||
|
||||
try {
|
||||
db = yield new promise((resolve, reject) => {
|
||||
let request = this.openWithPrincipal(principal, dbName);
|
||||
request.onsuccess = ev => resolve(ev.target.result);
|
||||
request.onerror = ev => reject(ev.target.error);
|
||||
});
|
||||
|
||||
let transaction = db.transaction(storeName, "readwrite");
|
||||
let store = transaction.objectStore(storeName);
|
||||
|
||||
yield new promise((resolve, reject) => {
|
||||
let request = store.delete(id);
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = ev => reject(ev.target.error);
|
||||
});
|
||||
|
||||
this.onItemUpdated("deleted", host, [dbName, storeName, id]);
|
||||
} catch (error) {
|
||||
let recordPath = [dbName, storeName, id].join("/");
|
||||
console.error(`Failed to delete indexedDB record: ${recordPath}: ${error}`);
|
||||
}
|
||||
|
||||
if (db) {
|
||||
db.close();
|
||||
}
|
||||
|
||||
return this.backToChild("removeDBRecord", null);
|
||||
}),
|
||||
|
||||
clearDBStore: Task.async(function* (host, principal, dbName, storeName) {
|
||||
let db;
|
||||
|
||||
try {
|
||||
db = yield new promise((resolve, reject) => {
|
||||
let request = this.openWithPrincipal(principal, dbName);
|
||||
request.onsuccess = ev => resolve(ev.target.result);
|
||||
request.onerror = ev => reject(ev.target.error);
|
||||
});
|
||||
|
||||
let transaction = db.transaction(storeName, "readwrite");
|
||||
let store = transaction.objectStore(storeName);
|
||||
|
||||
yield new promise((resolve, reject) => {
|
||||
let request = store.clear();
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = ev => reject(ev.target.error);
|
||||
});
|
||||
|
||||
this.onItemUpdated("cleared", host, [dbName, storeName]);
|
||||
} catch (error) {
|
||||
let storePath = [dbName, storeName].join("/");
|
||||
console.error(`Failed to clear indexedDB store: ${storePath}: ${error}`);
|
||||
}
|
||||
|
||||
if (db) {
|
||||
db.close();
|
||||
}
|
||||
|
||||
return this.backToChild("clearDBStore", null);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Fetches all the databases and their metadata for the given `host`.
|
||||
*/
|
||||
@ -1987,6 +2078,14 @@ var indexedDBHelpers = {
|
||||
let [host, principal, name] = args;
|
||||
return indexedDBHelpers.removeDB(host, principal, name);
|
||||
}
|
||||
case "removeDBRecord": {
|
||||
let [host, principal, db, store, id] = args;
|
||||
return indexedDBHelpers.removeDBRecord(host, principal, db, store, id);
|
||||
}
|
||||
case "clearDBStore": {
|
||||
let [host, principal, db, store] = args;
|
||||
return indexedDBHelpers.clearDBStore(host, principal, db, store);
|
||||
}
|
||||
default:
|
||||
console.error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD", msg.json.method);
|
||||
throw new Error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD");
|
||||
|
@ -187,7 +187,21 @@ createStorageSpec({
|
||||
name: Arg(1, "string"),
|
||||
},
|
||||
response: RetVal("idbdeleteresult")
|
||||
}
|
||||
},
|
||||
removeAll: {
|
||||
request: {
|
||||
host: Arg(0, "string"),
|
||||
name: Arg(1, "string"),
|
||||
},
|
||||
response: {}
|
||||
},
|
||||
removeItem: {
|
||||
request: {
|
||||
host: Arg(0, "string"),
|
||||
name: Arg(1, "string"),
|
||||
},
|
||||
response: {}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user