From 390894c822f1b163f16744646372a28c0d93a89e Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Fri, 2 Mar 2018 13:36:16 +0100 Subject: Bug 1146194: Multiple cookies with the same name not shown Issue #31 --- devtools/client/locales/en-US/storage.properties | 1 + devtools/client/shared/widgets/TableWidget.js | 34 +++- devtools/client/storage/test/browser.ini | 4 +- .../client/storage/test/browser_storage_basic.js | 23 ++- .../test/browser_storage_cookies_delete_all.js | 44 ++++- .../storage/test/browser_storage_cookies_domain.js | 12 +- .../storage/test/browser_storage_cookies_edit.js | 21 ++- .../test/browser_storage_cookies_edit_keyboard.js | 5 +- .../test/browser_storage_cookies_tab_navigation.js | 3 +- .../client/storage/test/browser_storage_delete.js | 8 +- .../storage/test/browser_storage_delete_tree.js | 10 +- .../browser_storage_dynamic_updates_cookies.js | 188 +++++++++++++++++++++ ...browser_storage_dynamic_updates_localStorage.js | 70 ++++++++ ...owser_storage_dynamic_updates_sessionStorage.js | 83 +++++++++ .../client/storage/test/browser_storage_sidebar.js | 8 +- .../client/storage/test/browser_storage_values.js | 14 +- devtools/client/storage/test/head.js | 47 +++++- devtools/client/storage/test/storage-updates.html | 4 +- devtools/client/storage/ui.js | 32 ++-- devtools/server/actors/storage.js | 114 ++++++++----- devtools/server/tests/browser/browser.ini | 2 + .../browser_storage_cookies-duplicate-names.js | 105 ++++++++++++ .../browser/browser_storage_dynamic_windows.js | 52 +----- .../tests/browser/browser_storage_listings.js | 18 +- .../tests/browser/browser_storage_updates.js | 32 +++- devtools/server/tests/browser/head.js | 36 +++- .../tests/browser/storage-cookies-same-name.html | 28 +++ 27 files changed, 829 insertions(+), 169 deletions(-) create mode 100644 devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js create mode 100644 devtools/client/storage/test/browser_storage_dynamic_updates_localStorage.js create mode 100644 devtools/client/storage/test/browser_storage_dynamic_updates_sessionStorage.js create mode 100644 devtools/server/tests/browser/browser_storage_cookies-duplicate-names.js create mode 100644 devtools/server/tests/browser/storage-cookies-same-name.html (limited to 'devtools') diff --git a/devtools/client/locales/en-US/storage.properties b/devtools/client/locales/en-US/storage.properties index 1eeb88ff9..fd3b3ec52 100644 --- a/devtools/client/locales/en-US/storage.properties +++ b/devtools/client/locales/en-US/storage.properties @@ -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 diff --git a/devtools/client/shared/widgets/TableWidget.js b/devtools/client/shared/widgets/TableWidget.js index 5dacd1b67..96c020230 100644 --- a/devtools/client/shared/widgets/TableWidget.js +++ b/devtools/client/shared/widgets/TableWidget.js @@ -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) { diff --git a/devtools/client/storage/test/browser.ini b/devtools/client/storage/test/browser.ini index dd7f48bd7..0ac28a92d 100644 --- a/devtools/client/storage/test/browser.ini +++ b/devtools/client/storage/test/browser.ini @@ -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] diff --git a/devtools/client/storage/test/browser_storage_basic.js b/devtools/client/storage/test/browser_storage_basic.js index 343d46170..10eb8d0ca 100644 --- a/devtools/client/storage/test/browser_storage_basic.js +++ b/devtools/client/storage/test/browser_storage_basic.js @@ -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"], diff --git a/devtools/client/storage/test/browser_storage_cookies_delete_all.js b/devtools/client/storage/test/browser_storage_cookies_delete_all.js index 6e6008e66..ba64014fd 100644 --- a/devtools/client/storage/test/browser_storage_cookies_delete_all.js +++ b/devtools/client/storage/test/browser_storage_cookies_delete_all.js @@ -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([ diff --git a/devtools/client/storage/test/browser_storage_cookies_domain.js b/devtools/client/storage/test/browser_storage_cookies_domain.js index dc93d6e67..7b194b73e 100644 --- a/devtools/client/storage/test/browser_storage_cookies_domain.js +++ b/devtools/client/storage/test/browser_storage_cookies_domain.js @@ -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(); diff --git a/devtools/client/storage/test/browser_storage_cookies_edit.js b/devtools/client/storage/test/browser_storage_cookies_edit.js index 5818e4864..14944b398 100644 --- a/devtools/client/storage/test/browser_storage_cookies_edit.js +++ b/devtools/client/storage/test/browser_storage_cookies_edit.js @@ -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(); }); diff --git a/devtools/client/storage/test/browser_storage_cookies_edit_keyboard.js b/devtools/client/storage/test/browser_storage_cookies_edit_keyboard.js index 1208c4376..4bbb63fbe 100644 --- a/devtools/client/storage/test/browser_storage_cookies_edit_keyboard.js +++ b/devtools/client/storage/test/browser_storage_cookies_edit_keyboard.js @@ -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"); diff --git a/devtools/client/storage/test/browser_storage_cookies_tab_navigation.js b/devtools/client/storage/test/browser_storage_cookies_tab_navigation.js index 783a0c844..5da359b8d 100644 --- a/devtools/client/storage/test/browser_storage_cookies_tab_navigation.js +++ b/devtools/client/storage/test/browser_storage_cookies_tab_navigation.js @@ -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", diff --git a/devtools/client/storage/test/browser_storage_delete.js b/devtools/client/storage/test/browser_storage_delete.js index c0e2b0ad7..d5cb59a10 100644 --- a/devtools/client/storage/test/browser_storage_delete.js +++ b/devtools/client/storage/test/browser_storage_delete.js @@ -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)`); }); diff --git a/devtools/client/storage/test/browser_storage_delete_tree.js b/devtools/client/storage/test/browser_storage_delete_tree.js index 867a1c8b6..3e866a2f5 100644 --- a/devtools/client/storage/test/browser_storage_delete_tree.js +++ b/devtools/client/storage/test/browser_storage_delete_tree.js @@ -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]], diff --git a/devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js b/devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js new file mode 100644 index 000000000..6cf52f2d3 --- /dev/null +++ b/devtools/client/storage/test/browser_storage_dynamic_updates_cookies.js @@ -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(); +}); diff --git a/devtools/client/storage/test/browser_storage_dynamic_updates_localStorage.js b/devtools/client/storage/test/browser_storage_dynamic_updates_localStorage.js new file mode 100644 index 000000000..35912ce3a --- /dev/null +++ b/devtools/client/storage/test/browser_storage_dynamic_updates_localStorage.js @@ -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(); +}); diff --git a/devtools/client/storage/test/browser_storage_dynamic_updates_sessionStorage.js b/devtools/client/storage/test/browser_storage_dynamic_updates_sessionStorage.js new file mode 100644 index 000000000..8c2f2537e --- /dev/null +++ b/devtools/client/storage/test/browser_storage_dynamic_updates_sessionStorage.js @@ -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(); +}); diff --git a/devtools/client/storage/test/browser_storage_sidebar.js b/devtools/client/storage/test/browser_storage_sidebar.js index 9b60026a0..d1a71c667 100644 --- a/devtools/client/storage/test/browser_storage_sidebar.js +++ b/devtools/client/storage/test/browser_storage_sidebar.js @@ -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 }, diff --git a/devtools/client/storage/test/browser_storage_values.js b/devtools/client/storage/test/browser_storage_values.js index 920ce350e..122a867bb 100644 --- a/devtools/client/storage/test/browser_storage_values.js +++ b/devtools/client/storage/test/browser_storage_values.js @@ -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"}, diff --git a/devtools/client/storage/test/head.js b/devtools/client/storage/test/head.js index 9662393cf..894056c9e 100644 --- a/devtools/client/storage/test/head.js +++ b/devtools/client/storage/test/head.js @@ -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}`; +} diff --git a/devtools/client/storage/test/storage-updates.html b/devtools/client/storage/test/storage-updates.html index a009814b2..341992f61 100644 --- a/devtools/client/storage/test/storage-updates.html +++ b/devtools/client/storage/test/storage-updates.html @@ -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"); }; diff --git a/devtools/client/storage/ui.js b/devtools/client/storage/ui.js index 6af493e44..c4dd57c6e 100644 --- a/devtools/client/storage/ui.js +++ b/devtools/client/storage/ui.js @@ -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); diff --git a/devtools/server/actors/storage.js b/devtools/server/actors/storage.js index 572cd6b68..894e282f0 100644 --- a/devtools/server/actors/storage.js +++ b/devtools/server/actors/storage.js @@ -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] && diff --git a/devtools/server/tests/browser/browser.ini b/devtools/server/tests/browser/browser.ini index c05933230..b7929e2b0 100644 --- a/devtools/server/tests/browser/browser.ini +++ b/devtools/server/tests/browser/browser.ini @@ -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] diff --git a/devtools/server/tests/browser/browser_storage_cookies-duplicate-names.js b/devtools/server/tests/browser/browser_storage_cookies-duplicate-names.js new file mode 100644 index 000000000..c1cf0aa72 --- /dev/null +++ b/devtools/server/tests/browser/browser_storage_cookies-duplicate-names.js @@ -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); +}); diff --git a/devtools/server/tests/browser/browser_storage_dynamic_windows.js b/devtools/server/tests/browser/browser_storage_dynamic_windows.js index 440c91222..91b4155d9 100644 --- a/devtools/server/tests/browser/browser_storage_dynamic_windows.js +++ b/devtools/server/tests/browser/browser_storage_dynamic_windows.js @@ -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": [] } }; diff --git a/devtools/server/tests/browser/browser_storage_listings.js b/devtools/server/tests/browser/browser_storage_listings.js index 4ff3c3fc1..2e4bd00a4 100644 --- a/devtools/server/tests/browser/browser_storage_listings.js +++ b/devtools/server/tests/browser/browser_storage_listings.js @@ -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, diff --git a/devtools/server/tests/browser/browser_storage_updates.js b/devtools/server/tests/browser/browser_storage_updates.js index 28b2e509f..01a35cefc 100644 --- a/devtools/server/tests/browser/browser_storage_updates.js +++ b/devtools/server/tests/browser/browser_storage_updates.js @@ -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/"), + ] } } } diff --git a/devtools/server/tests/browser/head.js b/devtools/server/tests/browser/head.js index 1e7f09d95..5cf98c2b0 100644 --- a/devtools/server/tests/browser/head.js +++ b/devtools/server/tests/browser/head.js @@ -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}`; +} diff --git a/devtools/server/tests/browser/storage-cookies-same-name.html b/devtools/server/tests/browser/storage-cookies-same-name.html new file mode 100644 index 000000000..e3e092ec3 --- /dev/null +++ b/devtools/server/tests/browser/storage-cookies-same-name.html @@ -0,0 +1,28 @@ + + + + + Storage inspector cookies with duplicate names + + + + + -- cgit v1.2.3 From 43ddb9b8c08ac148a9b03f16f45ec2cb71243f81 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Fri, 2 Mar 2018 14:33:20 +0100 Subject: Bug 1276339: Storage inspector doesn't work on chrome:// pages and web extensions Issue #31 --- devtools/client/locales/en-US/storage.properties | 2 +- devtools/client/storage/test/browser.ini | 2 + .../client/storage/test/browser_storage_basic.js | 22 +- .../client/storage/test/browser_storage_delete.js | 2 +- .../storage/test/browser_storage_delete_all.js | 6 +- .../storage/test/browser_storage_delete_tree.js | 6 +- .../test/browser_storage_empty_objectstores.js | 10 +- .../test/browser_storage_indexeddb_delete.js | 6 +- .../browser_storage_indexeddb_delete_blocked.js | 8 +- .../browser_storage_indexeddb_duplicate_names.js | 31 +++ .../client/storage/test/browser_storage_sidebar.js | 6 +- .../client/storage/test/browser_storage_values.js | 4 +- devtools/client/storage/test/head.js | 16 ++ .../test/storage-indexeddb-duplicate-names.html | 50 ++++ devtools/client/storage/ui.js | 5 +- devtools/server/actors/storage.js | 280 +++++++++++++++------ .../tests/browser/browser_storage_listings.js | 32 +-- devtools/shared/specs/storage.js | 2 +- 18 files changed, 361 insertions(+), 129 deletions(-) create mode 100644 devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js create mode 100644 devtools/client/storage/test/storage-indexeddb-duplicate-names.html (limited to 'devtools') diff --git a/devtools/client/locales/en-US/storage.properties b/devtools/client/locales/en-US/storage.properties index fd3b3ec52..3efd7c84a 100644 --- a/devtools/client/locales/en-US/storage.properties +++ b/devtools/client/locales/en-US/storage.properties @@ -60,7 +60,7 @@ table.headers.indexedDB.value=Value table.headers.indexedDB.origin=Origin table.headers.indexedDB.version=Version table.headers.indexedDB.objectStores=Object Stores -table.headers.indexedDB.keyPath=Key +table.headers.indexedDB.keyPath2=Key Path table.headers.indexedDB.autoIncrement=Auto Increment table.headers.indexedDB.indexes=Indexes diff --git a/devtools/client/storage/test/browser.ini b/devtools/client/storage/test/browser.ini index 0ac28a92d..08ee7f176 100644 --- a/devtools/client/storage/test/browser.ini +++ b/devtools/client/storage/test/browser.ini @@ -7,6 +7,7 @@ support-files = storage-cookies.html storage-empty-objectstores.html storage-idb-delete-blocked.html + storage-indexeddb-duplicate-names.html storage-listings.html storage-localstorage.html storage-overflow.html @@ -35,6 +36,7 @@ support-files = [browser_storage_empty_objectstores.js] [browser_storage_indexeddb_delete.js] [browser_storage_indexeddb_delete_blocked.js] +[browser_storage_indexeddb_duplicate_names.js] [browser_storage_localstorage_edit.js] [browser_storage_localstorage_error.js] [browser_storage_overflow.js] diff --git a/devtools/client/storage/test/browser_storage_basic.js b/devtools/client/storage/test/browser_storage_basic.js index 10eb8d0ca..72582e32f 100644 --- a/devtools/client/storage/test/browser_storage_basic.js +++ b/devtools/client/storage/test/browser_storage_basic.js @@ -53,28 +53,28 @@ const testCases = [ [["sessionStorage", "https://sectest1.example.org"], ["iframe-s-ss1"]], [["indexedDB", "http://test1.example.org"], - ["idb1", "idb2"]], - [["indexedDB", "http://test1.example.org", "idb1"], + ["idb1 (default)", "idb2 (default)"]], + [["indexedDB", "http://test1.example.org", "idb1 (default)"], ["obj1", "obj2"]], - [["indexedDB", "http://test1.example.org", "idb2"], + [["indexedDB", "http://test1.example.org", "idb2 (default)"], ["obj3"]], - [["indexedDB", "http://test1.example.org", "idb1", "obj1"], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], [1, 2, 3]], - [["indexedDB", "http://test1.example.org", "idb1", "obj2"], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj2"], [1]], - [["indexedDB", "http://test1.example.org", "idb2", "obj3"], + [["indexedDB", "http://test1.example.org", "idb2 (default)", "obj3"], []], [["indexedDB", "http://sectest1.example.org"], []], [["indexedDB", "https://sectest1.example.org"], - ["idb-s1", "idb-s2"]], - [["indexedDB", "https://sectest1.example.org", "idb-s1"], + ["idb-s1 (default)", "idb-s2 (default)"]], + [["indexedDB", "https://sectest1.example.org", "idb-s1 (default)"], ["obj-s1"]], - [["indexedDB", "https://sectest1.example.org", "idb-s2"], + [["indexedDB", "https://sectest1.example.org", "idb-s2 (default)"], ["obj-s2"]], - [["indexedDB", "https://sectest1.example.org", "idb-s1", "obj-s1"], + [["indexedDB", "https://sectest1.example.org", "idb-s1 (default)", "obj-s1"], [6, 7]], - [["indexedDB", "https://sectest1.example.org", "idb-s2", "obj-s2"], + [["indexedDB", "https://sectest1.example.org", "idb-s2 (default)", "obj-s2"], [16]], [["Cache", "http://test1.example.org", "plop"], [MAIN_DOMAIN + "404_cached_file.js", diff --git a/devtools/client/storage/test/browser_storage_delete.js b/devtools/client/storage/test/browser_storage_delete.js index d5cb59a10..a3ef7c290 100644 --- a/devtools/client/storage/test/browser_storage_delete.js +++ b/devtools/client/storage/test/browser_storage_delete.js @@ -17,7 +17,7 @@ const TEST_CASES = [ ["cookies", "test1.example.org"], getCookieId("c1", "test1.example.org", "/browser"), "name" ], - [["indexedDB", "http://test1.example.org", "idb1", "obj1"], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], 1, "name"], [["Cache", "http://test1.example.org", "plop"], MAIN_DOMAIN + "404_cached_file.js", "url"], diff --git a/devtools/client/storage/test/browser_storage_delete_all.js b/devtools/client/storage/test/browser_storage_delete_all.js index c4b6048fb..60b417bdb 100644 --- a/devtools/client/storage/test/browser_storage_delete_all.js +++ b/devtools/client/storage/test/browser_storage_delete_all.js @@ -29,7 +29,7 @@ add_task(function* () { ["iframe-u-ss1", "iframe-u-ss2"]], [["sessionStorage", "https://sectest1.example.org"], ["iframe-s-ss1"]], - [["indexedDB", "http://test1.example.org", "idb1", "obj1"], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], [1, 2, 3]], [["Cache", "http://test1.example.org", "plop"], [MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]], @@ -41,7 +41,7 @@ add_task(function* () { const deleteHosts = [ [["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"], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], 1, "name"], [["Cache", "http://test1.example.org", "plop"], MAIN_DOMAIN + "404_cached_file.js", "url"], ]; @@ -78,7 +78,7 @@ add_task(function* () { ["iframe-u-ss1", "iframe-u-ss2"]], [["sessionStorage", "https://sectest1.example.org"], []], - [["indexedDB", "http://test1.example.org", "idb1", "obj1"], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], []], [["Cache", "http://test1.example.org", "plop"], []], diff --git a/devtools/client/storage/test/browser_storage_delete_tree.js b/devtools/client/storage/test/browser_storage_delete_tree.js index 3e866a2f5..0a7a7e645 100644 --- a/devtools/client/storage/test/browser_storage_delete_tree.js +++ b/devtools/client/storage/test/browser_storage_delete_tree.js @@ -28,7 +28,7 @@ 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]], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], [1, 2, 3]], [["Cache", "http://test1.example.org", "plop"], [MAIN_DOMAIN + "404_cached_file.js", MAIN_DOMAIN + "browser_storage_basic.js"]], ]); @@ -38,7 +38,7 @@ add_task(function* () { ["cookies", "test1.example.org"], ["localStorage", "http://test1.example.org"], ["sessionStorage", "http://test1.example.org"], - ["indexedDB", "http://test1.example.org", "idb1", "obj1"], + ["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], ["Cache", "http://test1.example.org", "plop"], ]; @@ -67,7 +67,7 @@ add_task(function* () { [["cookies", "test1.example.org"], []], [["localStorage", "http://test1.example.org"], []], [["sessionStorage", "http://test1.example.org"], []], - [["indexedDB", "http://test1.example.org", "idb1", "obj1"], []], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], []], [["Cache", "http://test1.example.org", "plop"], []], ]); diff --git a/devtools/client/storage/test/browser_storage_empty_objectstores.js b/devtools/client/storage/test/browser_storage_empty_objectstores.js index 1749c91b8..e6f259742 100644 --- a/devtools/client/storage/test/browser_storage_empty_objectstores.js +++ b/devtools/client/storage/test/browser_storage_empty_objectstores.js @@ -21,14 +21,14 @@ // storage-secured-iframe.html and storage-unsecured-iframe.html const storeItems = [ [["indexedDB", "http://test1.example.org"], - ["idb1", "idb2"]], - [["indexedDB", "http://test1.example.org", "idb1"], + ["idb1 (default)", "idb2 (default)"]], + [["indexedDB", "http://test1.example.org", "idb1 (default)"], ["obj1", "obj2"]], - [["indexedDB", "http://test1.example.org", "idb2"], + [["indexedDB", "http://test1.example.org", "idb2 (default)"], []], - [["indexedDB", "http://test1.example.org", "idb1", "obj1"], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], [1, 2, 3]], - [["indexedDB", "http://test1.example.org", "idb1", "obj2"], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj2"], [1]] ]; diff --git a/devtools/client/storage/test/browser_storage_indexeddb_delete.js b/devtools/client/storage/test/browser_storage_indexeddb_delete.js index 18a0daf69..5c499c9e9 100644 --- a/devtools/client/storage/test/browser_storage_indexeddb_delete.js +++ b/devtools/client/storage/test/browser_storage_indexeddb_delete.js @@ -16,11 +16,11 @@ add_task(function* () { info("test state before delete"); yield checkState([ - [["indexedDB", "http://test1.example.org"], ["idb1", "idb2"]], + [["indexedDB", "http://test1.example.org"], ["idb1 (default)", "idb2 (default)"]], ]); info("do the delete"); - const deletedDb = ["indexedDB", "http://test1.example.org", "idb1"]; + const deletedDb = ["indexedDB", "http://test1.example.org", "idb1 (default)"]; yield selectTreeItem(deletedDb); @@ -40,7 +40,7 @@ add_task(function* () { info("test state after delete"); yield checkState([ - [["indexedDB", "http://test1.example.org"], ["idb2"]], + [["indexedDB", "http://test1.example.org"], ["idb2 (default)"]], ]); yield finishTests(); diff --git a/devtools/client/storage/test/browser_storage_indexeddb_delete_blocked.js b/devtools/client/storage/test/browser_storage_indexeddb_delete_blocked.js index 6e89c4f28..f87f15d82 100644 --- a/devtools/client/storage/test/browser_storage_indexeddb_delete_blocked.js +++ b/devtools/client/storage/test/browser_storage_indexeddb_delete_blocked.js @@ -13,19 +13,19 @@ add_task(function* () { info("test state before delete"); yield checkState([ - [["indexedDB", "http://test1.example.org"], ["idb"]] + [["indexedDB", "http://test1.example.org"], ["idb (default)"]] ]); info("do the delete"); yield selectTreeItem(["indexedDB", "http://test1.example.org"]); let actor = gUI.getCurrentActor(); - let result = yield actor.removeDatabase("http://test1.example.org", "idb"); + let result = yield actor.removeDatabase("http://test1.example.org", "idb (default)"); ok(result.blocked, "removeDatabase attempt is blocked"); info("test state after blocked delete"); yield checkState([ - [["indexedDB", "http://test1.example.org"], ["idb"]] + [["indexedDB", "http://test1.example.org"], ["idb (default)"]] ]); let eventWait = gUI.once("store-objects-updated"); @@ -47,7 +47,7 @@ add_task(function* () { info("try to delete database from nonexistent host"); let errorThrown = false; try { - result = yield actor.removeDatabase("http://test2.example.org", "idb"); + result = yield actor.removeDatabase("http://test2.example.org", "idb (default)"); } catch (ex) { errorThrown = true; } diff --git a/devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js b/devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js new file mode 100644 index 000000000..8316d22c5 --- /dev/null +++ b/devtools/client/storage/test/browser_storage_indexeddb_duplicate_names.js @@ -0,0 +1,31 @@ +/* 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/. */ + +// Test to verify that indexedDBs with duplicate names (different types / paths) +// work as expected. + +"use strict"; + +add_task(function* () { + const TESTPAGE = MAIN_DOMAIN + "storage-indexeddb-duplicate-names.html"; + + setPermission(TESTPAGE, "indexedDB"); + + yield openTabAndSetupStorage(TESTPAGE); + + yield checkState([ + [ + ["indexedDB", "http://test1.example.org"], [ + "idb1 (default)", + "idb1 (temporary)", + "idb1 (persistent)", + "idb2 (default)", + "idb2 (temporary)", + "idb2 (persistent)" + ] + ] + ]); + + yield finishTests(); +}); diff --git a/devtools/client/storage/test/browser_storage_sidebar.js b/devtools/client/storage/test/browser_storage_sidebar.js index d1a71c667..6712ac013 100644 --- a/devtools/client/storage/test/browser_storage_sidebar.js +++ b/devtools/client/storage/test/browser_storage_sidebar.js @@ -72,17 +72,17 @@ const testCases = [ sidebarHidden: true }, { - location: "idb2", + location: "idb2 (default)", sidebarHidden: false }, { - location: ["indexedDB", "http://test1.example.org", "idb2", "obj3"], + location: ["indexedDB", "http://test1.example.org", "idb2 (default)", "obj3"], sidebarHidden: true }, { - location: ["indexedDB", "https://sectest1.example.org", "idb-s2"], + location: ["indexedDB", "https://sectest1.example.org", "idb-s2 (default)"], sidebarHidden: true }, { diff --git a/devtools/client/storage/test/browser_storage_values.js b/devtools/client/storage/test/browser_storage_values.js index 122a867bb..1d3e9ff76 100644 --- a/devtools/client/storage/test/browser_storage_values.js +++ b/devtools/client/storage/test/browser_storage_values.js @@ -124,7 +124,7 @@ const testCases = [ {name: "ss5.3", value: `${LONG_WORD}&${LONG_WORD}`}, {name: "ss5.4", value: `${LONG_WORD}&${LONG_WORD}`}, ], true], - [["indexedDB", "http://test1.example.org", "idb1", "obj1"]], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"]], [1, [ {name: 1, value: JSON.stringify({id: 1, name: "foo", email: "foo@bar.com"})} ]], @@ -133,7 +133,7 @@ const testCases = [ {name: "1.name", value: "foo"}, {name: "1.email", value: "foo@bar.com"}, ], true], - [["indexedDB", "http://test1.example.org", "idb1", "obj2"]], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj2"]], [1, [ {name: 1, value: JSON.stringify({ id2: 1, name: "foo", email: "foo@bar.com", extra: "baz" diff --git a/devtools/client/storage/test/head.js b/devtools/client/storage/test/head.js index 894056c9e..0d7d13e17 100644 --- a/devtools/client/storage/test/head.js +++ b/devtools/client/storage/test/head.js @@ -879,3 +879,19 @@ var focusSearchBoxUsingShortcut = Task.async(function* (panelWin, callback) { function getCookieId(name, domain, path) { return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}`; } + +function setPermission(url, permission) { + const nsIPermissionManager = Components.interfaces.nsIPermissionManager; + + let uri = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService) + .newURI(url, null, null); + let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"] + .getService(Ci.nsIScriptSecurityManager); + let principal = ssm.createCodebasePrincipal(uri, {}); + + Components.classes["@mozilla.org/permissionmanager;1"] + .getService(nsIPermissionManager) + .addFromPrincipal(principal, permission, + nsIPermissionManager.ALLOW_ACTION); +} diff --git a/devtools/client/storage/test/storage-indexeddb-duplicate-names.html b/devtools/client/storage/test/storage-indexeddb-duplicate-names.html new file mode 100644 index 000000000..d8c76dc2a --- /dev/null +++ b/devtools/client/storage/test/storage-indexeddb-duplicate-names.html @@ -0,0 +1,50 @@ + + + + + Storage inspector IndexedDBs with duplicate names + + + + +

