Bug 1146194: Multiple cookies with the same name not shown

Issue #31
This commit is contained in:
janekptacijarabaci 2018-03-02 13:36:16 +01:00 committed by Roy Tam
commit a5b4f6e003
27 changed files with 829 additions and 169 deletions

View file

@ -35,6 +35,7 @@ tree.labels.Cache=Cache Storage
# LOCALIZATION NOTE (table.headers.*.*):
# These strings are the header names of the columns in the Storage Table for
# each type of storage available through the Storage Tree to the side.
table.headers.cookies.uniqueKey=Unique key
table.headers.cookies.name=Name
table.headers.cookies.path=Path
table.headers.cookies.host=Domain

View file

@ -615,8 +615,13 @@ TableWidget.prototype = {
/**
* Populates the header context menu with the names of the columns along with
* displaying which columns are hidden or visible.
*
* @param {Array} privateColumns=[]
* An array of column names that should never appear in the table. This
* allows us to e.g. have an invisible compound primary key for a
* table's rows.
*/
populateMenuPopup: function () {
populateMenuPopup: function (privateColumns = []) {
if (!this.menupopup) {
return;
}
@ -626,6 +631,10 @@ TableWidget.prototype = {
}
for (let column of this.columns.values()) {
if (privateColumns.includes(column.id)) {
continue;
}
let menuitem = this.document.createElementNS(XUL_NS, "menuitem");
menuitem.setAttribute("label", column.header.getAttribute("value"));
menuitem.setAttribute("data-id", column.id);
@ -663,16 +672,21 @@ TableWidget.prototype = {
* Creates the columns in the table. Without calling this method, data cannot
* be inserted into the table unless `initialColumns` was supplied.
*
* @param {object} columns
* @param {Object} columns
* A key value pair representing the columns of the table. Where the
* key represents the id of the column and the value is the displayed
* label in the header of the column.
* @param {string} sortOn
* @param {String} sortOn
* The id of the column on which the table will be initially sorted on.
* @param {array} hiddenColumns
* @param {Array} hiddenColumns
* Ids of all the columns that are hidden by default.
* @param {Array} privateColumns=[]
* An array of column names that should never appear in the table. This
* allows us to e.g. have an invisible compound primary key for a
* table's rows.
*/
setColumns: function (columns, sortOn = this.sortedOn, hiddenColumns = []) {
setColumns: function (columns, sortOn = this.sortedOn, hiddenColumns = [],
privateColumns = []) {
for (let column of this.columns.values()) {
column.destroy();
}
@ -702,13 +716,14 @@ TableWidget.prototype = {
}
this.columns.set(id, new Column(this, id, columns[id]));
if (hiddenColumns.indexOf(id) > -1) {
if (hiddenColumns.includes(id) || privateColumns.includes(id)) {
// Hide the column.
this.columns.get(id).toggleColumn();
}
}
this.sortedOn = sortOn;
this.sortBy(this.sortedOn);
this.populateMenuPopup();
this.populateMenuPopup(privateColumns);
},
/**
@ -778,6 +793,11 @@ TableWidget.prototype = {
return;
}
if (this.editBookmark && !this.items.has(this.editBookmark)) {
// Key has been updated... update bookmark.
this.editBookmark = item[this.uniqueId];
}
let index = this.columns.get(this.sortedOn).push(item);
for (let [key, column] of this.columns) {
if (key != this.sortedOn) {

View file

@ -29,7 +29,9 @@ support-files =
[browser_storage_delete.js]
[browser_storage_delete_all.js]
[browser_storage_delete_tree.js]
[browser_storage_dynamic_updates.js]
[browser_storage_dynamic_updates_cookies.js]
[browser_storage_dynamic_updates_localStorage.js]
[browser_storage_dynamic_updates_sessionStorage.js]
[browser_storage_empty_objectstores.js]
[browser_storage_indexeddb_delete.js]
[browser_storage_indexeddb_delete_blocked.js]

View file

@ -2,6 +2,8 @@
* 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 */
// Basic test to assert that the storage tree and table corresponding to each
// item in the storage tree is correctly displayed
@ -21,10 +23,23 @@
"use strict";
const testCases = [
[["cookies", "test1.example.org"],
["c1", "cs2", "c3", "uc1"]],
[["cookies", "sectest1.example.org"],
["uc1", "cs2", "sc1"]],
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("cs2", ".example.org", "/"),
getCookieId("c3", "test1.example.org", "/"),
getCookieId("uc1", ".example.org", "/")
]
],
[
["cookies", "sectest1.example.org"],
[
getCookieId("uc1", ".example.org", "/"),
getCookieId("cs2", ".example.org", "/"),
getCookieId("sc1", "sectest1.example.org", "/browser/devtools/client/storage/test/")
]
],
[["localStorage", "http://test1.example.org"],
["ls1", "ls2"]],
[["localStorage", "http://sectest1.example.org"],

View file

@ -21,8 +21,8 @@ function* performDelete(store, rowName, deleteAll) {
yield selectTreeItem(store);
let eventWait = gUI.once("store-objects-updated");
let cells = getRowCells(rowName, true);
let cells = getRowCells(rowName);
yield waitForContextMenu(contextMenu, cells.name, () => {
info(`Opened context menu in ${storeName}, row '${rowName}'`);
if (deleteAll) {
@ -43,24 +43,54 @@ add_task(function* () {
info("test state before delete");
yield checkState([
[["cookies", "test1.example.org"], ["c1", "c3", "cs2", "uc1"]],
[["cookies", "sectest1.example.org"], ["cs2", "sc1", "uc1"]],
[
["cookies", "test1.example.org"], [
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c3", "test1.example.org", "/"),
getCookieId("cs2", ".example.org", "/"),
getCookieId("uc1", ".example.org", "/")
]
],
[
["cookies", "sectest1.example.org"], [
getCookieId("cs2", ".example.org", "/"),
getCookieId("sc1", "sectest1.example.org",
"/browser/devtools/client/storage/test/"),
getCookieId("uc1", ".example.org", "/")
]
],
]);
info("delete all from domain");
// delete only cookies that match the host exactly
yield performDelete(["cookies", "test1.example.org"], "c1", false);
let id = getCookieId("c1", "test1.example.org", "/browser");
yield performDelete(["cookies", "test1.example.org"], id, 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"]],
[
["cookies", "test1.example.org"],
[
getCookieId("cs2", ".example.org", "/"),
getCookieId("uc1", ".example.org", "/")
]
],
[
["cookies", "sectest1.example.org"],
[
getCookieId("cs2", ".example.org", "/"),
getCookieId("uc1", ".example.org", "/"),
getCookieId("sc1", "sectest1.example.org",
"/browser/devtools/client/storage/test/"),
]
],
]);
info("delete all");
// delete all cookies for host, including domain cookies
yield performDelete(["cookies", "sectest1.example.org"], "uc1", true);
id = getCookieId("uc1", ".example.org", "/");
yield performDelete(["cookies", "sectest1.example.org"], id, true);
info("test state after delete all");
yield checkState([

View file

@ -13,8 +13,16 @@ add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
yield checkState([
[["cookies", "test1.example.org"],
["test1", "test2", "test3", "test4", "test5"]],
[
["cookies", "test1.example.org"],
[
getCookieId("test1", ".test1.example.org", "/browser"),
getCookieId("test2", "test1.example.org", "/browser"),
getCookieId("test3", ".test1.example.org", "/browser"),
getCookieId("test4", "test1.example.org", "/browser"),
getCookieId("test5", ".test1.example.org", "/browser")
]
],
]);
yield finishTests();

View file

@ -10,13 +10,20 @@ add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
showAllColumns(true);
yield editCell("test3", "name", "newTest3");
yield editCell("newTest3", "path", "/");
yield editCell("newTest3", "host", "test1.example.org");
yield editCell("newTest3", "expires", "Tue, 14 Feb 2040 17:41:14 GMT");
yield editCell("newTest3", "value", "newValue3");
yield editCell("newTest3", "isSecure", "true");
yield editCell("newTest3", "isHttpOnly", "true");
let id = getCookieId("test3", ".test1.example.org", "/browser");
yield editCell(id, "name", "newTest3");
id = getCookieId("newTest3", ".test1.example.org", "/browser");
yield editCell(id, "host", "test1.example.org");
id = getCookieId("newTest3", "test1.example.org", "/browser");
yield editCell(id, "path", "/");
id = getCookieId("newTest3", "test1.example.org", "/");
yield editCell(id, "expires", "Tue, 14 Feb 2040 17:41:14 GMT");
yield editCell(id, "value", "newValue3");
yield editCell(id, "isSecure", "true");
yield editCell(id, "isHttpOnly", "true");
yield finishTests();
});

View file

@ -10,10 +10,11 @@ add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
showAllColumns(true);
yield startCellEdit("test4", "name");
let id = getCookieId("test4", "test1.example.org", "/browser");
yield startCellEdit(id, "name");
yield typeWithTerminator("test6", "VK_TAB");
yield typeWithTerminator("/", "VK_TAB");
yield typeWithTerminator(".example.org", "VK_TAB");
yield typeWithTerminator("/", "VK_TAB");
yield typeWithTerminator("Tue, 25 Dec 2040 12:00:00 GMT", "VK_TAB");
yield typeWithTerminator("test6value", "VK_TAB");
yield typeWithTerminator("false", "VK_TAB");

View file

@ -10,7 +10,8 @@ add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies.html");
showAllColumns(true);
yield startCellEdit("test1", "name");
let id = getCookieId("test1", ".test1.example.org", "/browser");
yield startCellEdit(id, "name");
PressKeyXTimes("VK_TAB", 18);
is(getCurrentEditorValue(), "value3",

View file

@ -13,8 +13,10 @@ const TEST_CASES = [
"ls1", "name"],
[["sessionStorage", "http://test1.example.org"],
"ss1", "name"],
[["cookies", "test1.example.org"],
"c1", "name"],
[
["cookies", "test1.example.org"],
getCookieId("c1", "test1.example.org", "/browser"), "name"
],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"],
1, "name"],
[["Cache", "http://test1.example.org", "plop"],
@ -41,7 +43,7 @@ add_task(function* () {
yield waitForContextMenu(contextMenu, row[cellToClick], () => {
info(`Opened context menu in ${treeItemName}, row '${rowName}'`);
menuDeleteItem.click();
let truncatedRowName = String(rowName).substr(0, 16);
let truncatedRowName = String(rowName).replace(SEPARATOR_GUID, "-").substr(0, 16);
ok(menuDeleteItem.getAttribute("label").includes(truncatedRowName),
`Context menu item label contains '${rowName}' (maybe truncated)`);
});

View file

@ -17,7 +17,15 @@ add_task(function* () {
info("test state before delete");
yield checkState([
[["cookies", "test1.example.org"], ["c1", "c3", "cs2", "uc1"]],
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("cs2", ".example.org", "/"),
getCookieId("c3", "test1.example.org", "/"),
getCookieId("uc1", ".example.org", "/")
]
],
[["localStorage", "http://test1.example.org"], ["ls1", "ls2"]],
[["sessionStorage", "http://test1.example.org"], ["ss1"]],
[["indexedDB", "http://test1.example.org", "idb1", "obj1"], [1, 2, 3]],

View file

@ -0,0 +1,188 @@
/* 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";
// Test dynamic updates in the storage inspector for cookies.
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
gUI.tree.expandAll();
ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
let c1id = getCookieId("c1", "test1.example.org", "/browser");
yield selectTableItem(c1id);
// test that value is something initially
let initialValue = [[
{name: "c1", value: "1.2.3.4.5.6.7"},
{name: "c1.Path", value: "/browser"}
], [
{name: "c1", value: "Array"},
{name: "c1.0", value: "1"},
{name: "c1.6", value: "7"}
]];
// test that value is something initially
let finalValue = [[
{name: "c1", value: '{"foo": 4,"bar":6}'},
{name: "c1.Path", value: "/browser"}
], [
{name: "c1", value: "Object"},
{name: "c1.foo", value: "4"},
{name: "c1.bar", value: "6"}
]];
// Check that sidebar shows correct initial value
yield findVariableViewProperties(initialValue[0], false);
yield findVariableViewProperties(initialValue[1], true);
// Check if table shows correct initial value
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c2", "test1.example.org", "/browser")
]
],
]);
checkCell(c1id, "value", "1.2.3.4.5.6.7");
gWindow.addCookie("c1", '{"foo": 4,"bar":6}', "/browser");
yield gUI.once("sidebar-updated");
yield findVariableViewProperties(finalValue[0], false);
yield findVariableViewProperties(finalValue[1], true);
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c2", "test1.example.org", "/browser")
]
],
]);
checkCell(c1id, "value", '{"foo": 4,"bar":6}');
// Add a new entry
gWindow.addCookie("c3", "booyeah");
// Wait once for update and another time for value fetching
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c2", "test1.example.org", "/browser"),
getCookieId("c3", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
let c3id = getCookieId("c3", "test1.example.org",
"/browser/devtools/client/storage/test/");
checkCell(c3id, "value", "booyeah");
// Add another
gWindow.addCookie("c4", "booyeah");
// Wait once for update and another time for value fetching
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c1", "test1.example.org", "/browser"),
getCookieId("c2", "test1.example.org", "/browser"),
getCookieId("c3", "test1.example.org",
"/browser/devtools/client/storage/test/"),
getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
let c4id = getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/");
checkCell(c4id, "value", "booyeah");
// Removing cookies
gWindow.removeCookie("c1", "/browser");
yield gUI.once("sidebar-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c2", "test1.example.org", "/browser"),
getCookieId("c3", "test1.example.org",
"/browser/devtools/client/storage/test/"),
getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
ok(!gUI.sidebar.hidden, "Sidebar still visible for next row");
// Check if next element's value is visible in sidebar
yield findVariableViewProperties([{name: "c2", value: "foobar"}]);
// Keep deleting till no rows
gWindow.removeCookie("c3");
yield gUI.once("store-objects-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c2", "test1.example.org", "/browser"),
getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
// Check if next element's value is visible in sidebar
yield findVariableViewProperties([{name: "c2", value: "foobar"}]);
gWindow.removeCookie("c2", "/browser");
yield gUI.once("sidebar-updated");
yield checkState([
[
["cookies", "test1.example.org"],
[
getCookieId("c4", "test1.example.org",
"/browser/devtools/client/storage/test/")
]
],
]);
// Check if next element's value is visible in sidebar
yield findVariableViewProperties([{name: "c4", value: "booyeah"}]);
gWindow.removeCookie("c4");
yield gUI.once("store-objects-updated");
yield checkState([
[["cookies", "test1.example.org"], [ ]],
]);
ok(gUI.sidebar.hidden, "Sidebar is hidden when no rows");
yield finishTests();
});

View file

@ -0,0 +1,70 @@
/* 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";
// Test dynamic updates in the storage inspector for localStorage.
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
gUI.tree.expandAll();
ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
yield checkState([
[
["localStorage", "http://test1.example.org"],
["ls1", "ls2", "ls3", "ls4", "ls5", "ls6", "ls7"]
],
]);
gWindow.localStorage.removeItem("ls4");
yield gUI.once("store-objects-updated");
yield checkState([
[
["localStorage", "http://test1.example.org"],
["ls1", "ls2", "ls3", "ls5", "ls6", "ls7"]
],
]);
gWindow.localStorage.setItem("ls4", "again");
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
yield checkState([
[
["localStorage", "http://test1.example.org"],
["ls1", "ls2", "ls3", "ls4", "ls5", "ls6", "ls7"]
],
]);
// Updating a row
gWindow.localStorage.setItem("ls2", "ls2-changed");
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
checkCell("ls2", "value", "ls2-changed");
// Clearing items. Bug 1233497 makes it so that we can no longer yield
// CPOWs from Tasks. We work around this by calling clear via a ContentTask
// instead.
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
return Task.spawn(content.wrappedJSObject.clear);
});
yield gUI.once("store-objects-cleared");
yield checkState([
[
["localStorage", "http://test1.example.org"],
[ ]
],
]);
yield finishTests();
});

View file

@ -0,0 +1,83 @@
/* 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";
// Test dynamic updates in the storage inspector for sessionStorage.
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-updates.html");
gUI.tree.expandAll();
ok(gUI.sidebar.hidden, "Sidebar is initially hidden");
yield checkState([
[
["sessionStorage", "http://test1.example.org"],
["ss1", "ss2", "ss3"]
],
]);
gWindow.sessionStorage.setItem("ss4", "new-item");
yield gUI.once("store-objects-updated");
yield gUI.once("store-objects-updated");
yield checkState([
[
["sessionStorage", "http://test1.example.org"],
["ss1", "ss2", "ss3", "ss4"]
],
]);
// deleting item
gWindow.sessionStorage.removeItem("ss3");
yield gUI.once("store-objects-updated");
gWindow.sessionStorage.removeItem("ss1");
yield gUI.once("store-objects-updated");
yield checkState([
[
["sessionStorage", "http://test1.example.org"],
["ss2", "ss4"]
],
]);
yield selectTableItem("ss2");
ok(!gUI.sidebar.hidden, "sidebar is visible");
// Checking for correct value in sidebar before update
yield findVariableViewProperties([{name: "ss2", value: "foobar"}]);
gWindow.sessionStorage.setItem("ss2", "changed=ss2");
yield gUI.once("sidebar-updated");
checkCell("ss2", "value", "changed=ss2");
yield findVariableViewProperties([{name: "ss2", value: "changed=ss2"}]);
// Clearing items. Bug 1233497 makes it so that we can no longer yield
// CPOWs from Tasks. We work around this by calling clear via a ContentTask
// instead.
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
return Task.spawn(content.wrappedJSObject.clear);
});
yield gUI.once("store-objects-cleared");
yield checkState([
[
["sessionStorage", "http://test1.example.org"],
[ ]
],
]);
yield finishTests();
});

View file

@ -20,22 +20,22 @@ const testCases = [
sidebarHidden: true
},
{
location: "cs2",
location: getCookieId("cs2", ".example.org", "/"),
sidebarHidden: false
},
{
sendEscape: true
},
{
location: "cs2",
location: getCookieId("cs2", ".example.org", "/"),
sidebarHidden: false
},
{
location: "uc1",
location: getCookieId("uc1", ".example.org", "/"),
sidebarHidden: false
},
{
location: "uc1",
location: getCookieId("uc1", ".example.org", "/"),
sidebarHidden: false
},

View file

@ -17,7 +17,7 @@
const LONG_WORD = "a".repeat(1000);
const testCases = [
["cs2", [
[getCookieId("cs2", ".example.org", "/"), [
{name: "cs2", value: "sessionCookie"},
{name: "cs2.Path", value: "/"},
{name: "cs2.HostOnly", value: "false"},
@ -26,7 +26,7 @@ const testCases = [
{name: "cs2.Expires", value: "Session"},
{name: "cs2.Secure", value: "false"},
]],
["c1", [
[getCookieId("c1", "test1.example.org", "/browser"), [
{name: "c1", value: JSON.stringify(["foo", "Bar", {foo: "Bar"}])},
{name: "c1.Path", value: "/browser"},
{name: "c1.HostOnly", value: "true"},
@ -42,9 +42,13 @@ const testCases = [
{name: "c1.2", value: "Object"},
{name: "c1.2.foo", value: "Bar"},
], true],
["c_encoded", [
{name: "c_encoded", value: encodeURIComponent(JSON.stringify({foo: {foo1: "bar"}}))}
]],
[
getCookieId("c_encoded", "test1.example.org",
"/browser/devtools/client/storage/test/"),
[
{name: "c_encoded", value: encodeURIComponent(JSON.stringify({foo: {foo1: "bar"}}))}
]
],
[null, [
{name: "c_encoded", value: "Object"},
{name: "c_encoded.foo", value: "Object"},

View file

@ -24,6 +24,11 @@ const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/server/actors/storage.js,
// devtools/client/storage/ui.js and devtools/server/tests/browser/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
var gToolbox, gPanelWindow, gWindow, gUI;
// Services.prefs.setBoolPref(DUMPEMIT_PREF, true);
@ -505,10 +510,16 @@ function* selectTreeItem(ids) {
* The id of the row in the table widget
*/
function* selectTableItem(id) {
let selector = ".table-widget-cell[data-id='" + id + "']";
let table = gUI.table;
let selector = ".table-widget-column#" + table.uniqueId +
" .table-widget-cell[value='" + id + "']";
let target = gPanelWindow.document.querySelector(selector);
ok(target, "table item found with ids " + id);
if (!target) {
showAvailableIds();
}
yield click(target);
yield gUI.once("sidebar-updated");
}
@ -586,21 +597,38 @@ function getRowCells(id, includeHidden = false) {
if (!item) {
ok(false, "Row id '" + id + "' exists");
showAvailableIds();
}
let index = table.columns.get(table.uniqueId).visibleCellNodes.indexOf(item);
let index = table.columns.get(table.uniqueId).cellNodes.indexOf(item);
let cells = {};
for (let [name, column] of [...table.columns]) {
if (!includeHidden && column.column.parentNode.hidden) {
continue;
}
cells[name] = column.visibleCellNodes[index];
cells[name] = column.cellNodes[index];
}
return cells;
}
/**
* Show available ids.
*/
function showAvailableIds() {
let doc = gPanelWindow.document;
let table = gUI.table;
info("Available ids:");
let cells = doc.querySelectorAll(".table-widget-column#" + table.uniqueId +
" .table-widget-cell");
for (let cell of cells) {
info(" - " + cell.getAttribute("value"));
}
}
/**
* Get a cell value.
*
@ -798,9 +826,18 @@ function* checkState(state) {
is(items.size, names.length,
`There is correct number of rows in ${storeName}`);
if (names.length === 0) {
showAvailableIds();
}
for (let name of names) {
ok(items.has(name),
`There is item with name '${name}' in ${storeName}`);
if (!items.has(name)) {
showAvailableIds();
}
}
}
}
@ -838,3 +875,7 @@ var focusSearchBoxUsingShortcut = Task.async(function* (panelWin, callback) {
callback();
}
});
function getCookieId(name, domain, path) {
return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`;
}

View file

@ -38,8 +38,10 @@ window.removeCookie = function(name, path) {
* can be tested.
*/
window.clear = function*() {
sessionStorage.clear();
localStorage.clear();
dump("removed localStorage from " + document.location + "\n");
sessionStorage.clear();
dump("removed sessionStorage from " + document.location + "\n");
};

View file

@ -12,6 +12,12 @@ const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
const JSOL = require("devtools/client/shared/vendor/jsol");
const {KeyCodes} = require("devtools/client/shared/keycodes");
// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/server/actors/storage.js,
// devtools/client/storage/test/head.js and
// devtools/server/tests/browser/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
loader.lazyRequireGetter(this, "TreeWidget",
"devtools/client/shared/widgets/TreeWidget", true);
loader.lazyRequireGetter(this, "TableWidget",
@ -36,13 +42,6 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = {
preventDescriptorModifiers: true
};
// Columns which are hidden by default in the storage table
const HIDDEN_COLUMNS = [
"creationTime",
"isDomain",
"isSecure"
];
const REASON = {
NEW_ROW: "new-row",
NEXT_50_ITEMS: "next-50-items",
@ -786,6 +785,8 @@ StorageUI.prototype = {
let uniqueKey = null;
let columns = {};
let editableFields = [];
let hiddenFields = [];
let privateFields = [];
let fields = yield this.getCurrentActor().getFields(subtype);
fields.forEach(f => {
@ -797,6 +798,14 @@ StorageUI.prototype = {
editableFields.push(f.name);
}
if (f.hidden) {
hiddenFields.push(f.name);
}
if (f.private) {
privateFields.push(f.name);
}
columns[f.name] = f.name;
let columnName;
try {
@ -812,7 +821,7 @@ StorageUI.prototype = {
}
});
this.table.setColumns(columns, null, HIDDEN_COLUMNS);
this.table.setColumns(columns, null, hiddenFields, privateFields);
this.hideSidebar();
yield this.makeFieldsEditable(editableFields);
@ -927,10 +936,13 @@ StorageUI.prototype = {
let rowId = this.table.contextMenuRowId;
let data = this.table.items.get(rowId);
let name = addEllipsis(data[this.table.uniqueId]);
let name = data[this.table.uniqueId];
let separatorRegex = new RegExp(SEPARATOR_GUID, "g");
let label = addEllipsis((name + "").replace(separatorRegex, "-"));
this._tablePopupDelete.setAttribute("label",
L10N.getFormatStr("storage.popupMenu.deleteLabel", name));
L10N.getFormatStr("storage.popupMenu.deleteLabel", label));
if (type === "cookies") {
let host = addEllipsis(data.host);

View file

@ -15,6 +15,12 @@ const {isWindowIncluded} = require("devtools/shared/layout/utils");
const specs = require("devtools/shared/specs/storage");
const { Task } = require("devtools/shared/task");
// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/client/storage/ui.js,
// devtools/client/storage/test/head.js and
// devtools/server/tests/browser/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
loader.lazyImporter(this, "Sqlite", "resource://gre/modules/Sqlite.jsm");
@ -460,11 +466,13 @@ StorageActors.createActor({
}
return {
uniqueKey: `${cookie.name}${SEPARATOR_GUID}${cookie.host}` +
`${SEPARATOR_GUID}${cookie.path}`,
name: cookie.name,
path: cookie.path || "",
host: cookie.host || "",
path: cookie.path || "",
// because expires is in seconds
// because creationTime is in micro seconds
expires: (cookie.expires || 0) * 1000,
// because it is in micro seconds
@ -488,7 +496,10 @@ StorageActors.createActor({
for (let cookie of cookies) {
if (this.isCookieAtHost(cookie, host)) {
this.hostVsStores.get(host).set(cookie.name, cookie);
let uniqueKey = `${cookie.name}${SEPARATOR_GUID}${cookie.host}` +
`${SEPARATOR_GUID}${cookie.path}`;
this.hostVsStores.get(host).set(uniqueKey, cookie);
}
}
},
@ -521,8 +532,11 @@ StorageActors.createActor({
case "changed":
if (hosts.length) {
for (let host of hosts) {
this.hostVsStores.get(host).set(subject.name, subject);
data[host] = [subject.name];
let uniqueKey = `${subject.name}${SEPARATOR_GUID}${subject.host}` +
`${SEPARATOR_GUID}${subject.path}`;
this.hostVsStores.get(host).set(uniqueKey, subject);
data[host] = [uniqueKey];
}
this.storageActor.update(action, "cookies", data);
}
@ -531,8 +545,11 @@ StorageActors.createActor({
case "deleted":
if (hosts.length) {
for (let host of hosts) {
this.hostVsStores.get(host).delete(subject.name);
data[host] = [subject.name];
let uniqueKey = `${subject.name}${SEPARATOR_GUID}${subject.host}` +
`${SEPARATOR_GUID}${subject.path}`;
this.hostVsStores.get(host).delete(uniqueKey);
data[host] = [uniqueKey];
}
this.storageActor.update("deleted", "cookies", data);
}
@ -543,8 +560,11 @@ StorageActors.createActor({
for (let host of hosts) {
let stores = [];
for (let cookie of subject) {
this.hostVsStores.get(host).delete(cookie.name);
stores.push(cookie.name);
let uniqueKey = `${cookie.name}${SEPARATOR_GUID}${cookie.host}` +
`${SEPARATOR_GUID}${cookie.path}`;
this.hostVsStores.get(host).delete(uniqueKey);
stores.push(uniqueKey);
}
data[host] = stores;
}
@ -566,15 +586,17 @@ StorageActors.createActor({
getFields: Task.async(function* () {
return [
{ name: "name", editable: 1},
{ name: "path", editable: 1},
{ name: "host", editable: 1},
{ name: "expires", editable: 1},
{ name: "lastAccessed", editable: 0},
{ name: "value", editable: 1},
{ name: "isDomain", editable: 0},
{ name: "isSecure", editable: 1},
{ name: "isHttpOnly", editable: 1}
{ name: "uniqueKey", editable: false, private: true },
{ name: "name", editable: true, hidden: false },
{ name: "host", editable: true, hidden: false },
{ name: "path", editable: true, hidden: false },
{ name: "expires", editable: true, hidden: false },
{ name: "lastAccessed", editable: false, hidden: false },
{ name: "creationTime", editable: false, hidden: true },
{ name: "value", editable: true, hidden: false },
{ name: "isDomain", editable: false, hidden: true },
{ name: "isSecure", editable: true, hidden: true },
{ name: "isHttpOnly", editable: true, hidden: false }
];
}),
@ -696,7 +718,7 @@ var cookieHelpers = {
* {
* host: "http://www.mozilla.org",
* field: "value",
* key: "name",
* editCookie: "name",
* oldValue: "%7BHello%7D",
* newValue: "%7BHelloo%7D",
* items: {
@ -720,10 +742,13 @@ var cookieHelpers = {
let origPath = field === "path" ? oldValue : data.items.path;
let cookie = null;
let enumerator = Services.cookies.getCookiesFromHost(origHost, data.originAttributes || {});
let enumerator =
Services.cookies.getCookiesFromHost(origHost, data.originAttributes || {});
while (enumerator.hasMoreElements()) {
let nsiCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
if (nsiCookie.name === origName && nsiCookie.host === origHost) {
if (nsiCookie.name === origName &&
nsiCookie.host === origHost &&
nsiCookie.path === origPath) {
cookie = {
host: nsiCookie.host,
path: nsiCookie.path,
@ -743,7 +768,7 @@ var cookieHelpers = {
return;
}
// If the date is expired set it for 1 minute in the future.
// If the date is expired set it for 10 seconds in the future.
let now = new Date();
if (!cookie.isSession && (cookie.expires * 1000) <= now) {
let tenSecondsFromNow = (now.getTime() + 10 * 1000) / 1000;
@ -797,6 +822,15 @@ var cookieHelpers = {
},
_removeCookies(host, opts = {}) {
// We use a uniqueId to emulate compound keys for cookies. We need to
// extract the cookie name to remove the correct cookie.
if (opts.name) {
let split = opts.name.split(SEPARATOR_GUID);
opts.name = split[0];
opts.path = split[2];
}
function hostMatches(cookieHost, matchHost) {
if (cookieHost == null) {
return matchHost == null;
@ -807,12 +841,15 @@ var cookieHelpers = {
return cookieHost == host;
}
let enumerator = Services.cookies.getCookiesFromHost(host, opts.originAttributes || {});
let enumerator =
Services.cookies.getCookiesFromHost(host, opts.originAttributes || {});
while (enumerator.hasMoreElements()) {
let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
if (hostMatches(cookie.host, host) &&
(!opts.name || cookie.name === opts.name) &&
(!opts.domain || cookie.host === opts.domain)) {
(!opts.domain || cookie.host === opts.domain) &&
(!opts.path || cookie.path === opts.path)) {
Services.cookies.remove(
cookie.host,
cookie.name,
@ -1024,8 +1061,8 @@ function getObjectForLocalOrSessionStorage(type) {
getFields: Task.async(function* () {
return [
{ name: "name", editable: 1},
{ name: "value", editable: 1}
{ name: "name", editable: true },
{ name: "value", editable: true }
];
}),
@ -1205,8 +1242,8 @@ StorageActors.createActor({
getFields: Task.async(function* () {
return [
{ name: "url", editable: 0 },
{ name: "status", editable: 0 }
{ name: "url", editable: false },
{ name: "status", editable: false }
];
}),
@ -1725,26 +1762,26 @@ StorageActors.createActor({
// Detail of database
case "database":
return [
{ name: "objectStore", editable: 0 },
{ name: "keyPath", editable: 0 },
{ name: "autoIncrement", editable: 0 },
{ name: "indexes", editable: 0 },
{ name: "objectStore", editable: false },
{ name: "keyPath", editable: false },
{ name: "autoIncrement", editable: false },
{ name: "indexes", editable: false },
];
// Detail of object store
case "object store":
return [
{ name: "name", editable: 0 },
{ name: "value", editable: 0 }
{ name: "name", editable: false },
{ name: "value", editable: false }
];
// Detail of indexedDB for one origin
default:
return [
{ name: "db", editable: 0 },
{ name: "origin", editable: 0 },
{ name: "version", editable: 0 },
{ name: "objectStores", editable: 0 },
{ name: "db", editable: false },
{ name: "origin", editable: false },
{ name: "version", editable: false },
{ name: "objectStores", editable: false },
];
}
})
@ -2480,6 +2517,7 @@ let StorageActor = protocol.ActorClassWithSpec(specs.storageSpec, {
// added or changed update
this.removeNamesFromUpdateList("added", storeType, data);
this.removeNamesFromUpdateList("changed", storeType, data);
for (let host in data) {
if (data[host].length == 0 && this.boundUpdate.added &&
this.boundUpdate.added[storeType] &&

View file

@ -11,6 +11,7 @@ support-files =
doc_perf.html
navigate-first.html
navigate-second.html
storage-cookies-same-name.html
storage-dynamic-windows.html
storage-listings.html
storage-unsecured-iframe.html
@ -80,6 +81,7 @@ skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still di
#[browser_perf-front-profiler-01.js] bug 1077464
#[browser_perf-front-profiler-05.js] bug 1077464
#[browser_perf-front-profiler-06.js]
[browser_storage_cookies-duplicate-names.js]
[browser_storage_dynamic_windows.js]
[browser_storage_listings.js]
[browser_storage_updates.js]

View file

@ -0,0 +1,105 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the storage panel is able to display multiple cookies with the same
// name (and different paths).
const {StorageFront} = require("devtools/shared/fronts/storage");
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/server/tests/browser/storage-helpers.js", this);
const TESTDATA = {
"test1.example.org": [
{
name: "name",
value: "value1",
expires: 0,
path: "/",
host: "test1.example.org",
isDomain: false,
isSecure: false,
},
{
name: "name",
value: "value2",
expires: 0,
path: "/path2/",
host: "test1.example.org",
isDomain: false,
isSecure: false,
},
{
name: "name",
value: "value3",
expires: 0,
path: "/path3/",
host: "test1.example.org",
isDomain: false,
isSecure: false,
}
]
};
add_task(function* () {
yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-cookies-same-name.html");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let front = StorageFront(client, form);
let data = yield front.listStores();
ok(data.cookies, "Cookies storage actor is present");
yield testCookies(data.cookies);
yield clearStorage();
// Forcing GC/CC to get rid of docshells and windows created by this test.
forceCollections();
yield client.close();
forceCollections();
DebuggerServer.destroy();
forceCollections();
});
function testCookies(cookiesActor) {
let numHosts = Object.keys(cookiesActor.hosts).length;
is(numHosts, 1, "Correct number of host entries for cookies");
return testCookiesObjects(0, cookiesActor.hosts, cookiesActor);
}
var testCookiesObjects = Task.async(function* (index, hosts, cookiesActor) {
let host = Object.keys(hosts)[index];
let matchItems = data => {
is(data.total, TESTDATA[host].length,
"Number of cookies in host " + host + " matches");
for (let item of data.data) {
let found = false;
for (let toMatch of TESTDATA[host]) {
if (item.name === toMatch.name &&
item.host === toMatch.host &&
item.path === toMatch.path) {
found = true;
ok(true, "Found cookie " + item.name + " in response");
is(item.value.str, toMatch.value, "The value matches.");
is(item.expires, toMatch.expires, "The expiry time matches.");
is(item.path, toMatch.path, "The path matches.");
is(item.host, toMatch.host, "The host matches.");
is(item.isSecure, toMatch.isSecure, "The isSecure value matches.");
is(item.isDomain, toMatch.isDomain, "The isDomain value matches.");
break;
}
}
ok(found, "cookie " + item.name + " should exist in response");
}
};
ok(!!TESTDATA[host], "Host is present in the list : " + host);
matchItems(yield cookiesActor.getStoreObjects(host));
if (index == Object.keys(hosts).length - 1) {
return;
}
yield testCookiesObjects(++index, hosts, cookiesActor);
});

View file

@ -66,6 +66,7 @@ function markOutMatched(toBeEmptied, data, deleted) {
info("Testing for " + storageType);
for (let host in data[storageType]) {
ok(toBeEmptied[storageType][host], "Host " + host + " found");
if (!deleted) {
for (let item of data[storageType][host]) {
let index = toBeEmptied[storageType][host].indexOf(item);
@ -87,50 +88,6 @@ function markOutMatched(toBeEmptied, data, deleted) {
}
}
// function testReload(front) {
// info("Testing if reload works properly");
// let shouldBeEmptyFirst = Cu.cloneInto(beforeReload, {});
// let shouldBeEmptyLast = Cu.cloneInto(beforeReload, {});
// return new Promise(resolve => {
// let onStoresUpdate = data => {
// info("in stores update of testReload");
// // This might be second time stores update is happening, in which case,
// // data.deleted will be null.
// // OR.. This might be the first time on a super slow machine where both
// // data.deleted and data.added is missing in the first update.
// if (data.deleted) {
// markOutMatched(shouldBeEmptyFirst, data.deleted, true);
// }
// if (!Object.keys(shouldBeEmptyFirst).length) {
// info("shouldBeEmptyFirst is empty now");
// }
// // stores-update call might not have data.added for the first time on
// // slow machines, in which case, data.added will be null
// if (data.added) {
// markOutMatched(shouldBeEmptyLast, data.added);
// }
// if (!Object.keys(shouldBeEmptyLast).length) {
// info("Everything to be received is received.");
// endTestReloaded();
// }
// };
// let endTestReloaded = () => {
// front.off("stores-update", onStoresUpdate);
// resolve();
// };
// front.on("stores-update", onStoresUpdate);
// content.location.reload();
// });
// }
function testAddIframe(front) {
info("Testing if new iframe addition works properly");
return new Promise(resolve => {
@ -142,7 +99,10 @@ function testAddIframe(front) {
"https://sectest1.example.org": ["iframe-s-ss1"]
},
cookies: {
"sectest1.example.org": ["sc1"]
"sectest1.example.org": [
getCookieId("sc1", "sectest1.example.org",
"/browser/devtools/server/tests/browser/")
]
},
indexedDB: {
// empty because indexed db creation happens after the page load, so at
@ -150,7 +110,7 @@ function testAddIframe(front) {
"https://sectest1.example.org": []
},
Cache: {
"https://sectest1.example.org":[]
"https://sectest1.example.org": []
}
};

View file

@ -19,15 +19,6 @@ const storeMap = {
isDomain: false,
isSecure: false,
},
{
name: "cs2",
value: "sessionCookie",
path: "/",
host: ".example.org",
expires: 0,
isDomain: true,
isSecure: false,
},
{
name: "c3",
value: "foobar-2",
@ -337,7 +328,8 @@ function* testStores(data) {
}
function testCookies(cookiesActor) {
is(Object.keys(cookiesActor.hosts).length, 2, "Correct number of host entries for cookies");
is(Object.keys(cookiesActor.hosts).length, 2,
"Correct number of host entries for cookies");
return testCookiesObjects(0, cookiesActor.hosts, cookiesActor);
}
@ -346,9 +338,9 @@ var testCookiesObjects = Task.async(function* (index, hosts, cookiesActor) {
let matchItems = data => {
let cookiesLength = 0;
for (let secureCookie of storeMap.cookies[host]) {
if (secureCookie.isSecure) {
++cookiesLength;
}
if (secureCookie.isSecure) {
++cookiesLength;
}
}
// Any secure cookies did not get stored in the database.
is(data.total, storeMap.cookies[host].length - cookiesLength,

View file

@ -27,7 +27,12 @@ const TESTS = [
expected: {
added: {
cookies: {
"test1.example.org": ["c1", "c2"]
"test1.example.org": [
getCookieId("c1", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
getCookieId("c2", "test1.example.org",
"/browser/devtools/server/tests/browser/")
]
},
localStorage: {
"http://test1.example.org": ["l1"]
@ -48,7 +53,10 @@ const TESTS = [
expected: {
changed: {
cookies: {
"test1.example.org": ["c1"]
"test1.example.org": [
getCookieId("c1", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
}
},
added: {
@ -74,7 +82,10 @@ const TESTS = [
expected: {
deleted: {
cookies: {
"test1.example.org": ["c2"]
"test1.example.org": [
getCookieId("c2", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
},
localStorage: {
"http://test1.example.org": ["l1"]
@ -112,7 +123,10 @@ const TESTS = [
expected: {
added: {
cookies: {
"test1.example.org": ["c3"]
"test1.example.org": [
getCookieId("c3", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
},
sessionStorage: {
"http://test1.example.org": ["s1", "s2"]
@ -125,7 +139,10 @@ const TESTS = [
},
deleted: {
cookies: {
"test1.example.org": ["c1"]
"test1.example.org": [
getCookieId("c1", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
},
localStorage: {
"http://test1.example.org": ["l2"]
@ -158,7 +175,10 @@ const TESTS = [
expected: {
deleted: {
cookies: {
"test1.example.org": ["c3"]
"test1.example.org": [
getCookieId("c3", "test1.example.org",
"/browser/devtools/server/tests/browser/"),
]
}
}
}

View file

@ -2,6 +2,10 @@
* 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";
/* eslint no-unused-vars: [2, {"vars": "local"}] */
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
@ -19,6 +23,11 @@ const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
// GUID to be used as a separator in compound keys. This must match the same
// constant in devtools/server/actors/storage.js,
// devtools/client/storage/ui.js and devtools/client/storage/test/head.js
const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
// All tests are asynchronous.
waitForExplicitFinish();
@ -94,7 +103,6 @@ function once(target, eventName, useCapture = false) {
info("Waiting for event: '" + eventName + "' on " + target + ".");
return new Promise(resolve => {
for (let [add, remove] of [
["addEventListener", "removeEventListener"],
["addListener", "removeListener"],
@ -137,6 +145,8 @@ function getMockTabActor(win) {
}
registerCleanupFunction(function tearDown() {
Services.cookies.removeAll();
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
@ -148,8 +158,11 @@ function idleWait(time) {
function busyWait(time) {
let start = Date.now();
// eslint-disable-next-line
let stack;
while (Date.now() - start < time) { stack = Components.stack; }
while (Date.now() - start < time) {
stack = Components.stack;
}
}
/**
@ -172,11 +185,12 @@ function waitUntil(predicate, interval = 10) {
}
function waitForMarkerType(front, types, predicate,
unpackFun = (name, data) => data.markers,
eventName = "timeline-data")
{
unpackFun = (name, data) => data.markers,
eventName = "timeline-data") {
types = [].concat(types);
predicate = predicate || function () { return true; };
predicate = predicate || function () {
return true;
};
let filteredMarkers = [];
let { promise, resolve } = defer();
@ -190,9 +204,11 @@ function waitForMarkerType(front, types, predicate,
let markers = unpackFun(name, data);
info("Got markers: " + JSON.stringify(markers, null, 2));
filteredMarkers = filteredMarkers.concat(markers.filter(m => types.indexOf(m.name) !== -1));
filteredMarkers = filteredMarkers.concat(
markers.filter(m => types.indexOf(m.name) !== -1));
if (types.every(t => filteredMarkers.some(m => m.name === t)) && predicate(filteredMarkers)) {
if (types.every(t => filteredMarkers.some(m => m.name === t)) &&
predicate(filteredMarkers)) {
front.off(eventName, handler);
resolve(filteredMarkers);
}
@ -201,3 +217,7 @@ function waitForMarkerType(front, types, predicate,
return promise;
}
function getCookieId(name, domain, path) {
return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`;
}

View file

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Storage inspector cookies with duplicate names</title>
</head>
<body onload="createCookies()">
<script type="application/javascript;version=1.7">
"use strict";
function createCookies() {
document.cookie = "name=value1;path=/;";
document.cookie = "name=value2;path=/path2/;";
document.cookie = "name=value3;path=/path3/;";
}
window.removeCookie = function (name) {
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
};
window.clearCookies = function () {
let cookies = document.cookie;
for (let cookie of cookies.split(";")) {
removeCookie(cookie.split("=")[0]);
}
};
</script>
</body>
</html>