Bug 1336934 - Added option to remove all session cookies for a specific domain r=miker draft
authorSebastian Zartner <sebastianzartner@gmail.com>
Sun, 27 Aug 2017 00:33:33 +0200
changeset 653696 aba29ba0173e0176f8e9676aa0a6e17b70b0eae9
parent 653695 2f0b519d13e9dd945b37b235db1981d5ffb2199f
child 728384 af0bb0d99677792cb22701b6f1fd79bac1ad6023
push id76376
push userbmo:sebastianzartner@gmail.com
push dateSat, 26 Aug 2017 22:34:53 +0000
reviewersmiker
bugs1336934
milestone57.0a1
Bug 1336934 - Added option to remove all session cookies for a specific domain r=miker MozReview-Commit-ID: Anm6aHQOlkO
devtools/client/locales/en-US/storage.dtd
devtools/client/storage/storage.xul
devtools/client/storage/test/browser_storage_basic.js
devtools/client/storage/test/browser_storage_basic_usercontextid_1.js
devtools/client/storage/test/browser_storage_basic_with_fragment.js
devtools/client/storage/test/browser_storage_cookies_delete_all.js
devtools/client/storage/test/browser_storage_delete_tree.js
devtools/client/storage/test/browser_storage_delete_usercontextid.js
devtools/client/storage/test/storage-listings.html
devtools/client/storage/test/storage-secured-iframe.html
devtools/client/storage/test/storage-unsecured-iframe.html
devtools/client/storage/ui.js
devtools/server/actors/storage.js
devtools/shared/specs/storage.js
--- a/devtools/client/locales/en-US/storage.dtd
+++ b/devtools/client/locales/en-US/storage.dtd
@@ -4,8 +4,11 @@
 
 <!-- LOCALIZATION NOTE : This file contains the Storage Inspector strings. -->
 
 <!-- LOCALIZATION NOTE : Placeholder for the searchbox that allows you to filter the table items. -->
 <!ENTITY searchBox.placeholder         "Filter items">
 
 <!-- LOCALIZATION NOTE : Label of popup menu action to delete all storage items. -->
 <!ENTITY storage.popupMenu.deleteAllLabel "Delete All">
+
+<!-- LOCALIZATION NOTE : Label of popup menu action to delete all session cookies. -->
+<!ENTITY storage.popupMenu.deleteAllSessionCookiesLabel "Delete All Session Cookies">
--- a/devtools/client/storage/storage.xul
+++ b/devtools/client/storage/storage.xul
@@ -22,24 +22,28 @@
   <script type="text/javascript" src="chrome://global/content/globalOverlay.js"/>
 
   <commandset id="editMenuCommands"/>
 
   <popupset id="storagePopupSet">
     <menupopup id="storage-tree-popup">
       <menuitem id="storage-tree-popup-delete-all"
                 label="&storage.popupMenu.deleteAllLabel;"/>
+      <menuitem id="storage-tree-popup-delete-all-session-cookies"
+                label="&storage.popupMenu.deleteAllSessionCookiesLabel;"/>
       <menuitem id="storage-tree-popup-delete"/>
     </menupopup>
     <menupopup id="storage-table-popup">
       <menuitem id="storage-table-popup-add"/>
       <menuitem id="storage-table-popup-delete"/>
       <menuitem id="storage-table-popup-delete-all-from"/>
       <menuitem id="storage-table-popup-delete-all"
                 label="&storage.popupMenu.deleteAllLabel;"/>
+      <menuitem id="storage-table-popup-delete-all-session-cookies"
+                label="&storage.popupMenu.deleteAllSessionCookiesLabel;"/>
     </menupopup>
   </popupset>
 
   <box flex="1" class="devtools-responsive-container theme-body">
     <vbox id="storage-tree"/>
     <splitter class="devtools-side-splitter"/>
     <vbox flex="1">
       <hbox id="storage-toolbar" class="devtools-toolbar">
