/* 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 deleting storage items with userContextId.

// The items that will be deleted.
const TEST_CASES = [
  [["localStorage", "http://test1.example.org"], "ls1", "name"],
  [["sessionStorage", "http://test1.example.org"], "ss1", "name"],
  [
    ["cookies", "http://test1.example.org"],
    getCookieId("c1", "test1.example.org", "/browser"),
    "name",
  ],
  [
    ["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
    1,
    "name",
  ],
  [
    ["Cache", "http://test1.example.org", "plop"],
    MAIN_DOMAIN + "404_cached_file.js",
    "url",
  ],
];

// 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("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("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"],
    ["key", "ls1", "ls2"],
  ],
  [["localStorage", "http://sectest1.example.org"], ["iframe-u-ls1"]],
  [["localStorage", "https://sectest1.example.org"], ["iframe-s-ls1"]],
  [
    ["sessionStorage", "http://test1.example.org"],
    ["key", "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)", "idb2 (default)"],
  ],
  [
    ["indexedDB", "http://test1.example.org", "idb1 (default)"],
    ["obj1", "obj2"],
  ],
  [["indexedDB", "http://test1.example.org", "idb2 (default)"], ["obj3"]],
  [
    ["indexedDB", "http://test1.example.org", "idb1 (default)", "obj1"],
    [1, 2, 3],
  ],
  [["indexedDB", "http://test1.example.org", "idb1 (default)", "obj2"], [1]],
  [["indexedDB", "http://test1.example.org", "idb2 (default)", "obj3"], []],
  [["indexedDB", "http://sectest1.example.org"], []],
  [
    ["indexedDB", "https://sectest1.example.org"],
    ["idb-s1 (default)", "idb-s2 (default)"],
  ],
  [
    ["indexedDB", "https://sectest1.example.org", "idb-s1 (default)"],
    ["obj-s1"],
  ],
  [
    ["indexedDB", "https://sectest1.example.org", "idb-s2 (default)"],
    ["obj-s2"],
  ],
  [
    ["indexedDB", "https://sectest1.example.org", "idb-s1 (default)", "obj-s1"],
    [6, 7],
  ],
  [
    ["indexedDB", "https://sectest1.example.org", "idb-s2 (default)", "obj-s2"],
    [16],
  ],
  [
    ["Cache", "http://test1.example.org", "plop"],
    [
      MAIN_DOMAIN + "404_cached_file.js",
      MAIN_DOMAIN + "browser_storage_basic.js",
    ],
  ],
];

/**
 * Test that the desired number of tree items are present
 */
function testTree(tests) {
  const doc = gPanelWindow.document;
  for (const [item] of tests) {
    ok(
      doc.querySelector("[data-id='" + JSON.stringify(item) + "']"),
      `Tree item ${item.toSource()} should be present in the storage tree`
    );
  }
}

/**
 * Test that correct table entries are shown for each of the tree item
 */
async function testTables(tests) {
  const doc = gPanelWindow.document;
  // Expand all nodes so that the synthesized click event actually works
  gUI.tree.expandAll();

  // First tree item is already selected so no clicking and waiting for update
  for (const id of tests[0][1]) {
    ok(
      doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
      "Table item " + id + " should be present"
    );
  }

  // Click rest of the tree items and wait for the table to be updated
  for (const [treeItem, items] of tests.slice(1)) {
    await selectTreeItem(treeItem);

    // Check whether correct number of items are present in the table
    is(
      doc.querySelectorAll(
        ".table-widget-column:first-of-type .table-widget-cell"
      ).length,
      items.length,
      "Number of items in table is correct"
    );

    // Check if all the desired items are present in the table
    for (const id of items) {
      ok(
        doc.querySelector(".table-widget-cell[data-id='" + id + "']"),
        "Table item " + id + " should be present"
      );
    }
  }
}

add_task(async function () {
  // storage-listings.html explicitly mixes secure and insecure frames.
  // We should not enforce https for tests using this page.
  await pushPref("dom.security.https_first", false);

  // First, open a tab with the default userContextId and setup its storages.
  const tabDefault = await openTab(MAIN_DOMAIN + "storage-listings.html");

  // Second, start testing for userContextId 1.
  // We use the same item name as the default page has to see deleting items
  // from userContextId 1 will affect default one or not.
  await openTabAndSetupStorage(MAIN_DOMAIN + "storage-listings.html", {
    userContextId: 1,
  });

  const contextMenu = gPanelWindow.document.getElementById(
    "storage-table-popup"
  );
  const menuDeleteItem = contextMenu.querySelector(
    "#storage-table-popup-delete"
  );

  for (const [treeItem, rowName, cellToClick] of TEST_CASES) {
    const treeItemName = treeItem.join(" > ");

    info(`Selecting tree item ${treeItemName}`);
    await selectTreeItem(treeItem);

    const row = getRowCells(rowName);
    ok(
      gUI.table.items.has(rowName),
      `There is a row '${rowName}' in ${treeItemName}`
    );

    const eventWait = gUI.once("store-objects-edit");

    await waitForContextMenu(contextMenu, row[cellToClick], () => {
      info(`Opened context menu in ${treeItemName}, row '${rowName}'`);
      contextMenu.activateItem(menuDeleteItem);
      const truncatedRowName = String(rowName)
        .replace(SEPARATOR_GUID, "-")
        .substr(0, 16);
      ok(
        JSON.parse(
          menuDeleteItem.getAttribute("data-l10n-args")
        ).itemName.includes(truncatedRowName),
        `Context menu item label contains '${rowName}' (maybe truncated)`
      );
    });

    await eventWait;

    ok(
      !gUI.table.items.has(rowName),
      `There is no row '${rowName}' in ${treeItemName} after deletion`
    );
  }

  // Final, we see that the default tab is intact or not.
  await BrowserTestUtils.switchTab(gBrowser, tabDefault);
  await openStoragePanel();

  testTree(storageItemsForDefault);
  await testTables(storageItemsForDefault);
});
