Bug 1231434 - Add 'Delete All' context menu entry to storage inspector. r=mratcliffe

This commit is contained in:
Jarda Snajdr 2016-04-11 01:21:00 +02:00
Родитель d8f23fae58
Коммит 4840f48bea
13 изменённых файлов: 452 добавлений и 63 удалений

Просмотреть файл

@ -6,3 +6,6 @@
<!-- LOCALIZATION NOTE : Placeholder for the searchbox that allows you to filter the table items. -->
<!ENTITY searchBox.placeholder "Filter items">
<!-- LOCALIZATION NOTE : Label of popup menu action to delete all storage items. -->
<!ENTITY storage.popupMenu.deleteAllLabel "Delete All">

Просмотреть файл

@ -119,3 +119,7 @@ storage.parsedValue.label=Parsed Value
# LOCALIZATION NOTE (storage.popupMenu.deleteLabel):
# Label of popup menu action to delete storage item.
storage.popupMenu.deleteLabel=Delete “%S”
# LOCALIZATION NOTE (storage.popupMenu.deleteAllLabel):
# Label of popup menu action to delete all storage items.
storage.popupMenu.deleteAllFromLabel=Delete All From “%S”

Просмотреть файл

@ -803,6 +803,9 @@ TableWidget.prototype = {
if (typeof item == "string") {
item = this.items.get(item);
}
if (!item) {
return;
}
let removed = this.items.delete(item[this.uniqueId]);
if (!removed) {

Просмотреть файл

@ -17,9 +17,11 @@ const EventEmitter = require("devtools/shared/event-emitter");
* @param {Object} options
* - emptyText {string}: text to display when no entries in the table.
* - defaultType {string}: The default type of the tree items. For ex.
* 'js'
* 'js'
* - sorted {boolean}: Defaults to true. If true, tree items are kept in
* lexical order. If false, items will be kept in insertion order.
* lexical order. If false, items will be kept in insertion order.
* - contextMenuId {string}: ID of context menu to be displayed on
* tree items.
*/
function TreeWidget(node, options = {}) {
EventEmitter.decorate(this);
@ -31,6 +33,7 @@ function TreeWidget(node, options = {}) {
this.emptyText = options.emptyText || "";
this.defaultType = options.defaultType;
this.sorted = options.sorted !== false;
this.contextMenuId = options.contextMenuId;
this.setupRoot();
@ -53,30 +56,31 @@ TreeWidget.prototype = {
/**
* Select any node in the tree.
*
* @param {array} id
* @param {array} ids
* An array of ids leading upto the selected item
*/
set selectedItem(id) {
set selectedItem(ids) {
if (this._selectedLabel) {
this._selectedLabel.classList.remove("theme-selected");
}
let currentSelected = this._selectedLabel;
if (id == -1) {
if (ids == -1) {
this._selectedLabel = this._selectedItem = null;
return;
}
if (!Array.isArray(id)) {
if (!Array.isArray(ids)) {
return;
}
this._selectedLabel = this.root.setSelectedItem(id);
this._selectedLabel = this.root.setSelectedItem(ids);
if (!this._selectedLabel) {
this._selectedItem = null;
} else {
if (currentSelected != this._selectedLabel) {
this.ensureSelectedVisible();
}
this._selectedItem =
JSON.parse(this._selectedLabel.parentNode.getAttribute("data-id"));
this._selectedItem = ids;
this.emit("select", this._selectedItem,
this.attachments.get(JSON.stringify(ids)));
}
},
@ -120,9 +124,16 @@ TreeWidget.prototype = {
*/
setupRoot: function() {
this.root = new TreeItem(this.document);
if (this.contextMenuId) {
this.root.children.addEventListener("contextmenu", (event) => {
let menu = this.document.getElementById(this.contextMenuId);
menu.openPopupAtScreen(event.screenX, event.screenY, true);
});
}
this._parent.appendChild(this.root.children);
this.root.children.addEventListener("click", e => this.onClick(e));
this.root.children.addEventListener("mousedown", e => this.onClick(e));
this.root.children.addEventListener("keypress", e => this.onKeypress(e));
},
@ -315,21 +326,17 @@ TreeWidget.prototype = {
if (!target) {
return;
}
if (target.hasAttribute("expanded")) {
target.removeAttribute("expanded");
} else {
target.setAttribute("expanded", "true");
}
if (this._selectedLabel) {
this._selectedLabel.classList.remove("theme-selected");
}
if (this._selectedLabel != target) {
let ids = target.parentNode.getAttribute("data-id");
this._selectedItem = JSON.parse(ids);
this.emit("select", this._selectedItem, this.attachments.get(ids));
this._selectedLabel = target;
this.selectedItem = JSON.parse(ids);
}
target.classList.add("theme-selected");
},
/**
@ -337,7 +344,6 @@ TreeWidget.prototype = {
* items, as well as collapsing and expanding any item.
*/
onKeypress: function(event) {
let currentSelected = this._selectedLabel;
switch (event.keyCode) {
case event.DOM_VK_UP:
this.selectPreviousItem();
@ -367,11 +373,6 @@ TreeWidget.prototype = {
default: return;
}
event.preventDefault();
if (this._selectedLabel != currentSelected) {
let ids = JSON.stringify(this._selectedItem);
this.emit("select", this._selectedItem, this.attachments.get(ids));
this.ensureSelectedVisible();
}
},
/**

Просмотреть файл

@ -23,8 +23,15 @@
<commandset id="editMenuCommands"/>
<popupset id="storagePopupSet">
<menupopup id="storage-tree-popup">
<menuitem id="storage-tree-popup-delete-all"
label="&storage.popupMenu.deleteAllLabel;"/>
</menupopup>
<menupopup id="storage-table-popup">
<menuitem id="storage-table-popup-delete"/>
<menuitem id="storage-table-popup-delete-all-from"/>
<menuitem id="storage-table-popup-delete-all"
label="&storage.popupMenu.deleteAllLabel;"/>
</menupopup>
</popupset>

Просмотреть файл

@ -15,12 +15,15 @@ support-files =
head.js
[browser_storage_basic.js]
[browser_storage_cookies_delete_all.js]
[browser_storage_cookies_edit.js]
[browser_storage_cookies_edit_keyboard.js]
[browser_storage_cookies_tab_navigation.js]
[browser_storage_dynamic_updates.js]
[browser_storage_localstorage_edit.js]
[browser_storage_delete.js]
[browser_storage_delete_all.js]
[browser_storage_delete_tree.js]
[browser_storage_overflow.js]
[browser_storage_search.js]
skip-if = os == "linux" && e10s # Bug 1240804 - unhandled promise rejections

Просмотреть файл

@ -0,0 +1,74 @@
/* 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 head.js */
"use strict";
// Test deleting all cookies
function* performDelete(store, rowName, deleteAll) {
let contextMenu = gPanelWindow.document.getElementById(
"storage-table-popup");
let menuDeleteAllItem = contextMenu.querySelector(
"#storage-table-popup-delete-all");
let menuDeleteAllFromItem = contextMenu.querySelector(
"#storage-table-popup-delete-all-from");
let storeName = store.join(" > ");
yield selectTreeItem(store);
let eventWait = gUI.once("store-objects-updated");
let cells = getRowCells(rowName);
yield waitForContextMenu(contextMenu, cells.name, () => {
info(`Opened context menu in ${storeName}, row '${rowName}'`);
if (deleteAll) {
menuDeleteAllItem.click();
} else {
menuDeleteAllFromItem.click();
let hostName = cells.host.value;
ok(menuDeleteAllFromItem.getAttribute("label").includes(hostName),
`Context menu item label contains '${hostName}'`);
}
});
yield eventWait;
}
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
info("test state before delete");
yield checkState([
[["cookies", "test1.example.org"], ["c1", "c3", "cs2", "uc1"]],
[["cookies", "sectest1.example.org"], ["cs2", "sc1", "uc1"]],
]);
info("delete all from domain");
// delete only cookies that match the host exactly
yield performDelete(["cookies", "test1.example.org"], "c1", false);
info("test state after delete all from domain");
yield checkState([
// Domain cookies (.example.org) must not be deleted.
[["cookies", "test1.example.org"], ["cs2", "uc1"]],
[["cookies", "sectest1.example.org"], ["cs2", "sc1", "uc1"]],
]);
info("delete all");
// delete all cookies for host, including domain cookies
yield performDelete(["cookies", "sectest1.example.org"], "uc1", true);
info("test state after delete all");
yield checkState([
// Domain cookies (.example.org) are deleted too, so deleting in sectest1
// also removes stuff from test1.
[["cookies", "test1.example.org"], []],
[["cookies", "sectest1.example.org"], []],
]);
yield finishTests();
});

Просмотреть файл

@ -28,10 +28,11 @@ add_task(function* () {
yield selectTreeItem([store, host]);
let row = getRowCells(rowName);
ok(gUI.table.items.has(rowName),
`There is a row '${rowName}' in ${store} > ${host}`);
let eventWait = gUI.once("store-objects-updated");
yield waitForContextMenu(contextMenu, row[cellToClick], () => {
info(`Opened context menu in ${store} > ${host}, row '${rowName}'`);
menuDeleteItem.click();
@ -39,7 +40,7 @@ add_task(function* () {
`Context menu item label contains '${rowName}'`);
});
yield gUI.once("store-objects-updated");
yield eventWait;
ok(!gUI.table.items.has(rowName),
`There is no row '${rowName}' in ${store} > ${host} after deletion`);

Просмотреть файл

@ -0,0 +1,79 @@
/* 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 head.js */
"use strict";
// Test deleting all storage items
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
let contextMenu = gPanelWindow.document.getElementById("storage-table-popup");
let menuDeleteAllItem = contextMenu.querySelector(
"#storage-table-popup-delete-all");
info("test state before delete");
const beforeState = [
[["localStorage", "http://test1.example.org"],
["ls1", "ls2"]],
[["localStorage", "http://sectest1.example.org"],
["iframe-u-ls1"]],
[["localStorage", "https://sectest1.example.org"],
["iframe-s-ls1"]],
[["sessionStorage", "http://test1.example.org"],
["ss1"]],
[["sessionStorage", "http://sectest1.example.org"],
["iframe-u-ss1", "iframe-u-ss2"]],
[["sessionStorage", "https://sectest1.example.org"],
["iframe-s-ss1"]],
];
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"],
];
for (let [store, rowName] of deleteHosts) {
let storeName = store.join(" > ");
yield selectTreeItem(store);
let eventWait = gUI.once("store-objects-cleared");
let cell = getRowCells(rowName).name;
yield waitForContextMenu(contextMenu, cell, () => {
info(`Opened context menu in ${storeName}, row '${rowName}'`);
menuDeleteAllItem.click();
});
yield eventWait;
}
info("test state after delete");
const afterState = [
// iframes from the same host, one secure, one unsecure, are independent
// from each other. Delete all in one doesn't touch the other one.
[["localStorage", "http://test1.example.org"],
["ls1", "ls2"]],
[["localStorage", "http://sectest1.example.org"],
["iframe-u-ls1"]],
[["localStorage", "https://sectest1.example.org"],
[]],
[["sessionStorage", "http://test1.example.org"],
["ss1"]],
[["sessionStorage", "http://sectest1.example.org"],
["iframe-u-ss1", "iframe-u-ss2"]],
[["sessionStorage", "https://sectest1.example.org"],
[]],
];
yield checkState(afterState);
yield finishTests();
});

Просмотреть файл

@ -0,0 +1,60 @@
/* 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 head.js */
"use strict";
// Test deleting all storage items from the tree.
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
let contextMenu = gPanelWindow.document.getElementById("storage-tree-popup");
let menuDeleteAllItem = contextMenu.querySelector(
"#storage-tree-popup-delete-all");
info("test state before delete");
yield checkState([
[["cookies", "test1.example.org"], ["c1", "c3", "cs2", "uc1"]],
[["localStorage", "http://test1.example.org"], ["ls1", "ls2"]],
[["sessionStorage", "http://test1.example.org"], ["ss1"]],
]);
info("do the delete");
const deleteHosts = [
["cookies", "test1.example.org"],
["localStorage", "http://test1.example.org"],
["sessionStorage", "http://test1.example.org"],
];
for (let store of deleteHosts) {
let storeName = store.join(" > ");
yield selectTreeItem(store);
let eventName = "store-objects-" +
(store[0] == "cookies" ? "updated" : "cleared");
let eventWait = gUI.once(eventName);
let selector = `[data-id='${JSON.stringify(store)}'] > .tree-widget-item`;
let target = gPanelWindow.document.querySelector(selector);
ok(target, `tree item found in ${storeName}`);
yield waitForContextMenu(contextMenu, target, () => {
info(`Opened tree context menu in ${storeName}`);
menuDeleteAllItem.click();
});
yield eventWait;
}
info("test state after delete");
yield checkState([
[["cookies", "test1.example.org"], []],
[["localStorage", "http://test1.example.org"], []],
[["sessionStorage", "http://test1.example.org"], []],
]);
yield finishTests();
});

Просмотреть файл

@ -509,17 +509,13 @@ function matchVariablesViewProperty(prop, rule) {
* The array id of the item in the tree
*/
function* selectTreeItem(ids) {
// Expand tree as some/all items could be collapsed leading to click on an
// incorrect tree item
gUI.tree.expandAll();
let selector = "[data-id='" + JSON.stringify(ids) + "'] > .tree-widget-item";
let target = gPanelWindow.document.querySelector(selector);
ok(target, "tree item found with ids " + JSON.stringify(ids));
/* If this item is already selected, return */
if (gUI.tree.isSelected(ids)) {
return;
}
let updated = gUI.once("store-objects-updated");
yield click(target);
gUI.tree.selectedItem = ids;
yield updated;
}
@ -845,8 +841,35 @@ function waitForContextMenu(popup, button, onShown, onHidden) {
popup.addEventListener("popupshown", onPopupShown);
info("wait for the context menu to open");
button.scrollIntoView();
let eventDetails = {type: "contextmenu", button: 2};
EventUtils.synthesizeMouse(button, 2, 2, eventDetails,
button.ownerDocument.defaultView);
return deferred.promise;
}
/**
* Verify the storage inspector state: check that given type/host exists
* in the tree, and that the table contains rows with specified names.
*
* @param {Array} state Array of state specifications. For example,
* [["cookies", "example.com"], ["c1", "c2"]] means to select the
* "example.com" host in cookies and then verify there are "c1" and "c2"
* cookies (and no other ones).
*/
function* checkState(state) {
for (let [store, names] of state) {
let storeName = store.join(" > ");
info(`Selecting tree item ${storeName}`);
yield selectTreeItem(store);
let items = gUI.table.items;
is(items.size, names.length,
`There is correct number of rows in ${storeName}`);
for (let name of names) {
ok(items.has(name),
`There is item with name '${name}' in ${storeName}`);
}
}
}

Просмотреть файл

@ -70,7 +70,10 @@ var StorageUI = this.StorageUI = function StorageUI(front, target, panelWin) {
this.front = front;
let treeNode = this._panelDoc.getElementById("storage-tree");
this.tree = new TreeWidget(treeNode, {defaultType: "dir"});
this.tree = new TreeWidget(treeNode, {
defaultType: "dir",
contextMenuId: "storage-tree-popup"
});
this.onHostSelect = this.onHostSelect.bind(this);
this.tree.on("select", this.onHostSelect);
@ -111,14 +114,34 @@ var StorageUI = this.StorageUI = function StorageUI(front, target, panelWin) {
this.handleKeypress = this.handleKeypress.bind(this);
this._panelDoc.addEventListener("keypress", this.handleKeypress);
this.onPopupShowing = this.onPopupShowing.bind(this);
this.onTreePopupShowing = this.onTreePopupShowing.bind(this);
this._treePopup = this._panelDoc.getElementById("storage-tree-popup");
this._treePopup.addEventListener("popupshowing", this.onTreePopupShowing);
this.onTablePopupShowing = this.onTablePopupShowing.bind(this);
this._tablePopup = this._panelDoc.getElementById("storage-table-popup");
this._tablePopup.addEventListener("popupshowing", this.onPopupShowing, false);
this._tablePopup.addEventListener("popupshowing", this.onTablePopupShowing);
this.onRemoveItem = this.onRemoveItem.bind(this);
this.onRemoveAllFrom = this.onRemoveAllFrom.bind(this);
this.onRemoveAll = this.onRemoveAll.bind(this);
this._tablePopupDelete = this._panelDoc.getElementById(
"storage-table-popup-delete");
this._tablePopupDelete.addEventListener("command", this.onRemoveItem, false);
this._tablePopupDelete.addEventListener("command", this.onRemoveItem);
this._tablePopupDeleteAllFrom = this._panelDoc.getElementById(
"storage-table-popup-delete-all-from");
this._tablePopupDeleteAllFrom.addEventListener("command",
this.onRemoveAllFrom);
this._tablePopupDeleteAll = this._panelDoc.getElementById(
"storage-table-popup-delete-all");
this._tablePopupDeleteAll.addEventListener("command", this.onRemoveAll);
this._treePopupDeleteAll = this._panelDoc.getElementById(
"storage-tree-popup-delete-all");
this._treePopupDeleteAll.addEventListener("command", this.onRemoveAll);
};
exports.StorageUI = StorageUI;
@ -126,7 +149,6 @@ exports.StorageUI = StorageUI;
StorageUI.prototype = {
storageTypes: null,
shouldResetColumns: true,
shouldLoadMoreItems: true,
set animationsEnabled(value) {
@ -145,8 +167,16 @@ StorageUI.prototype = {
this.searchBox.removeEventListener("input", this.filterItems);
this.searchBox = null;
this._tablePopup.removeEventListener("popupshowing", this.onPopupShowing);
this._treePopup.removeEventListener("popupshowing",
this.onTreePopupShowing);
this._treePopupDeleteAll.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);
},
/**
@ -391,10 +421,10 @@ StorageUI.prototype = {
this.emit("store-objects-updated");
return;
}
if (this.shouldResetColumns) {
if (reason === REASON.POPULATE) {
this.resetColumns(data[0], type);
this.table.host = host;
}
this.table.host = host;
this.populateTable(data, reason);
this.emit("store-objects-updated");
@ -437,7 +467,6 @@ StorageUI.prototype = {
this.tree.add([type, host, ...names]);
if (!this.tree.selectedItem) {
this.tree.selectedItem = [type, host, names[0], names[1]];
this.fetchStorageObjects(type, host, [name], REASON.POPULATE);
}
} catch (ex) {
// Do Nothing
@ -445,7 +474,6 @@ StorageUI.prototype = {
}
if (!this.tree.selectedItem) {
this.tree.selectedItem = [type, host];
this.fetchStorageObjects(type, host, null, REASON.POPULATE);
}
}
}
@ -625,7 +653,6 @@ StorageUI.prototype = {
if (item.length > 2) {
names = [JSON.stringify(item.slice(2))];
}
this.shouldResetColumns = true;
this.fetchStorageObjects(type, host, names, REASON.POPULATE);
this.itemOffset = 0;
},
@ -657,7 +684,6 @@ StorageUI.prototype = {
}
this.table.setColumns(columns, null, HIDDEN_COLUMNS);
this.table.datatype = type;
this.shouldResetColumns = false;
this.hideSidebar();
},
@ -757,23 +783,54 @@ StorageUI.prototype = {
* If the current storage actor doesn't support removing items, prevent
* showing the menu.
*/
onPopupShowing: function(event) {
onTablePopupShowing: function(event) {
if (!this.getCurrentActor().removeItem) {
event.preventDefault();
return;
}
const maxLen = ITEM_NAME_MAX_LENGTH;
let [type] = this.tree.selectedItem;
let rowId = this.table.contextMenuRowId;
let data = this.table.items.get(rowId);
let name = data[this.table.uniqueId];
const maxLen = ITEM_NAME_MAX_LENGTH;
if (name.length > maxLen) {
name = name.substr(0, maxLen) + L10N.ellipsis;
}
this._tablePopupDelete.setAttribute("label",
L10N.getFormatStr("storage.popupMenu.deleteLabel", name));
if (type === "cookies") {
let host = data.host;
if (host.length > maxLen) {
host = host.substr(0, maxLen) + L10N.ellipsis;
}
this._tablePopupDeleteAllFrom.hidden = false;
this._tablePopupDeleteAllFrom.setAttribute("label",
L10N.getFormatStr("storage.popupMenu.deleteAllFromLabel", host));
} else {
this._tablePopupDeleteAllFrom.hidden = true;
}
},
onTreePopupShowing: function(event) {
let showMenu = false;
let selectedItem = this.tree.selectedItem;
// Never show menu on the 1st level item
if (selectedItem && selectedItem.length > 1) {
// this.currentActor() would return wrong value here
let actor = this.storageTypes[selectedItem[0]];
if (actor.removeAll) {
showMenu = true;
}
}
if (!showMenu) {
event.preventDefault();
}
},
/**
@ -787,4 +844,30 @@ StorageUI.prototype = {
actor.removeItem(host, data[this.table.uniqueId]);
},
/**
* Handles removing all items from the storage
*/
onRemoveAll: function() {
// 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 actor = this.storageTypes[type];
actor.removeAll(host);
},
/**
* Handles removing all cookies with exactly the same domain as the
* cookie in the selected row.
*/
onRemoveAllFrom: function() {
let [, host] = this.tree.selectedItem;
let actor = this.getCurrentActor();
let rowId = this.table.contextMenuRowId;
let data = this.table.items.get(rowId);
actor.removeAll(host, data.host);
},
};

Просмотреть файл

@ -686,8 +686,18 @@ StorageActors.createActor({
this.removeCookie(host, name);
}), {
request: {
host: Arg(0),
name: Arg(1),
host: Arg(0, "string"),
name: Arg(1, "string"),
},
response: {}
}),
removeAll: method(Task.async(function*(host, domain) {
this.removeAllCookies(host, domain);
}), {
request: {
host: Arg(0, "string"),
domain: Arg(1, "nullable:string")
},
response: {}
}),
@ -696,11 +706,18 @@ StorageActors.createActor({
cookieHelpers.onCookieChanged = this.onCookieChanged.bind(this);
if (!DebuggerServer.isInChildProcess) {
this.getCookiesFromHost = cookieHelpers.getCookiesFromHost;
this.addCookieObservers = cookieHelpers.addCookieObservers;
this.removeCookieObservers = cookieHelpers.removeCookieObservers;
this.editCookie = cookieHelpers.editCookie;
this.removeCookie = cookieHelpers.removeCookie;
this.getCookiesFromHost =
cookieHelpers.getCookiesFromHost.bind(cookieHelpers);
this.addCookieObservers =
cookieHelpers.addCookieObservers.bind(cookieHelpers);
this.removeCookieObservers =
cookieHelpers.removeCookieObservers.bind(cookieHelpers);
this.editCookie =
cookieHelpers.editCookie.bind(cookieHelpers);
this.removeCookie =
cookieHelpers.removeCookie.bind(cookieHelpers);
this.removeAllCookies =
cookieHelpers.removeAllCookies.bind(cookieHelpers);
return;
}
@ -722,6 +739,8 @@ StorageActors.createActor({
callParentProcess.bind(null, "editCookie");
this.removeCookie =
callParentProcess.bind(null, "removeCookie");
this.removeAllCookies =
callParentProcess.bind(null, "removeAllCookies");
addMessageListener("storage:storage-cookie-request-child",
cookieHelpers.handleParentRequest);
@ -875,7 +894,7 @@ var cookieHelpers = {
);
},
removeCookie: function(host, name) {
_removeCookies: function(host, opts = {}) {
function hostMatches(cookieHost, matchHost) {
if (cookieHost == null) {
return matchHost == null;
@ -889,7 +908,9 @@ var cookieHelpers = {
let enumerator = Services.cookies.getCookiesFromHost(host);
while (enumerator.hasMoreElements()) {
let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
if (hostMatches(cookie.host, host) && cookie.name === name) {
if (hostMatches(cookie.host, host) &&
(!opts.name || cookie.name === opts.name) &&
(!opts.domain || cookie.host === opts.domain)) {
Services.cookies.remove(
cookie.host,
cookie.name,
@ -901,6 +922,16 @@ var cookieHelpers = {
}
},
removeCookie: function(host, name) {
if (name !== undefined) {
this._removeCookies(host, { name });
}
},
removeAllCookies: function(host, domain) {
this._removeCookies(host, { domain });
},
addCookieObservers: function() {
Services.obs.addObserver(cookieHelpers, "cookie-changed", false);
return null;
@ -969,6 +1000,11 @@ var cookieHelpers = {
let name = msg.data.args[1];
return cookieHelpers.removeCookie(host, name);
}
case "removeAllCookies": {
let host = msg.data.args[0];
let domain = msg.data.args[1];
return cookieHelpers.removeAllCookies(host, domain);
}
default:
console.error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD", msg.json.method);
throw new Error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD");
@ -1180,6 +1216,16 @@ function getObjectForLocalOrSessionStorage(type) {
},
response: {}
}),
removeAll: method(Task.async(function*(host) {
let storage = this.hostVsStores.get(host);
storage.clear();
}), {
request: {
host: Arg(0)
},
response: {}
}),
};
}
@ -2078,7 +2124,7 @@ var StorageActor = exports.StorageActor = protocol.ActorClass({
data: Arg(0, "json")
},
"stores-reloaded": {
type: "storesRelaoded",
type: "storesReloaded",
data: Arg(0, "json")
}
},
@ -2310,11 +2356,13 @@ var StorageActor = exports.StorageActor = protocol.ActorClass({
this.boundUpdate[action][storeType] = {};
}
for (let host in data) {
if (!this.boundUpdate[action][storeType][host] || action == "deleted") {
this.boundUpdate[action][storeType][host] = data[host];
} else {
this.boundUpdate[action][storeType][host] =
this.boundUpdate[action][storeType][host].concat(data[host]);
if (!this.boundUpdate[action][storeType][host]) {
this.boundUpdate[action][storeType][host] = [];
}
for (let name of data[host]) {
if (!this.boundUpdate[action][storeType][host].includes(name)) {
this.boundUpdate[action][storeType][host].push(name);
}
}
}
if (action == "added") {