--- a/devtools/client/storage/test/browser_storage_basic.js
+++ b/devtools/client/storage/test/browser_storage_basic.js
@@ -24,25 +24,32 @@
 
 const testCases = [
   [
     ["cookies", "http://test1.example.org"],
     [
       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", "https://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"],
    ["ls1", "ls2"]],
   [["localStorage", "http://sectest1.example.org"],
    ["iframe-u-ls1"]],
   [["localStorage", "https://sectest1.example.org"],
    ["iframe-s-ls1"]],
--- a/devtools/client/storage/test/browser_storage_basic_usercontextid_1.js
+++ b/devtools/client/storage/test/browser_storage_basic_usercontextid_1.js
@@ -9,25 +9,32 @@
 
 const testCases = [
   [
     ["cookies", "http://test1.example.org"],
     [
       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", "https://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"],
    ["ls1", "ls2"]],
   [["localStorage", "http://sectest1.example.org"],
    ["iframe-u-ls1"]],
   [["localStorage", "https://sectest1.example.org"],
    ["iframe-s-ls1"]],
--- a/devtools/client/storage/test/browser_storage_basic_with_fragment.js
+++ b/devtools/client/storage/test/browser_storage_basic_with_fragment.js
@@ -29,25 +29,30 @@
 
 const testCases = [
   [
     ["cookies", "http://test1.example.org"],
     [
       getCookieId("c1", "test1.example.org", "/browser"),
       getCookieId("cs2", ".example.org", "/"),
       getCookieId("c3", "test1.example.org", "/"),
-      getCookieId("uc1", ".example.org", "/")
+      getCookieId("uc1", ".example.org", "/"),
+      getCookieId("uc2", ".example.org", "/")
     ]
   ],
   [
     ["cookies", "https://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("sc1", "sectest1.example.org",
+        "/browser/devtools/client/storage/test/"),
+      getCookieId("sc2", "sectest1.example.org",
+        "/browser/devtools/client/storage/test/")
     ]
   ],
   [["localStorage", "http://test1.example.org"],
    ["ls1", "ls2"]],
   [["localStorage", "http://sectest1.example.org"],
    ["iframe-u-ls1"]],
   [["localStorage", "https://sectest1.example.org"],
    ["iframe-s-ls1"]],
--- a/devtools/client/storage/test/browser_storage_cookies_delete_all.js
+++ b/devtools/client/storage/test/browser_storage_cookies_delete_all.js
@@ -3,99 +3,146 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* import-globals-from ../../framework/test/shared-head.js */
 
 "use strict";
 
 // 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");
 
   let storeName = store.join(" > ");
 
   yield selectTreeItem(store);
 
   let eventWait = gUI.once("store-objects-updated");
   let cells = getRowCells(rowName, true);
 
   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;
     }
   });
 
   yield eventWait;
 }
 
 add_task(function* () {
   yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html");
 
   info("test state before delete");
   yield checkState([
     [
       ["cookies", "http://test1.example.org"], [
         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", "https://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", "/")
       ]
     ],
   ]);
 
   info("delete all from domain");
   // delete only cookies that match the host exactly
   let id = getCookieId("c1", "test1.example.org", "/browser");
-  yield performDelete(["cookies", "http://test1.example.org"], id, false);
+  yield performDelete(["cookies", "http://test1.example.org"], id, "deleteAllFrom");
 
   info("test state after delete all from domain");
   yield checkState([
     // Domain cookies (.example.org) must not be deleted.
     [
       ["cookies", "http://test1.example.org"],
       [
         getCookieId("cs2", ".example.org", "/"),
-        getCookieId("uc1", ".example.org", "/")
+        getCookieId("c4", ".example.org", "/"),
+        getCookieId("uc1", ".example.org", "/"),
+        getCookieId("uc2", ".example.org", "/")
       ]
     ],
     [
       ["cookies", "https://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", "http://sectest1.example.org"], id,
+    "deleteAllSessionCookies");
+
+  info("test state after delete all session cookies");
+  yield checkState([
+    // Cookies with expiry date must not be deleted.
+    [
+      ["cookies", "http://test1.example.org"],
+      [
+        getCookieId("c4", ".example.org", "/"),
+        getCookieId("uc2", ".example.org", "/")
+      ]
+    ],
+    [
+      ["cookies", "https://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", "http://sectest1.example.org"], id, true);
+  id = getCookieId("uc2", ".example.org", "/");
+  yield performDelete(["cookies", "http://sectest1.example.org"], id, "deleteAll");
 
   info("test state after delete all");
   yield checkState([
     // Domain cookies (.example.org) are deleted too, so deleting in sectest1
     // also removes stuff from test1.
     [["cookies", "http://test1.example.org"], []],
     [["cookies", "https://sectest1.example.org"], []],
   ]);
--- a/devtools/client/storage/test/browser_storage_delete_tree.js
+++ b/devtools/client/storage/test/browser_storage_delete_tree.js
@@ -18,17 +18,19 @@ add_task(function* () {
   info("test state before delete");
   yield checkState([
     [
       ["cookies", "http://test1.example.org"],
       [
         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"]],
     [["sessionStorage", "http://test1.example.org"], ["ss1"]],
     [["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"]],
   ]);
--- a/devtools/client/storage/test/browser_storage_delete_usercontextid.js
+++ b/devtools/client/storage/test/browser_storage_delete_usercontextid.js
@@ -27,25 +27,32 @@ const TEST_CASES = [
 // The storage items that should exist for default userContextId
 const storageItemsForDefault = [
   [
     ["cookies", "http://test1.example.org"],
     [
       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", "https://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"],
    ["ls1", "ls2"]],
   [["localStorage", "http://sectest1.example.org"],
    ["iframe-u-ls1"]],
   [["localStorage", "https://sectest1.example.org"],
    ["iframe-s-ls1"]],
--- a/devtools/client/storage/test/storage-listings.html
+++ b/devtools/client/storage/test/storage-listings.html
@@ -15,17 +15,20 @@ Storage inspector front end - tests
 let partialHostname = location.hostname.match(/^[^.]+(\..*)$/)[1];
 let cookieExpiresTime1 = 2000000000000;
 let cookieExpiresTime2 = 2000000001000;
 // Setting up some cookies to eat.
 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");
 // ... and finally some session storage items too
 sessionStorage.setItem("ss1", "foobar-3");
 dump("added cookies and storage from main page\n");
 
 let idbGenerator = async function() {
--- a/devtools/client/storage/test/storage-secured-iframe.html
+++ b/devtools/client/storage/test/storage-secured-iframe.html
@@ -4,17 +4,20 @@
 Iframe for testing multiple host detetion in storage actor
 -->
 <head>
   <meta charset="utf-8">
 </head>
 <body>
 <script type="application/javascript">
 "use strict";
+let cookieExpiresTime = 2000000000000;
 document.cookie = "sc1=foobar;";
+document.cookie = "sc2=foobar-2; expires=" +
+  new Date(cookieExpiresTime).toGMTString() + ";";
 localStorage.setItem("iframe-s-ls1", "foobar");
 sessionStorage.setItem("iframe-s-ss1", "foobar-2");
 dump("added cookies and storage from secured iframe\n");
 
 let idbGenerator = function*() {
   let request = indexedDB.open("idb-s1", 1);
   request.onerror = function() {
     throw new Error("error opening db connection");
--- a/devtools/client/storage/test/storage-unsecured-iframe.html
+++ b/devtools/client/storage/test/storage-unsecured-iframe.html
@@ -4,16 +4,19 @@
 Iframe for testing multiple host detetion in storage actor
 -->
 <head>
   <meta charset="utf-8">
 </head>
 <body>
 <script>
 "use strict";
+let cookieExpiresTime = 2000000000000;
 document.cookie = "uc1=foobar; domain=.example.org; path=/";
+document.cookie = "uc2=foobar-2; expires=" +
+  new Date(cookieExpiresTime).toGMTString() + "; path=/; domain=.example.org";
 localStorage.setItem("iframe-u-ls1", "foobar");
 sessionStorage.setItem("iframe-u-ss1", "foobar1");
 sessionStorage.setItem("iframe-u-ss2", "foobar2");
 dump("added cookies and storage from unsecured iframe\n");
 </script>
 </body>
 </html>
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -168,16 +168,17 @@ function StorageUI(front, target, panelW
   this.onTablePopupShowing = this.onTablePopupShowing.bind(this);
   this._tablePopup = this._panelDoc.getElementById("storage-table-popup");
   this._tablePopup.addEventListener("popupshowing", this.onTablePopupShowing);
 
   this.onAddItem = this.onAddItem.bind(this);
   this.onRemoveItem = this.onRemoveItem.bind(this);
   this.onRemoveAllFrom = this.onRemoveAllFrom.bind(this);
   this.onRemoveAll = this.onRemoveAll.bind(this);
+  this.onRemoveAllSessionCookies = this.onRemoveAllSessionCookies.bind(this);
   this.onRemoveTreeItem = this.onRemoveTreeItem.bind(this);
 
   this._addButton = this._panelDoc.getElementById("add-button");
   this._addButton.addEventListener("command", this.onAddItem);
 
   this._tablePopupAddItem = this._panelDoc.getElementById(
     "storage-table-popup-add");
   this._tablePopupAddItem.addEventListener("command", this.onAddItem);
@@ -190,20 +191,30 @@ function StorageUI(front, target, panelW
     "storage-table-popup-delete-all-from");
   this._tablePopupDeleteAllFrom.addEventListener("command",
     this.onRemoveAllFrom);
 
   this._tablePopupDeleteAll = this._panelDoc.getElementById(
     "storage-table-popup-delete-all");
   this._tablePopupDeleteAll.addEventListener("command", this.onRemoveAll);
 
+  this._tablePopupDeleteAllSessionCookies = this._panelDoc.getElementById(
+    "storage-table-popup-delete-all-session-cookies");
+  this._tablePopupDeleteAllSessionCookies.addEventListener("command",
+    this.onRemoveAllSessionCookies);
+
   this._treePopupDeleteAll = this._panelDoc.getElementById(
     "storage-tree-popup-delete-all");
   this._treePopupDeleteAll.addEventListener("command", this.onRemoveAll);
 
+  this._treePopupDeleteAllSessionCookies = this._panelDoc.getElementById(
+    "storage-tree-popup-delete-all-session-cookies");
+  this._treePopupDeleteAllSessionCookies.addEventListener("command",
+    this.onRemoveAllSessionCookies);
+
   this._treePopupDelete = this._panelDoc.getElementById("storage-tree-popup-delete");
   this._treePopupDelete.addEventListener("command", this.onRemoveTreeItem);
 }
 
 exports.StorageUI = StorageUI;
 
 StorageUI.prototype = {
 
@@ -229,22 +240,26 @@ StorageUI.prototype = {
 
     this.sidebarToggleBtn.removeEventListener("click", this.onPaneToggleButtonClicked);
     this.sidebarToggleBtn = null;
 
     this._treePopup.removeEventListener("popupshowing", this.onTreePopupShowing);
     this._addButton.removeEventListener("command", this.onAddItem);
     this._tablePopupAddItem.removeEventListener("command", this.onAddItem);
     this._treePopupDeleteAll.removeEventListener("command", this.onRemoveAll);
+    this._treePopupDeleteAllSessionCookies.removeEventListener("command",
+      this.onRemoveAllSessionCookies);
     this._treePopupDelete.removeEventListener("command", this.onRemoveTreeItem);
 
     this._tablePopup.removeEventListener("popupshowing", this.onTablePopupShowing);
     this._tablePopupDelete.removeEventListener("command", this.onRemoveItem);
     this._tablePopupDeleteAllFrom.removeEventListener("command", this.onRemoveAllFrom);
     this._tablePopupDeleteAll.removeEventListener("command", this.onRemoveAll);
+    this._tablePopupDeleteAllSessionCookies.removeEventListener("command",
+      this.onRemoveAllSessionCookies);
   },
 
   setupToolbar: function () {
     this.searchBox = this._panelDoc.getElementById("storage-searchbox");
     this.searchBox.addEventListener("command", this.filterItems);
 
     // Setup the sidebar toggle button.
     this.sidebarToggleBtn = this._panelDoc.querySelector(".sidebar-toggle");
@@ -573,16 +588,18 @@ StorageUI.prototype = {
           }
         }
 
         this.actorSupportsAddItem = yield this._target.actorHasMethod(type, "addItem");
         this.actorSupportsRemoveItem =
           yield this._target.actorHasMethod(type, "removeItem");
         this.actorSupportsRemoveAll =
           yield this._target.actorHasMethod(type, "removeAll");
+        this.actorSupportsRemoveAllSessionCookies =
+          yield this._target.actorHasMethod(type, "removeAllSessionCookies");
 
         yield this.resetColumns(type, host, subType);
       }
 
       let {data} = yield storageType.getStoreObjects(host, names, fetchOpts);
       if (data.length) {
         this.populateTable(data, reason);
       }
@@ -1109,16 +1126,27 @@ StorageUI.prototype = {
 
         if (selectedItem.length == level) {
           showDeleteAll = true;
         }
       }
 
       this._treePopupDeleteAll.hidden = !showDeleteAll;
 
+      // The delete all session cookies action is displayed for cookie object stores
+      // (level 2 of tree)
+      let showDeleteAllSessionCookies = false;
+      if (this.actorSupportsRemoveAllSessionCookies) {
+        if (type === "cookies" && selectedItem.length === 2) {
+          showDeleteAllSessionCookies = true;
+        }
+      }
+
+      this._treePopupDeleteAllSessionCookies.hidden = !showDeleteAllSessionCookies;
+
       // The delete action is displayed for:
       // - IndexedDB databases (level 3 of the tree)
       // - Cache objects (level 3 of the tree)
       let showDelete = (type == "indexedDB" || type == "Cache") &&
                        selectedItem.length == 3;
       this._treePopupDelete.hidden = !showDelete;
       if (showDelete) {
         let itemName = addEllipsis(selectedItem[selectedItem.length - 1]);
@@ -1171,16 +1199,29 @@ StorageUI.prototype = {
     // data from server are successfully fetched (and that's async).
     let [, host, ...path] = this.tree.selectedItem;
     let front = this.getCurrentFront();
     let name = path.length > 0 ? JSON.stringify(path) : undefined;
     front.removeAll(host, name);
   },
 
   /**
+   * Handles removing all session cookies from the storage
+   */
+  onRemoveAllSessionCookies: function () {
+    // Cannot use this.currentActor() if the handler is called from the
+    // tree context menu: it returns the correct value only after the
+    // table data from server is successfully fetched (and that's async).
+    let [, host, ...path] = this.tree.selectedItem;
+    let front = this.getCurrentFront();
+    let name = path.length > 0 ? JSON.stringify(path) : undefined;
+    front.removeAllSessionCookies(host, name);
+  },
+
+  /**
    * Handles removing all cookies with exactly the same domain as the
    * cookie in the selected row.
    */
   onRemoveAllFrom: function () {
     let [, host] = this.tree.selectedItem;
     let front = this.getCurrentFront();
     let rowId = this.table.contextMenuRowId;
     let data = this.table.items.get(rowId);
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -686,32 +686,40 @@ StorageActors.createActor({
   }),
 
   removeAll: Task.async(function* (host, domain) {
     let doc = this.storageActor.document;
     this.removeAllCookies(host, domain, doc.nodePrincipal
                                            .originAttributes);
   }),
 
+  removeAllSessionCookies: Task.async(function* (host, domain) {
+    let doc = this.storageActor.document;
+    this.removeAllSessionCookies(host, domain, doc.nodePrincipal
+        .originAttributes);
+  }),
+
   maybeSetupChildProcess() {
     cookieHelpers.onCookieChanged = this.onCookieChanged.bind(this);
 
     if (!DebuggerServer.isInChildProcess) {
       this.getCookiesFromHost =
         cookieHelpers.getCookiesFromHost.bind(cookieHelpers);
       this.addCookieObservers =
         cookieHelpers.addCookieObservers.bind(cookieHelpers);
       this.removeCookieObservers =
         cookieHelpers.removeCookieObservers.bind(cookieHelpers);
       this.editCookie =
         cookieHelpers.editCookie.bind(cookieHelpers);
       this.removeCookie =
         cookieHelpers.removeCookie.bind(cookieHelpers);
       this.removeAllCookies =
         cookieHelpers.removeAllCookies.bind(cookieHelpers);
+      this.removeAllSessionCookies =
+        cookieHelpers.removeAllSessionCookies.bind(cookieHelpers);
       return;
     }
 
     const { sendSyncMessage, addMessageListener } =
       this.conn.parentMessageManager;
 
     this.conn.setupInParent({
       module: "devtools/server/actors/storage",
@@ -725,16 +733,18 @@ StorageActors.createActor({
     this.removeCookieObservers =
       callParentProcess.bind(null, "removeCookieObservers");
     this.editCookie =
       callParentProcess.bind(null, "editCookie");
     this.removeCookie =
       callParentProcess.bind(null, "removeCookie");
     this.removeAllCookies =
       callParentProcess.bind(null, "removeAllCookies");
+    this.removeAllSessionCookies =
+      callParentProcess.bind(null, "removeAllSessionCookies");
 
     addMessageListener("debug:storage-cookie-request-child",
                        cookieHelpers.handleParentRequest);
 
     function callParentProcess(methodName, ...args) {
       let reply = sendSyncMessage("debug:storage-cookie-request-parent", {
         method: methodName,
         args: args
@@ -915,17 +925,18 @@ var cookieHelpers = {
     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.path || cookie.path === opts.path)) {
+          (!opts.path || cookie.path === opts.path) &&
+          (!opts.session || (!cookie.expires && !cookie.maxAge))) {
         Services.cookies.remove(
           cookie.host,
           cookie.name,
           cookie.path,
           false,
           cookie.originAttributes
         );
       }
@@ -937,16 +948,20 @@ var cookieHelpers = {
       this._removeCookies(host, { name, originAttributes });
     }
   },
 
   removeAllCookies(host, domain, originAttributes) {
     this._removeCookies(host, { domain, originAttributes });
   },
 
+  removeAllSessionCookies(host, domain, originAttributes) {
+    this._removeCookies(host, { domain, originAttributes, session: true });
+  },
+
   addCookieObservers() {
     Services.obs.addObserver(cookieHelpers, "cookie-changed");
     return null;
   },
 
   removeCookieObservers() {
     Services.obs.removeObserver(cookieHelpers, "cookie-changed");
     return null;
@@ -1019,16 +1034,22 @@ var cookieHelpers = {
         return cookieHelpers.removeCookie(host, name, originAttributes);
       }
       case "removeAllCookies": {
         let host = msg.data.args[0];
         let domain = msg.data.args[1];
         let originAttributes = msg.data.args[2];
         return cookieHelpers.removeAllCookies(host, domain, originAttributes);
       }
+      case "removeAllSessionCookies": {
+        let host = msg.data.args[0];
+        let domain = msg.data.args[1];
+        let originAttributes = msg.data.args[2];
+        return cookieHelpers.removeAllSessionCookies(host, domain, originAttributes);
+      }
       default:
         console.error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD", msg.json.method);
         throw new Error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD");
     }
   },
 };
 
 /**
--- a/devtools/shared/specs/storage.js
+++ b/devtools/shared/specs/storage.js
@@ -99,16 +99,24 @@ createStorageSpec({
     }, {
       removeAll: {
         request: {
           host: Arg(0, "string"),
           domain: Arg(1, "nullable:string")
         },
         response: {}
       }
+    }, {
+      removeAllSessionCookies: {
+        request: {
+          host: Arg(0, "string"),
+          domain: Arg(1, "nullable:string")
+        },
+        response: {}
+      }
     }
   )
 });
 
 // Local Storage / Session Storage store object
 types.addDictType("storageobject", {
   name: "string",
   value: "longstring"