storage-indexeddb-duplicate-names.html

+ + diff --git a/devtools/client/storage/ui.js b/devtools/client/storage/ui.js index c4dd57c6e..1241d0120 100644 --- a/devtools/client/storage/ui.js +++ b/devtools/client/storage/ui.js @@ -809,7 +809,10 @@ StorageUI.prototype = { columns[f.name] = f.name; let columnName; try { - columnName = L10N.getStr("table.headers." + type + "." + f.name); + // Path key names for l10n in the case of a string change. + let name = f.name === "keyPath" ? "keyPath2" : f.name; + + columnName = L10N.getStr("table.headers." + type + "." + name); } catch (e) { columnName = COOKIE_KEY_MAP[f.name]; } diff --git a/devtools/server/actors/storage.js b/devtools/server/actors/storage.js index 894e282f0..22f4eaabe 100644 --- a/devtools/server/actors/storage.js +++ b/devtools/server/actors/storage.js @@ -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/. */ +/* globals StopIteration */ + "use strict"; const {Cc, Ci, Cu, CC} = require("chrome"); @@ -93,7 +95,7 @@ var StorageActors = {}; * - observe : Method which gets triggered on the notificaiton of the watched * topic. * - getNamesForHost : Given a host, get list of all known store names. - * - getValuesForHost : Given a host (and optianally a name) get all known + * - getValuesForHost : Given a host (and optionally a name) get all known * store objects. * - toStoreObject : Given a store object, convert it to the required format * so that it can be transferred over wire. @@ -141,6 +143,9 @@ StorageActors.defaults = function (typeName, observationTopic) { * Converts the window.location object into host. */ getHostName(location) { + if (location.protocol === "chrome:") { + return location.href; + } return location.hostname || location.href; }, @@ -744,6 +749,7 @@ var cookieHelpers = { let enumerator = Services.cookies.getCookiesFromHost(origHost, data.originAttributes || {}); + while (enumerator.hasMoreElements()) { let nsiCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2); if (nsiCookie.name === origName && @@ -1041,6 +1047,9 @@ function getObjectForLocalOrSessionStorage(type) { if (!location.host) { return location.href; } + if (location.protocol === "chrome:") { + return location.href; + } return location.protocol + "//" + location.host; }, @@ -1251,6 +1260,9 @@ StorageActors.createActor({ if (!location.host) { return location.href; } + if (location.protocol === "chrome:") { + return location.href; + } return location.protocol + "//" + location.host; }, @@ -1423,12 +1435,15 @@ ObjectStoreMetadata.prototype = { * The host associated with this indexed db. * @param {IDBDatabase} db * The particular indexed db. + * @param {String} storage + * Storage type, either "temporary", "default" or "persistent". */ -function DatabaseMetadata(origin, db) { +function DatabaseMetadata(origin, db, storage) { this._origin = origin; this._name = db.name; this._version = db.version; this._objectStores = []; + this.storage = storage; if (db.objectStoreNames.length) { let transaction = db.transaction(db.objectStoreNames, "readonly"); @@ -1448,7 +1463,7 @@ DatabaseMetadata.prototype = { toObject() { return { - name: this._name, + name: `${this._name} (${this.storage})`, origin: this._origin, version: this._version, objectStores: this._objectStores.size @@ -1524,6 +1539,9 @@ StorageActors.createActor({ if (!location.host) { return location.href; } + if (location.protocol === "chrome:") { + return location.href; + } return location.protocol + "//" + location.host; }, @@ -1616,15 +1634,17 @@ StorageActors.createActor({ populateStoresForHost: Task.async(function* (host) { let storeMap = new Map(); let {names} = yield this.getDBNamesForHost(host); + let win = this.storageActor.getWindowFromHost(host); if (win) { let principal = win.document.nodePrincipal; - for (let name of names) { - let metadata = yield this.getDBMetaData(host, principal, name); + for (let {name, storage} of names) { + let metadata = yield this.getDBMetaData(host, principal, name, storage); metadata = indexedDBHelpers.patchMetadataMapsAndProtos(metadata); - storeMap.set(name, metadata); + + storeMap.set(`${name} (${storage})`, metadata); } } @@ -1657,10 +1677,22 @@ StorageActors.createActor({ objectStores: item.objectStores }; } + + let value = JSON.stringify(item.value); + + // FIXME: Bug 1318029 - Due to a bug that is thrown whenever a + // LongStringActor string reaches DebuggerServer.LONG_STRING_LENGTH we need + // to trim the value. When the bug is fixed we should stop trimming the + // string here. + let maxLength = DebuggerServer.LONG_STRING_LENGTH - 1; + if (value.length > maxLength) { + value = value.substr(0, maxLength); + } + // Indexed db entry return { name: item.name, - value: new LongStringActor(this.conn, JSON.stringify(item.value)) + value: new LongStringActor(this.conn, value) }; }, @@ -1696,16 +1728,18 @@ StorageActors.createActor({ maybeSetupChildProcess() { if (!DebuggerServer.isInChildProcess) { this.backToChild = (func, rv) => rv; + this.clearDBStore = indexedDBHelpers.clearDBStore; + this.gatherFilesOrFolders = indexedDBHelpers.gatherFilesOrFolders; this.getDBMetaData = indexedDBHelpers.getDBMetaData; - this.openWithPrincipal = indexedDBHelpers.openWithPrincipal; this.getDBNamesForHost = indexedDBHelpers.getDBNamesForHost; - this.getSanitizedHost = indexedDBHelpers.getSanitizedHost; this.getNameFromDatabaseFile = indexedDBHelpers.getNameFromDatabaseFile; - this.getValuesForHost = indexedDBHelpers.getValuesForHost; this.getObjectStoreData = indexedDBHelpers.getObjectStoreData; + this.getSanitizedHost = indexedDBHelpers.getSanitizedHost; + this.getValuesForHost = indexedDBHelpers.getValuesForHost; + this.openWithPrincipal = indexedDBHelpers.openWithPrincipal; this.removeDB = indexedDBHelpers.removeDB; this.removeDBRecord = indexedDBHelpers.removeDBRecord; - this.clearDBStore = indexedDBHelpers.clearDBStore; + this.splitNameAndStorage = indexedDBHelpers.splitNameAndStorage; return; } @@ -1718,6 +1752,7 @@ StorageActors.createActor({ }); this.getDBMetaData = callParentProcessAsync.bind(null, "getDBMetaData"); + this.splitNameAndStorage = callParentProcessAsync.bind(null, "splitNameAndStorage"); this.getDBNamesForHost = callParentProcessAsync.bind(null, "getDBNamesForHost"); this.getValuesForHost = callParentProcessAsync.bind(null, "getValuesForHost"); this.removeDB = callParentProcessAsync.bind(null, "removeDB"); @@ -1813,14 +1848,14 @@ var indexedDBHelpers = { * `name` for the given `host` with its `principal`. The stored metadata * information is of `DatabaseMetadata` type. */ - getDBMetaData: Task.async(function* (host, principal, name) { - let request = this.openWithPrincipal(principal, name); + getDBMetaData: Task.async(function* (host, principal, name, storage) { + let request = this.openWithPrincipal(principal, name, storage); let success = promise.defer(); request.onsuccess = event => { let db = event.target.result; - let dbData = new DatabaseMetadata(host, db); + let dbData = new DatabaseMetadata(host, db, storage); db.close(); success.resolve(this.backToChild("getDBMetaData", dbData)); @@ -1833,21 +1868,37 @@ var indexedDBHelpers = { return success.promise; }), + splitNameAndStorage: function (name) { + let lastOpenBracketIndex = name.lastIndexOf("("); + let lastCloseBracketIndex = name.lastIndexOf(")"); + let delta = lastCloseBracketIndex - lastOpenBracketIndex - 1; + + let storage = name.substr(lastOpenBracketIndex + 1, delta); + + name = name.substr(0, lastOpenBracketIndex - 1); + + return { storage, name }; + }, + /** * Opens an indexed db connection for the given `principal` and * database `name`. */ - openWithPrincipal(principal, name) { - return indexedDBForStorage.openForPrincipal(principal, name); + openWithPrincipal: function (principal, name, storage) { + return indexedDBForStorage.openForPrincipal(principal, name, + { storage: storage }); }, - removeDB: Task.async(function* (host, principal, name) { + removeDB: Task.async(function* (host, principal, dbName) { let result = new promise(resolve => { - let request = indexedDBForStorage.deleteForPrincipal(principal, name); + let {name, storage} = this.splitNameAndStorage(dbName); + let request = + indexedDBForStorage.deleteForPrincipal(principal, name, + { storage: storage }); request.onsuccess = () => { resolve({}); - this.onItemUpdated("deleted", host, [name]); + this.onItemUpdated("deleted", host, [dbName]); }; request.onblocked = () => { @@ -1873,10 +1924,11 @@ var indexedDBHelpers = { removeDBRecord: Task.async(function* (host, principal, dbName, storeName, id) { let db; + let {name, storage} = this.splitNameAndStorage(dbName); try { db = yield new promise((resolve, reject) => { - let request = this.openWithPrincipal(principal, dbName); + let request = this.openWithPrincipal(principal, name, storage); request.onsuccess = ev => resolve(ev.target.result); request.onerror = ev => reject(ev.target.error); }); @@ -1905,10 +1957,11 @@ var indexedDBHelpers = { clearDBStore: Task.async(function* (host, principal, dbName, storeName) { let db; + let {name, storage} = this.splitNameAndStorage(dbName); try { db = yield new promise((resolve, reject) => { - let request = this.openWithPrincipal(principal, dbName); + let request = this.openWithPrincipal(principal, name, storage); request.onsuccess = ev => resolve(ev.target.result); request.onerror = ev => reject(ev.target.error); }); @@ -1940,46 +1993,106 @@ var indexedDBHelpers = { */ getDBNamesForHost: Task.async(function* (host) { let sanitizedHost = this.getSanitizedHost(host); - let directory = OS.Path.join(OS.Constants.Path.profileDir, "storage", - "default", sanitizedHost, "idb"); + let profileDir = OS.Constants.Path.profileDir; + let files = []; + let names = []; + let storagePath = OS.Path.join(profileDir, "storage"); + + // We expect sqlite DB paths to look something like this: + // - PathToProfileDir/storage/default/http+++www.example.com/ + // idb/1556056096MeysDaabta.sqlite + // - PathToProfileDir/storage/permanent/http+++www.example.com/ + // idb/1556056096MeysDaabta.sqlite + // - PathToProfileDir/storage/temporary/http+++www.example.com/ + // idb/1556056096MeysDaabta.sqlite + // + // The subdirectory inside the storage folder is determined by the storage + // type: + // - default: { storage: "default" } or not specified. + // - permanent: { storage: "persistent" }. + // - temporary: { storage: "temporary" }. + let sqliteFiles = yield this.gatherFilesOrFolders(storagePath, path => { + if (path.endsWith(".sqlite")) { + let { components } = OS.Path.split(path); + let isIDB = components[components.length - 2] === "idb"; + + return isIDB; + } + return false; + }); - let exists = yield OS.File.exists(directory); - if (!exists && host.startsWith("about:")) { - // try for moz-safe-about directory - sanitizedHost = this.getSanitizedHost("moz-safe-" + host); - directory = OS.Path.join(OS.Constants.Path.profileDir, "storage", - "permanent", sanitizedHost, "idb"); - exists = yield OS.File.exists(directory); - } - if (!exists) { - return this.backToChild("getDBNamesForHost", {names: []}); + for (let file of sqliteFiles) { + let splitPath = OS.Path.split(file).components; + let idbIndex = splitPath.indexOf("idb"); + let name = splitPath[idbIndex - 1]; + let storage = splitPath[idbIndex - 2]; + let relative = file.substr(profileDir.length + 1); + + if (name.startsWith(sanitizedHost)) { + files.push({ + file: relative, + storage: storage === "permanent" ? "persistent" : storage + }); + } } - let names = []; - let dirIterator = new OS.File.DirectoryIterator(directory); - try { - yield dirIterator.forEach(file => { - // Skip directories. - if (file.isDir) { - return null; + if (files.length > 0) { + for (let {file, storage} of files) { + let name = yield this.getNameFromDatabaseFile(file); + if (name) { + names.push({ + name, + storage + }); } + } + } + return this.backToChild("getDBNamesForHost", {names}); + }), - // Skip any non-sqlite files. - if (!file.name.endsWith(".sqlite")) { - return null; - } + /** + * Gather together all of the files in path and pass each path through a + * validation function. + * + * @param {String} + * Path in which to begin searching. + * @param {Function} + * Validation function, which checks each file path. If this function + * Returns true the file path is kept. + * + * @returns {Array} + * An array of file paths. + */ + gatherFilesOrFolders: Task.async(function* (path, validationFunc) { + let files = []; + let iterator; + let paths = [path]; + + while (paths.length > 0) { + try { + iterator = new OS.File.DirectoryIterator(paths.pop()); - return this.getNameFromDatabaseFile(file.path).then(name => { - if (name) { - names.push(name); + for (let child in iterator) { + child = yield child; + + path = child.path; + + if (child.isDir) { + paths.push(path); + } else if (validationFunc(path)) { + files.push(path); } - return null; - }); - }); - } finally { - dirIterator.close(); + } + } catch (ex) { + // Ignore StopIteration to prevent exiting the loop. + if (ex != StopIteration) { + throw ex; + } + } } - return this.backToChild("getDBNamesForHost", {names: names}); + iterator.close(); + + return files; }), /** @@ -1987,6 +2100,9 @@ var indexedDBHelpers = { * name. */ getSanitizedHost(host) { + if (host.startsWith("about:")) { + host = "moz-safe-" + host; + } return host.replace(ILLEGAL_CHAR_REGEX, "+"); }, @@ -2000,7 +2116,7 @@ var indexedDBHelpers = { // Content pages might be having an open transaction for the same indexed db // which this sqlite file belongs to. In that case, sqlite.openConnection - // will throw. Thus we retey for some time to see if lock is removed. + // will throw. Thus we retry for some time to see if lock is removed. while (!connection && retryCount++ < 25) { try { connection = yield Sqlite.openConnection({ path: path }); @@ -2061,8 +2177,14 @@ var indexedDBHelpers = { return this.backToChild("getValuesForHost", {objectStores: objectStores}); } // Get either all entries from the object store, or a particular id - let result = yield this.getObjectStoreData(host, principal, db2, - objectStore, id, options.index, options.size); + let storage = hostVsStores.get(host).get(db2).storage; + let result = yield this.getObjectStoreData(host, principal, db2, storage, { + objectStore: objectStore, + id: id, + index: options.index, + offset: 0, + size: options.size + }); return this.backToChild("getValuesForHost", {result: result}); }), @@ -2076,23 +2198,27 @@ var indexedDBHelpers = { * The principal of the given document. * @param {string} dbName * The name of the indexed db from the above host. - * @param {string} objectStore - * The name of the object store from the above db. - * @param {string} id - * id of the requested entry from the above object store. - * null if all entries from the above object store are requested. - * @param {string} index - * name of the IDBIndex to be iterated on while fetching entries. - * null or "name" if no index is to be iterated. - * @param {number} offset - * ofsset of the entries to be fetched. - * @param {number} size - * The intended size of the entries to be fetched. + * @param {String} storage + * Storage type, either "temporary", "default" or "persistent". + * @param {Object} requestOptions + * An object in the following format: + * { + * objectStore: The name of the object store from the above db, + * id: Id of the requested entry from the above object + * store. null if all entries from the above object + * store are requested, + * index: Name of the IDBIndex to be iterated on while fetching + * entries. null or "name" if no index is to be + * iterated, + * offset: offset of the entries to be fetched, + * size: The intended size of the entries to be fetched + * } */ - getObjectStoreData(host, principal, dbName, objectStore, id, index, - offset, size) { - let request = this.openWithPrincipal(principal, dbName); + getObjectStoreData(host, principal, dbName, storage, requestOptions) { + let {name} = this.splitNameAndStorage(dbName); + let request = this.openWithPrincipal(principal, name, storage); let success = promise.defer(); + let {objectStore, id, index, offset, size} = requestOptions; let data = []; let db; @@ -2194,8 +2320,12 @@ var indexedDBHelpers = { switch (msg.json.method) { case "getDBMetaData": { - let [host, principal, name] = args; - return indexedDBHelpers.getDBMetaData(host, principal, name); + let [host, principal, name, storage] = args; + return indexedDBHelpers.getDBMetaData(host, principal, name, storage); + } + case "splitNameAndStorage": { + let [name] = args; + return indexedDBHelpers.splitNameAndStorage(name); } case "getDBNamesForHost": { let [host] = args; @@ -2207,8 +2337,8 @@ var indexedDBHelpers = { hostVsStores, principal); } case "removeDB": { - let [host, principal, name] = args; - return indexedDBHelpers.removeDB(host, principal, name); + let [host, principal, dbName] = args; + return indexedDBHelpers.removeDB(host, principal, dbName); } case "removeDBRecord": { let [host, principal, db, store, id] = args; diff --git a/devtools/server/tests/browser/browser_storage_listings.js b/devtools/server/tests/browser/browser_storage_listings.js index 2e4bd00a4..15e5ccd50 100644 --- a/devtools/server/tests/browser/browser_storage_listings.js +++ b/devtools/server/tests/browser/browser_storage_listings.js @@ -121,24 +121,24 @@ const storeMap = { const IDBValues = { listStoresResponse: { "http://test1.example.org": [ - ["idb1", "obj1"], ["idb1", "obj2"], ["idb2", "obj3"] + ["idb1 (default)", "obj1"], ["idb1 (default)", "obj2"], ["idb2 (default)", "obj3"] ], "http://sectest1.example.org": [ ], "https://sectest1.example.org": [ - ["idb-s1", "obj-s1"], ["idb-s2", "obj-s2"] + ["idb-s1 (default)", "obj-s1"], ["idb-s2 (default)", "obj-s2"] ] }, - dbDetails : { + dbDetails: { "http://test1.example.org": [ { - db: "idb1", + db: "idb1 (default)", origin: "http://test1.example.org", version: 1, objectStores: 2 }, { - db: "idb2", + db: "idb2 (default)", origin: "http://test1.example.org", version: 1, objectStores: 1 @@ -148,13 +148,13 @@ const IDBValues = { ], "https://sectest1.example.org": [ { - db: "idb-s1", + db: "idb-s1 (default)", origin: "https://sectest1.example.org", version: 1, objectStores: 1 }, { - db: "idb-s2", + db: "idb-s2 (default)", origin: "https://sectest1.example.org", version: 1, objectStores: 1 @@ -163,7 +163,7 @@ const IDBValues = { }, objectStoreDetails: { "http://test1.example.org": { - idb1: [ + "idb1 (default)": [ { objectStore: "obj1", keyPath: "id", @@ -190,7 +190,7 @@ const IDBValues = { indexes: [] } ], - idb2: [ + "idb2 (default)": [ { objectStore: "obj3", keyPath: "id3", @@ -208,7 +208,7 @@ const IDBValues = { }, "http://sectest1.example.org" : {}, "https://sectest1.example.org": { - "idb-s1": [ + "idb-s1 (default)": [ { objectStore: "obj-s1", keyPath: "id", @@ -216,7 +216,7 @@ const IDBValues = { indexes: [] }, ], - "idb-s2": [ + "idb-s2 (default)": [ { objectStore: "obj-s2", keyPath: "id3", @@ -236,7 +236,7 @@ const IDBValues = { }, entries: { "http://test1.example.org": { - "idb1#obj1": [ + "idb1 (default)#obj1": [ { name: 1, value: { @@ -262,7 +262,7 @@ const IDBValues = { } } ], - "idb1#obj2": [ + "idb1 (default)#obj2": [ { name: 1, value: { @@ -273,11 +273,11 @@ const IDBValues = { } } ], - "idb2#obj3": [] + "idb2 (default)#obj3": [] }, "http://sectest1.example.org" : {}, "https://sectest1.example.org": { - "idb-s1#obj-s1": [ + "idb-s1 (default)#obj-s1": [ { name: 6, value: { @@ -295,7 +295,7 @@ const IDBValues = { } } ], - "idb-s2#obj-s2": [ + "idb-s2 (default)#obj-s2": [ { name: 13, value: { diff --git a/devtools/shared/specs/storage.js b/devtools/shared/specs/storage.js index d6ddaefe5..3674992ed 100644 --- a/devtools/shared/specs/storage.js +++ b/devtools/shared/specs/storage.js @@ -61,7 +61,7 @@ types.addDictType("cookiestoreobject", { // Common methods for edit/remove const editRemoveMethods = { - getEditableFields: { + getFields: { request: {}, response: { value: RetVal("json") -- cgit v1.2.3 From 71fd51a863171d9f462d1311749de717361406a6 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Fri, 2 Mar 2018 14:59:24 +0100 Subject: Bug 1320362: Move indexedDb storage type in the storage inspector into a new column Issue #31 --- devtools/client/locales/en-US/storage.properties | 2 ++ devtools/client/shared/widgets/TableWidget.js | 24 ++++++++++++++++++++++ devtools/client/storage/ui.js | 12 ++++++++++- devtools/server/actors/storage.js | 8 +++++++- .../tests/browser/browser_storage_listings.js | 8 ++++---- devtools/shared/specs/storage.js | 3 +++ 6 files changed, 51 insertions(+), 6 deletions(-) (limited to 'devtools') diff --git a/devtools/client/locales/en-US/storage.properties b/devtools/client/locales/en-US/storage.properties index 3efd7c84a..7e7e656ab 100644 --- a/devtools/client/locales/en-US/storage.properties +++ b/devtools/client/locales/en-US/storage.properties @@ -53,8 +53,10 @@ table.headers.sessionStorage.value=Value table.headers.Cache.url=URL table.headers.Cache.status=Status +table.headers.indexedDB.uniqueKey=Unique key table.headers.indexedDB.name=Key table.headers.indexedDB.db=Database Name +table.headers.indexedDB.storage=Storage table.headers.indexedDB.objectStore=Object Store Name table.headers.indexedDB.value=Value table.headers.indexedDB.origin=Origin diff --git a/devtools/client/shared/widgets/TableWidget.js b/devtools/client/shared/widgets/TableWidget.js index 96c020230..84645b5b1 100644 --- a/devtools/client/shared/widgets/TableWidget.js +++ b/devtools/client/shared/widgets/TableWidget.js @@ -719,6 +719,10 @@ TableWidget.prototype = { if (hiddenColumns.includes(id) || privateColumns.includes(id)) { // Hide the column. this.columns.get(id).toggleColumn(); + + if (privateColumns.includes(id)) { + this.columns.get(id).private = true; + } } } this.sortedOn = sortOn; @@ -978,6 +982,9 @@ module.exports.TableWidget = TableWidget; * The displayed string on the column's header. */ function Column(table, id, header) { + // By default cells are visible in the UI. + this._private = false; + this.tbody = table.tbody; this.document = table.document; this.window = table.window; @@ -1060,6 +1067,23 @@ Column.prototype = { return this._sortState || 0; }, + /** + * Get the private state of the column (visibility in the UI). + */ + get private() { + return this._private; + }, + + /** + * Set the private state of the column (visibility in the UI). + * + * @param {Boolean} state + * Private (true or false) + */ + set private(state) { + this._private = state; + }, + /** * Sets the sorted value */ diff --git a/devtools/client/storage/ui.js b/devtools/client/storage/ui.js index 1241d0120..946db68a2 100644 --- a/devtools/client/storage/ui.js +++ b/devtools/client/storage/ui.js @@ -573,7 +573,7 @@ StorageUI.prototype = { }, /** - * Populates the selected entry from teh table in the sidebar for a more + * Populates the selected entry from the table in the sidebar for a more * detailed view. */ displayObjectSidebar: Task.async(function* () { @@ -615,6 +615,11 @@ StorageUI.prototype = { let otherProps = itemProps.filter( e => !["name", "value", "valueActor"].includes(e)); for (let prop of otherProps) { + let column = this.table.columns.get(prop); + if (column && column.private) { + continue; + } + let cookieProp = COOKIE_KEY_MAP[prop] || prop; // The pseduo property of HostOnly refers to converse of isDomain property rawObject[cookieProp] = (prop === "isDomain") ? !item[prop] : item[prop]; @@ -626,6 +631,11 @@ StorageUI.prototype = { } else { // Case when displaying IndexedDB db/object store properties. for (let key in item) { + let column = this.table.columns.get(key); + if (column && column.private) { + continue; + } + mainScope.addItem(key, {}, true).setGrip(item[key]); this.parseItemValue(key, item[key]); } diff --git a/devtools/server/actors/storage.js b/devtools/server/actors/storage.js index 22f4eaabe..f4fbbd8c3 100644 --- a/devtools/server/actors/storage.js +++ b/devtools/server/actors/storage.js @@ -1463,7 +1463,9 @@ DatabaseMetadata.prototype = { toObject() { return { - name: `${this._name} (${this.storage})`, + uniqueKey: `${this._name}${SEPARATOR_GUID}${this.storage}`, + name: this._name, + storage: this.storage, origin: this._origin, version: this._version, objectStores: this._objectStores.size @@ -1671,7 +1673,9 @@ StorageActors.createActor({ if ("objectStores" in item) { // DB meta data return { + uniqueKey: `${item.name} (${item.storage})`, db: item.name, + storage: item.storage, origin: item.origin, version: item.version, objectStores: item.objectStores @@ -1813,7 +1817,9 @@ StorageActors.createActor({ // Detail of indexedDB for one origin default: return [ + { name: "uniqueKey", editable: false, private: true }, { name: "db", editable: false }, + { name: "storage", editable: false }, { name: "origin", editable: false }, { name: "version", editable: false }, { name: "objectStores", editable: false }, diff --git a/devtools/server/tests/browser/browser_storage_listings.js b/devtools/server/tests/browser/browser_storage_listings.js index 15e5ccd50..e47a320b8 100644 --- a/devtools/server/tests/browser/browser_storage_listings.js +++ b/devtools/server/tests/browser/browser_storage_listings.js @@ -470,17 +470,17 @@ var testIndexedDBs = Task.async(function* (index, hosts, indexedDBActor) { for (let item of data.data) { let found = false; for (let toMatch of IDBValues.dbDetails[host]) { - if (item.db == toMatch.db) { + if (item.uniqueKey == toMatch.db) { found = true; - ok(true, "Found indexed db " + item.db + " in response"); + ok(true, "Found indexed db " + item.uniqueKey + " in response"); is(item.origin, toMatch.origin, "The origin matches."); is(item.version, toMatch.version, "The version matches."); is(item.objectStores, toMatch.objectStores, - "The numebr of object stores matches."); + "The number of object stores matches."); break; } } - ok(found, "indexed db " + item.name + " should exist in response"); + ok(found, "indexed db " + item.uniqueKey + " should exist in response"); } }; diff --git a/devtools/shared/specs/storage.js b/devtools/shared/specs/storage.js index 3674992ed..77f90323f 100644 --- a/devtools/shared/specs/storage.js +++ b/devtools/shared/specs/storage.js @@ -40,6 +40,7 @@ function createStorageSpec(options) { // Cookies store object types.addDictType("cookieobject", { + uniqueKey: "string", name: "string", value: "longstring", path: "nullable:string", @@ -176,11 +177,13 @@ createStorageSpec({ // This is a union on idb object, db metadata object and object store metadata // object types.addDictType("idbobject", { + uniqueKey: "string", name: "nullable:string", db: "nullable:string", objectStore: "nullable:string", origin: "nullable:string", version: "nullable:number", + storage: "nullable:string", objectStores: "nullable:number", keyPath: "nullable:string", autoIncrement: "nullable:boolean", -- cgit v1.2.3 From 4240e91e328b12c1b7f47b813e20c5671f4d8593 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Fri, 2 Mar 2018 15:06:49 +0100 Subject: Bug 1334252: Find IDB storage for host more directly Issue #31 --- devtools/server/actors/storage.js | 109 ++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 57 deletions(-) (limited to 'devtools') diff --git a/devtools/server/actors/storage.js b/devtools/server/actors/storage.js index f4fbbd8c3..497051e57 100644 --- a/devtools/server/actors/storage.js +++ b/devtools/server/actors/storage.js @@ -2,8 +2,6 @@ * 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/. */ -/* globals StopIteration */ - "use strict"; const {Cc, Ci, Cu, CC} = require("chrome"); @@ -1733,7 +1731,9 @@ StorageActors.createActor({ if (!DebuggerServer.isInChildProcess) { this.backToChild = (func, rv) => rv; this.clearDBStore = indexedDBHelpers.clearDBStore; - this.gatherFilesOrFolders = indexedDBHelpers.gatherFilesOrFolders; + this.findIDBPathsForHost = indexedDBHelpers.findIDBPathsForHost; + this.findSqlitePathsForHost = indexedDBHelpers.findSqlitePathsForHost; + this.findStorageTypePaths = indexedDBHelpers.findStorageTypePaths; this.getDBMetaData = indexedDBHelpers.getDBMetaData; this.getDBNamesForHost = indexedDBHelpers.getDBNamesForHost; this.getNameFromDatabaseFile = indexedDBHelpers.getNameFromDatabaseFile; @@ -2011,35 +2011,23 @@ var indexedDBHelpers = { // idb/1556056096MeysDaabta.sqlite // - PathToProfileDir/storage/temporary/http+++www.example.com/ // idb/1556056096MeysDaabta.sqlite - // // The subdirectory inside the storage folder is determined by the storage // type: // - default: { storage: "default" } or not specified. // - permanent: { storage: "persistent" }. // - temporary: { storage: "temporary" }. - let sqliteFiles = yield this.gatherFilesOrFolders(storagePath, path => { - if (path.endsWith(".sqlite")) { - let { components } = OS.Path.split(path); - let isIDB = components[components.length - 2] === "idb"; - - return isIDB; - } - return false; - }); + let sqliteFiles = yield this.findSqlitePathsForHost(storagePath, sanitizedHost); for (let file of sqliteFiles) { let splitPath = OS.Path.split(file).components; let idbIndex = splitPath.indexOf("idb"); - let name = splitPath[idbIndex - 1]; let storage = splitPath[idbIndex - 2]; let relative = file.substr(profileDir.length + 1); - if (name.startsWith(sanitizedHost)) { - files.push({ - file: relative, - storage: storage === "permanent" ? "persistent" : storage - }); - } + files.push({ + file: relative, + storage: storage === "permanent" ? "persistent" : storage + }); } if (files.length > 0) { @@ -2057,48 +2045,55 @@ var indexedDBHelpers = { }), /** - * Gather together all of the files in path and pass each path through a - * validation function. - * - * @param {String} - * Path in which to begin searching. - * @param {Function} - * Validation function, which checks each file path. If this function - * Returns true the file path is kept. - * - * @returns {Array} - * An array of file paths. + * Find all SQLite files that hold IndexedDB data for a host, such as: + * storage/temporary/http+++www.example.com/idb/1556056096MeysDaabta.sqlite */ - gatherFilesOrFolders: Task.async(function* (path, validationFunc) { - let files = []; - let iterator; - let paths = [path]; - - while (paths.length > 0) { - try { - iterator = new OS.File.DirectoryIterator(paths.pop()); - - for (let child in iterator) { - child = yield child; - - path = child.path; - - if (child.isDir) { - paths.push(path); - } else if (validationFunc(path)) { - files.push(path); - } - } - } catch (ex) { - // Ignore StopIteration to prevent exiting the loop. - if (ex != StopIteration) { - throw ex; + findSqlitePathsForHost: Task.async(function* (storagePath, sanitizedHost) { + let sqlitePaths = []; + let idbPaths = yield this.findIDBPathsForHost(storagePath, sanitizedHost); + for (let idbPath of idbPaths) { + let iterator = new OS.File.DirectoryIterator(idbPath); + yield iterator.forEach(entry => { + if (!entry.isDir && entry.path.endsWith(".sqlite")) { + sqlitePaths.push(entry.path); } + }); + iterator.close(); + } + return sqlitePaths; + }), + + /** + * Find all paths that hold IndexedDB data for a host, such as: + * storage/temporary/http+++www.example.com/idb + */ + findIDBPathsForHost: Task.async(function* (storagePath, sanitizedHost) { + let idbPaths = []; + let typePaths = yield this.findStorageTypePaths(storagePath); + for (let typePath of typePaths) { + let idbPath = OS.Path.join(typePath, sanitizedHost, "idb"); + if (yield OS.File.exists(idbPath)) { + idbPaths.push(idbPath); } } - iterator.close(); + return idbPaths; + }), - return files; + /** + * Find all the storage types, such as "default", "permanent", or "temporary". + * These names have changed over time, so it seems simpler to look through all types + * that currently exist in the profile. + */ + findStorageTypePaths: Task.async(function* (storagePath) { + let iterator = new OS.File.DirectoryIterator(storagePath); + let typePaths = []; + yield iterator.forEach(entry => { + if (entry.isDir) { + typePaths.push(entry.path); + } + }); + iterator.close(); + return typePaths; }), /** -- cgit v1.2.3 From f0175fb7abd0aad3054236fb1a0cc430c6d05db9 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Fri, 2 Mar 2018 15:26:16 +0100 Subject: Bug 1321820: Storage Inspector fails with dom.caches.enabled=false Issue #31 --- devtools/client/storage/test/browser.ini | 1 + .../test/browser_storage_dom_cache_disabled.js | 37 ++++++++++++++++++++++ devtools/client/storage/test/head.js | 10 +++--- devtools/client/storage/test/storage-listings.html | 11 +++++-- devtools/server/actors/storage.js | 5 +++ 5 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 devtools/client/storage/test/browser_storage_dom_cache_disabled.js (limited to 'devtools') diff --git a/devtools/client/storage/test/browser.ini b/devtools/client/storage/test/browser.ini index 08ee7f176..c13b9d0ec 100644 --- a/devtools/client/storage/test/browser.ini +++ b/devtools/client/storage/test/browser.ini @@ -30,6 +30,7 @@ support-files = [browser_storage_delete.js] [browser_storage_delete_all.js] [browser_storage_delete_tree.js] +[browser_storage_dom_cache_disabled.js] [browser_storage_dynamic_updates_cookies.js] [browser_storage_dynamic_updates_localStorage.js] [browser_storage_dynamic_updates_sessionStorage.js] diff --git a/devtools/client/storage/test/browser_storage_dom_cache_disabled.js b/devtools/client/storage/test/browser_storage_dom_cache_disabled.js new file mode 100644 index 000000000..db0aca392 --- /dev/null +++ b/devtools/client/storage/test/browser_storage_dom_cache_disabled.js @@ -0,0 +1,37 @@ +/* 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 the storage inspector when dom.caches.enabled=false. + +add_task(function* () { + // Disable the DOM cache + Services.prefs.setBoolPref(DOM_CACHE, false); + + yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html"); + + const state = [ + [["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"]], + [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"], + [1, 2, 3]], + ]; + + yield checkState(state); + + yield finishTests(); +}); diff --git a/devtools/client/storage/test/head.js b/devtools/client/storage/test/head.js index 0d7d13e17..07a66120e 100644 --- a/devtools/client/storage/test/head.js +++ b/devtools/client/storage/test/head.js @@ -15,6 +15,7 @@ Services.scriptloader.loadSubScript( const {TableWidget} = require("devtools/client/shared/widgets/TableWidget"); const SPLIT_CONSOLE_PREF = "devtools.toolbox.splitconsoleEnabled"; const STORAGE_PREF = "devtools.storage.enabled"; +const DOM_CACHE = "dom.caches.enabled"; const DUMPEMIT_PREF = "devtools.dump.emit"; const DEBUGGERLOG_PREF = "devtools.debugger.log"; // Allows Cache API to be working on usage `http` test page @@ -38,11 +39,12 @@ Services.prefs.setBoolPref(STORAGE_PREF, true); Services.prefs.setBoolPref(CACHES_ON_HTTP_PREF, true); registerCleanupFunction(() => { gToolbox = gPanelWindow = gWindow = gUI = null; - Services.prefs.clearUserPref(STORAGE_PREF); - Services.prefs.clearUserPref(SPLIT_CONSOLE_PREF); - Services.prefs.clearUserPref(DUMPEMIT_PREF); - Services.prefs.clearUserPref(DEBUGGERLOG_PREF); Services.prefs.clearUserPref(CACHES_ON_HTTP_PREF); + Services.prefs.clearUserPref(DEBUGGERLOG_PREF); + Services.prefs.clearUserPref(DOM_CACHE); + Services.prefs.clearUserPref(DUMPEMIT_PREF); + Services.prefs.clearUserPref(SPLIT_CONSOLE_PREF); + Services.prefs.clearUserPref(STORAGE_PREF); }); /** diff --git a/devtools/client/storage/test/storage-listings.html b/devtools/client/storage/test/storage-listings.html index de3054d3a..89ae5a332 100644 --- a/devtools/client/storage/test/storage-listings.html +++ b/devtools/client/storage/test/storage-listings.html @@ -1,4 +1,4 @@ -ļ»æ + + + + diff --git a/devtools/client/storage/storage.xul b/devtools/client/storage/storage.xul index 9fbef5199..85425912c 100644 --- a/devtools/client/storage/storage.xul +++ b/devtools/client/storage/storage.xul @@ -26,6 +26,8 @@ + @@ -33,6 +35,8 @@ + diff --git a/devtools/client/storage/test/browser_storage_basic.js b/devtools/client/storage/test/browser_storage_basic.js index 72582e32f..7585eed1f 100644 --- a/devtools/client/storage/test/browser_storage_basic.js +++ b/devtools/client/storage/test/browser_storage_basic.js @@ -29,15 +29,22 @@ const testCases = [ getCookieId("c1", "test1.example.org", "/browser"), getCookieId("cs2", ".example.org", "/"), getCookieId("c3", "test1.example.org", "/"), - getCookieId("uc1", ".example.org", "/") + getCookieId("c4", ".example.org", "/"), + getCookieId("uc1", ".example.org", "/"), + getCookieId("uc2", ".example.org", "/") ] ], [ ["cookies", "sectest1.example.org"], [ getCookieId("uc1", ".example.org", "/"), + getCookieId("uc2", ".example.org", "/"), getCookieId("cs2", ".example.org", "/"), - getCookieId("sc1", "sectest1.example.org", "/browser/devtools/client/storage/test/") + getCookieId("c4", ".example.org", "/"), + getCookieId("sc1", "sectest1.example.org", + "/browser/devtools/client/storage/test/"), + getCookieId("sc2", "sectest1.example.org", + "/browser/devtools/client/storage/test/") ] ], [["localStorage", "http://test1.example.org"], diff --git a/devtools/client/storage/test/browser_storage_cookies_delete_all.js b/devtools/client/storage/test/browser_storage_cookies_delete_all.js index ba64014fd..4640ebecb 100644 --- a/devtools/client/storage/test/browser_storage_cookies_delete_all.js +++ b/devtools/client/storage/test/browser_storage_cookies_delete_all.js @@ -8,11 +8,13 @@ // Test deleting all cookies -function* performDelete(store, rowName, deleteAll) { +function* performDelete(store, rowName, action) { let contextMenu = gPanelWindow.document.getElementById( "storage-table-popup"); let menuDeleteAllItem = contextMenu.querySelector( "#storage-table-popup-delete-all"); + let menuDeleteAllSessionCookiesItem = contextMenu.querySelector( + "#storage-table-popup-delete-all-session-cookies"); let menuDeleteAllFromItem = contextMenu.querySelector( "#storage-table-popup-delete-all-from"); @@ -25,13 +27,19 @@ function* performDelete(store, rowName, deleteAll) { 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), + switch (action) { + case "deleteAll": + menuDeleteAllItem.click(); + break; + case "deleteAllSessionCookies": + menuDeleteAllSessionCookiesItem.click(); + break; + case "deleteAllFrom": + menuDeleteAllFromItem.click(); + let hostName = cells.host.value; + ok(menuDeleteAllFromItem.getAttribute("label").includes(hostName), `Context menu item label contains '${hostName}'`); + break; } }); @@ -48,15 +56,21 @@ add_task(function* () { getCookieId("c1", "test1.example.org", "/browser"), getCookieId("c3", "test1.example.org", "/"), getCookieId("cs2", ".example.org", "/"), - getCookieId("uc1", ".example.org", "/") + getCookieId("c4", ".example.org", "/"), + getCookieId("uc1", ".example.org", "/"), + getCookieId("uc2", ".example.org", "/") ] ], [ ["cookies", "sectest1.example.org"], [ getCookieId("cs2", ".example.org", "/"), + getCookieId("c4", ".example.org", "/"), getCookieId("sc1", "sectest1.example.org", "/browser/devtools/client/storage/test/"), - getCookieId("uc1", ".example.org", "/") + getCookieId("sc2", "sectest1.example.org", + "/browser/devtools/client/storage/test/"), + getCookieId("uc1", ".example.org", "/"), + getCookieId("uc2", ".example.org", "/") ] ], ]); @@ -64,6 +78,7 @@ add_task(function* () { info("delete all from domain"); // delete only cookies that match the host exactly let id = getCookieId("c1", "test1.example.org", "/browser"); + yield performDelete(["cookies", "test1.example.org"], id, "deleteAllFrom"); yield performDelete(["cookies", "test1.example.org"], id, false); info("test state after delete all from domain"); @@ -73,24 +88,57 @@ add_task(function* () { ["cookies", "test1.example.org"], [ getCookieId("cs2", ".example.org", "/"), - getCookieId("uc1", ".example.org", "/") + getCookieId("c4", ".example.org", "/"), + getCookieId("uc1", ".example.org", "/"), + getCookieId("uc2", ".example.org", "/") ] ], [ ["cookies", "sectest1.example.org"], [ getCookieId("cs2", ".example.org", "/"), + getCookieId("c4", ".example.org", "/"), getCookieId("uc1", ".example.org", "/"), + getCookieId("uc2", ".example.org", "/"), getCookieId("sc1", "sectest1.example.org", "/browser/devtools/client/storage/test/"), + getCookieId("sc2", "sectest1.example.org", + "/browser/devtools/client/storage/test/") + ] + ], + ]); + + info("delete all session cookies"); + // delete only session cookies + id = getCookieId("cs2", ".example.org", "/"); + yield performDelete(["cookies", "sectest1.example.org"], id, + "deleteAllSessionCookies"); + + info("test state after delete all session cookies"); + yield checkState([ + // Cookies with expiry date must not be deleted. + [ + ["cookies", "test1.example.org"], + [ + getCookieId("c4", ".example.org", "/"), + getCookieId("uc2", ".example.org", "/") + ] + ], + [ + ["cookies", "sectest1.example.org"], + [ + getCookieId("c4", ".example.org", "/"), + getCookieId("uc2", ".example.org", "/"), + getCookieId("sc2", "sectest1.example.org", + "/browser/devtools/client/storage/test/") ] ], ]); info("delete all"); // delete all cookies for host, including domain cookies - id = getCookieId("uc1", ".example.org", "/"); - yield performDelete(["cookies", "sectest1.example.org"], id, true); + id = getCookieId("uc2", ".example.org", "/"); + yield performDelete(["cookies", "sectest1.example.org"], id, "deleteAll"); info("test state after delete all"); yield checkState([ diff --git a/devtools/client/storage/test/browser_storage_delete_tree.js b/devtools/client/storage/test/browser_storage_delete_tree.js index 0a7a7e645..2bca4c344 100644 --- a/devtools/client/storage/test/browser_storage_delete_tree.js +++ b/devtools/client/storage/test/browser_storage_delete_tree.js @@ -23,7 +23,9 @@ add_task(function* () { getCookieId("c1", "test1.example.org", "/browser"), getCookieId("cs2", ".example.org", "/"), getCookieId("c3", "test1.example.org", "/"), - getCookieId("uc1", ".example.org", "/") + getCookieId("c4", ".example.org", "/"), + getCookieId("uc1", ".example.org", "/"), + getCookieId("uc2", ".example.org", "/") ] ], [["localStorage", "http://test1.example.org"], ["ls1", "ls2"]], diff --git a/devtools/client/storage/test/storage-listings.html b/devtools/client/storage/test/storage-listings.html index 89ae5a332..313b36b71 100644 --- a/devtools/client/storage/test/storage-listings.html +++ b/devtools/client/storage/test/storage-listings.html @@ -20,7 +20,10 @@ document.cookie = "c1=foobar; expires=" + new Date(cookieExpiresTime1).toGMTString() + "; path=/browser"; document.cookie = "cs2=sessionCookie; path=/; domain=" + partialHostname; document.cookie = "c3=foobar-2; expires=" + - new Date(cookieExpiresTime2).toGMTString() + "; path=/"; + new Date(cookieExpiresTime1).toGMTString() + "; path=/"; +document.cookie = "c4=foobar-3; expires=" + + new Date(cookieExpiresTime2).toGMTString() + "; path=/; domain=" + + partialHostname; // ... and some local storage items .. localStorage.setItem("ls1", "foobar"); localStorage.setItem("ls2", "foobar-2"); diff --git a/devtools/client/storage/test/storage-secured-iframe.html b/devtools/client/storage/test/storage-secured-iframe.html index 8424fd4cd..9e1ef60a0 100644 --- a/devtools/client/storage/test/storage-secured-iframe.html +++ b/devtools/client/storage/test/storage-secured-iframe.html @@ -1,4 +1,4 @@ -ļ»æ +