mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 1304297 - Support for deleting Cache and Response objects in Storage Inspector r=miker
MozReview-Commit-ID: BdK4rKhmzTo --HG-- extra : rebase_source : f03fe260d8719a890a263d7674c8d880a0257f16
This commit is contained in:
parent
c29d9ac401
commit
d928014d00
@ -26,7 +26,7 @@
|
||||
<menupopup id="storage-tree-popup">
|
||||
<menuitem id="storage-tree-popup-delete-all"
|
||||
label="&storage.popupMenu.deleteAllLabel;"/>
|
||||
<menuitem id="storage-tree-popup-delete-database"/>
|
||||
<menuitem id="storage-tree-popup-delete"/>
|
||||
</menupopup>
|
||||
<menupopup id="storage-table-popup">
|
||||
<menuitem id="storage-table-popup-delete"/>
|
||||
|
@ -19,6 +19,7 @@ support-files =
|
||||
!/devtools/client/framework/test/shared-head.js
|
||||
|
||||
[browser_storage_basic.js]
|
||||
[browser_storage_cache_delete.js]
|
||||
[browser_storage_cache_error.js]
|
||||
[browser_storage_cookies_delete_all.js]
|
||||
[browser_storage_cookies_domain.js]
|
||||
|
46
devtools/client/storage/test/browser_storage_cache_delete.js
Normal file
46
devtools/client/storage/test/browser_storage_cache_delete.js
Normal file
@ -0,0 +1,46 @@
|
||||
/* 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/. */
|
||||
|
||||
/* import-globals-from ../../framework/test/shared-head.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test deleting a Cache object from the tree using context menu
|
||||
|
||||
add_task(function* () {
|
||||
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
|
||||
|
||||
let contextMenu = gPanelWindow.document.getElementById("storage-tree-popup");
|
||||
let menuDeleteItem = contextMenu.querySelector("#storage-tree-popup-delete");
|
||||
|
||||
let cacheToDelete = ["Cache", "http://test1.example.org", "plop"];
|
||||
|
||||
info("test state before delete");
|
||||
yield selectTreeItem(cacheToDelete);
|
||||
ok(gUI.tree.isSelected(cacheToDelete), "Cache item is present in the tree");
|
||||
|
||||
info("do the delete");
|
||||
let eventWait = gUI.once("store-objects-updated");
|
||||
|
||||
let selector = `[data-id='${JSON.stringify(cacheToDelete)}'] > .tree-widget-item`;
|
||||
let target = gPanelWindow.document.querySelector(selector);
|
||||
ok(target, "Cache item's tree element is present");
|
||||
|
||||
yield waitForContextMenu(contextMenu, target, () => {
|
||||
info("Opened tree context menu");
|
||||
menuDeleteItem.click();
|
||||
|
||||
let cacheName = cacheToDelete[2];
|
||||
ok(menuDeleteItem.getAttribute("label").includes(cacheName),
|
||||
`Context menu item label contains '${cacheName}')`);
|
||||
});
|
||||
|
||||
yield eventWait;
|
||||
|
||||
info("test state after delete");
|
||||
yield selectTreeItem(cacheToDelete);
|
||||
ok(!gUI.tree.isSelected(cacheToDelete), "Cache item is no longer present in the tree");
|
||||
|
||||
yield finishTests();
|
||||
});
|
@ -16,7 +16,9 @@ const TEST_CASES = [
|
||||
[["cookies", "test1.example.org"],
|
||||
"c1", "name"],
|
||||
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
|
||||
1, "name"]
|
||||
1, "name"],
|
||||
[["Cache", "http://test1.example.org", "plop"],
|
||||
MAIN_DOMAIN + "404_cached_file.js", "url"],
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
@ -39,8 +41,9 @@ add_task(function* () {
|
||||
yield waitForContextMenu(contextMenu, row[cellToClick], () => {
|
||||
info(`Opened context menu in ${treeItemName}, row '${rowName}'`);
|
||||
menuDeleteItem.click();
|
||||
ok(menuDeleteItem.getAttribute("label").includes(rowName),
|
||||
`Context menu item label contains '${rowName}'`);
|
||||
let truncatedRowName = String(rowName).substr(0, 16);
|
||||
ok(menuDeleteItem.getAttribute("label").includes(truncatedRowName),
|
||||
`Context menu item label contains '${rowName}' (maybe truncated)`);
|
||||
});
|
||||
|
||||
yield eventWait;
|
||||
|
@ -31,25 +31,29 @@ add_task(function* () {
|
||||
["iframe-s-ss1"]],
|
||||
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
|
||||
[1, 2, 3]],
|
||||
[["Cache", "http://test1.example.org", "plop"],
|
||||
[MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]],
|
||||
];
|
||||
|
||||
yield checkState(beforeState);
|
||||
|
||||
info("do the delete");
|
||||
const deleteHosts = [
|
||||
[["localStorage", "https://sectest1.example.org"], "iframe-s-ls1"],
|
||||
[["sessionStorage", "https://sectest1.example.org"], "iframe-s-ss1"],
|
||||
[["indexedDB", "http://test1.example.org", "idb1", "obj1"], 1],
|
||||
[["localStorage", "https://sectest1.example.org"], "iframe-s-ls1", "name"],
|
||||
[["sessionStorage", "https://sectest1.example.org"], "iframe-s-ss1", "name"],
|
||||
[["indexedDB", "http://test1.example.org", "idb1", "obj1"], 1, "name"],
|
||||
[["Cache", "http://test1.example.org", "plop"],
|
||||
MAIN_DOMAIN + "404_cached_file.js", "url"],
|
||||
];
|
||||
|
||||
for (let [store, rowName] of deleteHosts) {
|
||||
for (let [store, rowName, cellToClick] of deleteHosts) {
|
||||
let storeName = store.join(" > ");
|
||||
|
||||
yield selectTreeItem(store);
|
||||
|
||||
let eventWait = gUI.once("store-objects-cleared");
|
||||
|
||||
let cell = getRowCells(rowName).name;
|
||||
let cell = getRowCells(rowName)[cellToClick];
|
||||
yield waitForContextMenu(contextMenu, cell, () => {
|
||||
info(`Opened context menu in ${storeName}, row '${rowName}'`);
|
||||
menuDeleteAllItem.click();
|
||||
@ -76,6 +80,8 @@ add_task(function* () {
|
||||
[]],
|
||||
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
|
||||
[]],
|
||||
[["Cache", "http://test1.example.org", "plop"],
|
||||
[]],
|
||||
];
|
||||
|
||||
yield checkState(afterState);
|
||||
|
@ -21,6 +21,8 @@ add_task(function* () {
|
||||
[["localStorage", "http://test1.example.org"], ["ls1", "ls2"]],
|
||||
[["sessionStorage", "http://test1.example.org"], ["ss1"]],
|
||||
[["indexedDB", "http://test1.example.org", "idb1", "obj1"], [1, 2, 3]],
|
||||
[["Cache", "http://test1.example.org", "plop"],
|
||||
[MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]],
|
||||
]);
|
||||
|
||||
info("do the delete");
|
||||
@ -29,6 +31,7 @@ add_task(function* () {
|
||||
["localStorage", "http://test1.example.org"],
|
||||
["sessionStorage", "http://test1.example.org"],
|
||||
["indexedDB", "http://test1.example.org", "idb1", "obj1"],
|
||||
["Cache", "http://test1.example.org", "plop"],
|
||||
];
|
||||
|
||||
for (let store of deleteHosts) {
|
||||
@ -57,6 +60,7 @@ add_task(function* () {
|
||||
[["localStorage", "http://test1.example.org"], []],
|
||||
[["sessionStorage", "http://test1.example.org"], []],
|
||||
[["indexedDB", "http://test1.example.org", "idb1", "obj1"], []],
|
||||
[["Cache", "http://test1.example.org", "plop"], []],
|
||||
]);
|
||||
|
||||
yield finishTests();
|
||||
|
@ -12,8 +12,7 @@ add_task(function* () {
|
||||
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-empty-objectstores.html");
|
||||
|
||||
let contextMenu = gPanelWindow.document.getElementById("storage-tree-popup");
|
||||
let menuDeleteDb = contextMenu.querySelector(
|
||||
"#storage-tree-popup-delete-database");
|
||||
let menuDeleteDb = contextMenu.querySelector("#storage-tree-popup-delete");
|
||||
|
||||
info("test state before delete");
|
||||
yield checkState([
|
||||
|
@ -67,6 +67,13 @@ const ITEM_NAME_MAX_LENGTH = 32;
|
||||
|
||||
function addEllipsis(name) {
|
||||
if (name.length > ITEM_NAME_MAX_LENGTH) {
|
||||
if (/^https?:/.test(name)) {
|
||||
// For URLs, add ellipsis in the middle
|
||||
const halfLen = ITEM_NAME_MAX_LENGTH / 2;
|
||||
return name.slice(0, halfLen) + ELLIPSIS + name.slice(-halfLen);
|
||||
}
|
||||
|
||||
// For other strings, add ellipsis at the end
|
||||
return name.substr(0, ITEM_NAME_MAX_LENGTH) + ELLIPSIS;
|
||||
}
|
||||
|
||||
@ -157,7 +164,7 @@ function StorageUI(front, target, panelWin, toolbox) {
|
||||
this.onRemoveItem = this.onRemoveItem.bind(this);
|
||||
this.onRemoveAllFrom = this.onRemoveAllFrom.bind(this);
|
||||
this.onRemoveAll = this.onRemoveAll.bind(this);
|
||||
this.onRemoveDatabase = this.onRemoveDatabase.bind(this);
|
||||
this.onRemoveTreeItem = this.onRemoveTreeItem.bind(this);
|
||||
|
||||
this._tablePopupDelete = this._panelDoc.getElementById(
|
||||
"storage-table-popup-delete");
|
||||
@ -176,10 +183,8 @@ function StorageUI(front, target, panelWin, toolbox) {
|
||||
"storage-tree-popup-delete-all");
|
||||
this._treePopupDeleteAll.addEventListener("command", this.onRemoveAll);
|
||||
|
||||
this._treePopupDeleteDatabase = this._panelDoc.getElementById(
|
||||
"storage-tree-popup-delete-database");
|
||||
this._treePopupDeleteDatabase.addEventListener("command",
|
||||
this.onRemoveDatabase);
|
||||
this._treePopupDelete = this._panelDoc.getElementById("storage-tree-popup-delete");
|
||||
this._treePopupDelete.addEventListener("command", this.onRemoveTreeItem);
|
||||
}
|
||||
|
||||
exports.StorageUI = StorageUI;
|
||||
@ -205,21 +210,14 @@ StorageUI.prototype = {
|
||||
this.searchBox.removeEventListener("input", this.filterItems);
|
||||
this.searchBox = null;
|
||||
|
||||
this._treePopup.removeEventListener("popupshowing",
|
||||
this.onTreePopupShowing);
|
||||
this._treePopupDeleteAll.removeEventListener("command",
|
||||
this.onRemoveAll);
|
||||
this._treePopupDeleteDatabase.removeEventListener("command",
|
||||
this.onRemoveDatabase);
|
||||
this._treePopup.removeEventListener("popupshowing", this.onTreePopupShowing);
|
||||
this._treePopupDeleteAll.removeEventListener("command", this.onRemoveAll);
|
||||
this._treePopupDelete.removeEventListener("command", this.onRemoveTreeItem);
|
||||
|
||||
this._tablePopup.removeEventListener("popupshowing",
|
||||
this.onTablePopupShowing);
|
||||
this._tablePopupDelete.removeEventListener("command",
|
||||
this.onRemoveItem);
|
||||
this._tablePopupDeleteAllFrom.removeEventListener("command",
|
||||
this.onRemoveAllFrom);
|
||||
this._tablePopupDeleteAll.removeEventListener("command",
|
||||
this.onRemoveAll);
|
||||
this._tablePopup.removeEventListener("popupshowing", this.onTablePopupShowing);
|
||||
this._tablePopupDelete.removeEventListener("command", this.onRemoveItem);
|
||||
this._tablePopupDeleteAllFrom.removeEventListener("command", this.onRemoveAllFrom);
|
||||
this._tablePopupDeleteAll.removeEventListener("command", this.onRemoveAll);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -944,24 +942,39 @@ StorageUI.prototype = {
|
||||
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));
|
||||
// (level 4 of tree), for Cache objects (level 3) and for the whole host (level 2)
|
||||
// for other storage types (cookies, localStorage, ...).
|
||||
let showDeleteAll = false;
|
||||
if (actor.removeAll) {
|
||||
let level;
|
||||
if (type == "indexedDB") {
|
||||
level = 4;
|
||||
} else if (type == "Cache") {
|
||||
level = 3;
|
||||
} else {
|
||||
level = 2;
|
||||
}
|
||||
|
||||
if (selectedItem.length == level) {
|
||||
showDeleteAll = true;
|
||||
}
|
||||
}
|
||||
|
||||
this._treePopupDeleteAll.hidden = !showDeleteAll;
|
||||
|
||||
// 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]);
|
||||
this._treePopupDeleteDatabase.setAttribute("label",
|
||||
L10N.getFormatStr("storage.popupMenu.deleteLabel", dbName));
|
||||
// The delete action is displayed for:
|
||||
// - IndexedDB databases (level 3 of the tree)
|
||||
// - Cache objects (level 3 of the tree)
|
||||
let showDelete = (type == "indexedDB" || type == "Cache") &&
|
||||
selectedItem.length == 3;
|
||||
this._treePopupDelete.hidden = !showDelete;
|
||||
if (showDelete) {
|
||||
let itemName = addEllipsis(selectedItem[selectedItem.length - 1]);
|
||||
this._treePopupDelete.setAttribute("label",
|
||||
L10N.getFormatStr("storage.popupMenu.deleteLabel", itemName));
|
||||
}
|
||||
|
||||
showMenu = showDeleteAll || showDeleteDb;
|
||||
showMenu = showDeleteAll || showDelete;
|
||||
}
|
||||
|
||||
if (!showMenu) {
|
||||
@ -1010,15 +1023,24 @@ StorageUI.prototype = {
|
||||
actor.removeAll(host, data.host);
|
||||
},
|
||||
|
||||
onRemoveDatabase: function () {
|
||||
let [type, host, name] = this.tree.selectedItem;
|
||||
let actor = this.storageTypes[type];
|
||||
onRemoveTreeItem: function () {
|
||||
let [type, host, ...path] = this.tree.selectedItem;
|
||||
|
||||
actor.removeDatabase(host, name).then(result => {
|
||||
if (type == "indexedDB" && path.length == 1) {
|
||||
this.removeDatabase(host, path[0]);
|
||||
} else if (type == "Cache" && path.length == 1) {
|
||||
this.removeCache(host, path[0]);
|
||||
}
|
||||
},
|
||||
|
||||
removeDatabase: function (host, dbName) {
|
||||
let actor = this.storageTypes.indexedDB;
|
||||
|
||||
actor.removeDatabase(host, dbName).then(result => {
|
||||
if (result.blocked) {
|
||||
let notificationBox = this._toolbox.getNotificationBox();
|
||||
notificationBox.appendNotification(
|
||||
L10N.getFormatStr("storage.idb.deleteBlocked", name),
|
||||
L10N.getFormatStr("storage.idb.deleteBlocked", dbName),
|
||||
"storage-idb-delete-blocked",
|
||||
null,
|
||||
notificationBox.PRIORITY_WARNING_LOW);
|
||||
@ -1026,10 +1048,16 @@ StorageUI.prototype = {
|
||||
}).catch(error => {
|
||||
let notificationBox = this._toolbox.getNotificationBox();
|
||||
notificationBox.appendNotification(
|
||||
L10N.getFormatStr("storage.idb.deleteError", name),
|
||||
L10N.getFormatStr("storage.idb.deleteError", dbName),
|
||||
"storage-idb-delete-error",
|
||||
null,
|
||||
notificationBox.PRIORITY_CRITICAL_LOW);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
removeCache: function (host, cacheName) {
|
||||
let actor = this.storageTypes.Cache;
|
||||
|
||||
actor.removeItem(host, JSON.stringify([ cacheName ]));
|
||||
},
|
||||
};
|
||||
|
@ -1250,6 +1250,61 @@ StorageActors.createActor({
|
||||
toStoreObject(item) {
|
||||
return item;
|
||||
},
|
||||
|
||||
removeItem: Task.async(function* (host, name) {
|
||||
const cacheMap = this.hostVsStores.get(host);
|
||||
if (!cacheMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedName = JSON.parse(name);
|
||||
|
||||
if (parsedName.length == 1) {
|
||||
// Delete the whole Cache object
|
||||
const [ cacheName ] = parsedName;
|
||||
cacheMap.delete(cacheName);
|
||||
const cacheStorage = yield this.getCachesForHost(host);
|
||||
yield cacheStorage.delete(cacheName);
|
||||
this.onItemUpdated("deleted", host, [ cacheName ]);
|
||||
} else if (parsedName.length == 2) {
|
||||
// Delete one cached request
|
||||
const [ cacheName, url ] = parsedName;
|
||||
const cache = cacheMap.get(cacheName);
|
||||
if (cache) {
|
||||
yield cache.delete(url);
|
||||
this.onItemUpdated("deleted", host, [ cacheName, url ]);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
removeAll: Task.async(function* (host, name) {
|
||||
const cacheMap = this.hostVsStores.get(host);
|
||||
if (!cacheMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedName = JSON.parse(name);
|
||||
|
||||
// Only a Cache object is a valid object to clear
|
||||
if (parsedName.length == 1) {
|
||||
const [ cacheName ] = parsedName;
|
||||
const cache = cacheMap.get(cacheName);
|
||||
if (cache) {
|
||||
let keys = yield cache.keys();
|
||||
yield promise.all(keys.map(key => cache.delete(key)));
|
||||
this.onItemUpdated("cleared", host, [ cacheName ]);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* CacheStorage API doesn't support any notifications, we must fake them
|
||||
*/
|
||||
onItemUpdated(action, host, path) {
|
||||
this.storageActor.update(action, "Cache", {
|
||||
[host]: [ JSON.stringify(path) ]
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -153,7 +153,23 @@ types.addDictType("cachestoreobject", {
|
||||
// Cache storage spec
|
||||
createStorageSpec({
|
||||
typeName: "Cache",
|
||||
storeObjectType: "cachestoreobject"
|
||||
storeObjectType: "cachestoreobject",
|
||||
methods: {
|
||||
removeAll: {
|
||||
request: {
|
||||
host: Arg(0, "string"),
|
||||
name: Arg(1, "string"),
|
||||
},
|
||||
response: {}
|
||||
},
|
||||
removeItem: {
|
||||
request: {
|
||||
host: Arg(0, "string"),
|
||||
name: Arg(1, "string"),
|
||||
},
|
||||
response: {}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
// Indexed DB store object
|
||||
|
Loading…
Reference in New Issue
Block a user