Bug 1763313 - [devtools] Migrate storage panel to Fluent. r=jdescottes,flod

Both the dtd and properties files strings are moved to the existing storage.ftl.
A migration file was added to keep the localized strings.

Some of the strings were migrated to a declarative approach, setting `data-l10n-id` and
`data-l10n-args` attributes on elements. But in other cases, this was not easily doable.
To accomodate with the asynchronicity of `formatValue`, we translate all the strings that
don't have dynamic parameters during the initialization of the panel, and store them in
a Map for easy retrieval.

We also took this opportunity to cleanup the strings key names and have them all prefixed
with `storage-*`. Some functions where refactored so it should be easier to search the
code for a specific l10n string.

Finally, the `table.headers.indexedDB.*` strings weren't migrated as they were never read
in the UI (they are referenced in `NON_L10N_STRINGS`, which bypass localization).

Differential Revision: https://phabricator.services.mozilla.com/D143012
This commit is contained in:
Nicolas Chevobbe 2022-04-13 12:47:57 +00:00
parent abb6e53e32
commit 5c8a5c6759
8 changed files with 459 additions and 316 deletions

View File

@ -1,17 +0,0 @@
<!-- 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/. -->
<!-- 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">
<!-- LOCALIZATION NOTE : Label of popup menu action to copy a storage item. -->
<!ENTITY storage.popupMenu.copyLabel "Copy">

View File

@ -4,6 +4,9 @@
### These strings are used inside the Storage Inspector.
# Key shortcut used to focus the filter box on top of the data view
storage-filter-key = CmdOrCtrl+F
# Hint shown when the selected storage host does not contain any data
storage-table-empty-text = No data present for selected host
@ -30,3 +33,99 @@ storage-table-type-cache-hint = View and delete the cache storage entries by sel
# Hint shown when the extension storage type is selected. Clicking the link will open
# https://firefox-source-docs.mozilla.org/devtools-user/storage_inspector/extension_storage/
storage-table-type-extensionstorage-hint = View and edit the extension storage by selecting a host. <a data-l10n-name="learn-more-link">Learn more</a>
# Placeholder for the searchbox that allows you to filter the table items
storage-search-box =
.placeholder = Filter Items
# Placeholder text in the sidebar search box
storage-variable-view-search-box =
.placeholder = Filter values
# Add Item button title
storage-add-button =
.title = Add Item
# Refresh button title
storage-refresh-button =
.title = Refresh Items
# Context menu action to delete all storage items
storage-context-menu-delete-all =
.label = Delete All
# Context menu action to delete all session cookies
storage-context-menu-delete-all-session-cookies =
.label = Delete All Session Cookies
# Context menu action to copy a storage item
storage-context-menu-copy =
.label = Copy
# Context menu action to delete storage item
# Variables:
# $itemName (String) - Name of the storage item that will be deleted
storage-context-menu-delete =
.label = Delete “{ $itemName }”
# Context menu action to add an item
storage-context-menu-add-item =
.label = Add Item
# Context menu action to delete all storage items from a given host
# Variables:
# $host (String) - Host for which we want to delete the items
storage-context-menu-delete-all-from =
.label = Delete All From “{ $host }”
## Header names of the columns in the Storage Table for each type of storage available
## through the Storage Tree to the side.
storage-table-headers-cookies-name = Name
storage-table-headers-cookies-value = Value
storage-table-headers-cookies-expires = Expires / Max-Age
storage-table-headers-cookies-size = Size
storage-table-headers-cookies-last-accessed = Last Accessed
storage-table-headers-cookies-creation-time = Created
storage-table-headers-cache-status = Status
storage-table-headers-extension-storage-area = Storage Area
## Labels for Storage type groups present in the Storage Tree, like cookies, local storage etc.
storage-tree-labels-cookies = Cookies
storage-tree-labels-local-storage = Local Storage
storage-tree-labels-session-storage = Session Storage
storage-tree-labels-indexed-db = Indexed DB
storage-tree-labels-cache = Cache Storage
storage-tree-labels-extension-storage = Extension Storage
##
# Tooltip for the button that collapses the right panel in the
# storage UI when the panel is closed.
storage-expand-pane =
.title = Expand Pane
# Tooltip for the button that collapses the right panel in the
# storage UI when the panel is open.
storage-collapse-pane =
.title = Collapse Pane
# String displayed in the expires column when the cookie is a Session Cookie
storage-expires-session = Session
# Heading displayed over the item value in the sidebar
storage-data = Data
# Heading displayed over the item parsed value in the sidebar
storage-parsed-value = Parsed Value
# Warning notification when IndexedDB database could not be deleted immediately.
# Variables:
# $dbName (String) - Name of the database
storage-idb-delete-blocked = Database “{ $dbName }” will be deleted after all connections are closed.
# Error notification when IndexedDB database could not be deleted.
# Variables:
# $dbName (String) - Name of the database
storage-idb-delete-error = Database “{ $dbName }” could not be deleted.

View File

@ -1,102 +0,0 @@
# 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/.
# LOCALIZATION NOTE These strings are used inside the Storage Editor tool.
# LOCALIZATION NOTE The correct localization of this file might be to keep it
# in English, or another language commonly spoken among web developers.
# You want to make that choice consistent across the developer tools.
# A good criteria is the language in which you'd find the best documentation
# on web development on the web.
# LOCALIZATION NOTE (storage.filter.key):
# Key shortcut used to focus the filter box on top of the data view
storage.filter.key=CmdOrCtrl+F
# LOCALIZATION NOTE (tree.labels.*):
# These strings are the labels for Storage type groups present in the Storage
# Tree, like cookies, local storage etc.
tree.labels.cookies=Cookies
tree.labels.localStorage=Local Storage
tree.labels.sessionStorage=Session Storage
tree.labels.indexedDB=Indexed DB
tree.labels.Cache=Cache Storage
tree.labels.extensionStorage=Extension 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.name=Name
table.headers.cookies.value=Value
table.headers.cookies.expires2=Expires / Max-Age
table.headers.cookies.size=Size
table.headers.cookies.lastAccessed2=Last Accessed
table.headers.cookies.creationTime2=Created
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
table.headers.indexedDB.version=Version
table.headers.indexedDB.objectStores=Object Stores
table.headers.indexedDB.keyPath2=Key Path
table.headers.indexedDB.autoIncrement=Auto Increment
table.headers.indexedDB.indexes=Indexes
table.headers.extensionStorage.area=Storage Area
# LOCALIZATION NOTE (label.expires.session):
# This string is displayed in the expires column when the cookie is Session
# Cookie
label.expires.session=Session
# LOCALIZATION NOTE (storage.search.placeholder):
# This is the placeholder text in the sidebar search box
storage.search.placeholder=Filter values
# LOCALIZATION NOTE (storage.data.label):
# This is the heading displayed over the item value in the sidebar
storage.data.label=Data
# LOCALIZATION NOTE (storage.parsedValue.label):
# This is the heading displayed over the item parsed value in the sidebar
storage.parsedValue.label=Parsed Value
# LOCALIZATION NOTE (storage.popupMenu.deleteLabel):
# Label of popup menu action to delete storage item.
storage.popupMenu.deleteLabel=Delete “%S”
# LOCALIZATION NOTE (storage.popupMenu.addItemLabel):
# Label of popup menu action to add an item.
storage.popupMenu.addItemLabel=Add Item
# LOCALIZATION NOTE (storage.popupMenu.refreshItemLabel):
# Label of popup menu action to refresh an item.
storage.popupMenu.refreshItemLabel=Refresh Items
# LOCALIZATION NOTE (storage.popupMenu.deleteAllFromLabel):
# Label of popup menu action to delete all storage items.
storage.popupMenu.deleteAllFromLabel=Delete All From “%S”
# LOCALIZATION NOTE (storage.idb.deleteBlocked):
# Warning notification when IndexedDB database could not be deleted immediately.
storage.idb.deleteBlocked=Database “%S” will be deleted after all connections are closed.
# LOCALIZATION NOTE (storage.idb.deleteError):
# Error notification when IndexedDB database could not be deleted.
storage.idb.deleteError=Database “%S” could not be deleted.
# LOCALIZATION NOTE (storage.expandPane):
# This is the tooltip for the button that collapses the right panel in the
# storage UI when the panel is closed.
storage.expandPane=Expand Pane
# LOCALIZATION NOTE (storage.collapsePane):
# This is the tooltip for the button that collapses the right panel in the
# storage UI when the panel is open.
storage.collapsePane=Collapse Pane

View File

@ -105,22 +105,22 @@ VariablesView.prototype = {
* This new scope will be considered the parent of any other scope
* added afterwards.
*
* @param string aName
* The scope's name (e.g. "Local", "Global" etc.).
* @param string l10nId
* The scope localized string id.
* @param string aCustomClass
* An additional class name for the containing element.
* @return Scope
* The newly created Scope instance.
*/
addScope: function(aName = "", aCustomClass = "") {
addScope: function(l10nId = "", aCustomClass = "") {
this._removeEmptyNotice();
this._toggleSearchVisibility(true);
const scope = new Scope(this, aName, { customClass: aCustomClass });
const scope = new Scope(this, l10nId, { customClass: aCustomClass });
this._store.push(scope);
this._itemsByElement.set(scope._target, scope);
this._currHierarchy.set(aName, scope);
scope.header = !!aName;
this._currHierarchy.set(l10nId, scope);
scope.header = !!l10nId;
return scope;
},
@ -396,25 +396,6 @@ VariablesView.prototype = {
return !!this._searchboxContainer;
},
/**
* Sets the text displayed for the searchbox in this container.
* @param string aValue
*/
set searchPlaceholder(aValue) {
if (this._searchboxNode) {
this._searchboxNode.setAttribute("placeholder", aValue);
}
this._searchboxPlaceholder = aValue;
},
/**
* Gets the text displayed for the searchbox in this container.
* @return string
*/
get searchPlaceholder() {
return this._searchboxPlaceholder;
},
/**
* Enables variable and property searching in this view.
* Use the "searchEnabled" setter to enable searching.
@ -441,7 +422,7 @@ VariablesView.prototype = {
"input"
));
searchbox.className = "variables-view-searchinput devtools-filterinput";
searchbox.setAttribute("placeholder", this._searchboxPlaceholder);
searchbox.setAttribute("data-l10n-id", "storage-variable-view-search-box");
searchbox.addEventListener("input", this._onSearchboxInput);
searchbox.addEventListener("keydown", this._onSearchboxKeyDown);
@ -1045,7 +1026,6 @@ VariablesView.prototype = {
_list: null,
_searchboxNode: null,
_searchboxContainer: null,
_searchboxPlaceholder: "",
_emptyTextNode: null,
_emptyTextValue: "",
};
@ -1272,12 +1252,12 @@ VariablesView.getterOrSetterDeleteCallback = function(aItem) {
*
* @param VariablesView aView
* The view to contain this scope.
* @param string aName
* The scope's name.
* @param string l10nId
* The scope localized string id.
* @param object aFlags [optional]
* Additional options or flags for this scope.
*/
function Scope(aView, aName, aFlags = {}) {
function Scope(aView, l10nId, aFlags = {}) {
this.ownerView = aView;
this._onClick = this._onClick.bind(this);
@ -1301,7 +1281,7 @@ function Scope(aView, aName, aFlags = {}) {
this.contextMenuId = aView.contextMenuId;
this.separatorStr = aView.separatorStr;
this._init(aName, aFlags);
this._init(l10nId, aFlags);
}
Scope.prototype = {
@ -1833,18 +1813,18 @@ Scope.prototype = {
/**
* Initializes this scope's id, view and binds event listeners.
*
* @param string aName
* The scope's name.
* @param string l10nId
* The scope localized string id.
* @param object aFlags [optional]
* Additional options or flags for this scope.
*/
_init: function(aName, aFlags) {
this._idString = generateId((this._nameString = aName));
this._displayScope(
aName,
`${this.targetClassName} ${aFlags.customClass}`,
"devtools-toolbar"
);
_init: function(l10nId, aFlags) {
this._idString = generateId((this._nameString = l10nId));
this._displayScope({
l10nId,
targetClassName: `${this.targetClassName} ${aFlags.customClass}`,
titleClassName: "devtools-toolbar",
});
this._addEventListeners();
this.parentNode.appendChild(this._target);
},
@ -1852,30 +1832,42 @@ Scope.prototype = {
/**
* Creates the necessary nodes for this scope.
*
* @param string aName
* The scope's name.
* @param string aTargetClassName
* @param Object options
* @param string options.l10nId [optional]
* The scope localized string id.
* @param string options.value [optional]
* The scope's name. Either this or l10nId need to be passed
* @param string options.targetClassName
* A custom class name for this scope's target element.
* @param string aTitleClassName [optional]
* @param string options.titleClassName [optional]
* A custom class name for this scope's title element.
*/
_displayScope: function(aName = "", aTargetClassName, aTitleClassName = "") {
_displayScope: function({
l10nId,
value,
targetClassName,
titleClassName = "",
}) {
const document = this.document;
const element = (this._target = document.createXULElement("vbox"));
element.id = this._idString;
element.className = aTargetClassName;
element.className = targetClassName;
const arrow = (this._arrow = document.createXULElement("hbox"));
arrow.className = "arrow theme-twisty";
const name = (this._name = document.createXULElement("label"));
name.className = "plain name";
name.setAttribute("value", aName.trim());
if (l10nId) {
name.setAttribute("data-l10n-id", l10nId);
} else {
name.setAttribute("value", value);
}
name.setAttribute("crop", "end");
const title = (this._title = document.createXULElement("hbox"));
title.className = "title " + aTitleClassName;
title.className = "title " + titleClassName;
title.setAttribute("align", "center");
const enumerable = (this._enum = document.createXULElement("vbox"));
@ -2608,7 +2600,7 @@ Variable.prototype = extend(Scope.prototype, {
*/
_init: function(aName, aDescriptor) {
this._idString = generateId((this._nameString = aName));
this._displayScope(aName, this.targetClassName);
this._displayScope({ value: aName, targetClassName: this.targetClassName });
this._displayVariable();
this._customizeVariable();
this._prepareTooltips();

View File

@ -8,10 +8,7 @@
<?xml-stylesheet href="chrome://devtools/skin/storage.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/components/SidebarToggle.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % storageDTD SYSTEM "chrome://devtools/locale/storage.dtd">
%storageDTD;
]>
<!DOCTYPE window>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
@ -24,23 +21,30 @@
<popupset id="storagePopupSet">
<menupopup id="storage-tree-popup">
<menuitem id="storage-tree-popup-delete-all"
label="&storage.popupMenu.deleteAllLabel;"/>
data-l10n-id="storage-context-menu-delete-all"/>
<menuitem id="storage-tree-popup-delete-all-session-cookies"
label="&storage.popupMenu.deleteAllSessionCookiesLabel;"/>
<menuitem id="storage-tree-popup-delete"/>
data-l10n-id="storage-context-menu-delete-all-session-cookies"/>
<menuitem id="storage-tree-popup-delete"
data-l10n-id="storage-context-menu-delete"
data-l10n-args='{"itemName": ""}'/>
</menupopup>
<menupopup id="variable-view-popup">
<menuitem id="variable-view-popup-copy"
label="&storage.popupMenu.copyLabel;"/>
data-l10n-id="storage-context-menu-copy"/>
</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-add"
data-l10n-id="storage-context-menu-add-item"/>
<menuitem id="storage-table-popup-delete"
data-l10n-id="storage-context-menu-delete"
data-l10n-args='{"itemName": ""}'/>
<menuitem id="storage-table-popup-delete-all-from"
data-l10n-id="storage-context-menu-delete-all-from"
data-l10n-args='{"host": ""}'/>
<menuitem id="storage-table-popup-delete-all"
label="&storage.popupMenu.deleteAllLabel;"/>
data-l10n-id="storage-context-menu-delete-all"/>
<menuitem id="storage-table-popup-delete-all-session-cookies"
label="&storage.popupMenu.deleteAllSessionCookiesLabel;"/>
data-l10n-id="storage-context-menu-delete-all-session-cookies"/>
</menupopup>
</popupset>
@ -51,12 +55,14 @@
<hbox id="storage-toolbar" class="devtools-toolbar devtools-input-toolbar">
<html:input id="storage-searchbox"
class="devtools-filterinput"
placeholder="&searchBox.placeholder;"/>
data-l10n-id="storage-search-box"/>
<hbox class="devtools-separator"/>
<html:button id="add-button"
class="devtools-button add-button"></html:button>
class="devtools-button add-button"
data-l10n-id="storage-add-button"></html:button>
<html:button id="refresh-button"
class="devtools-button refresh-button"></html:button>
class="devtools-button refresh-button"
data-l10n-id="storage-refresh-button"></html:button>
<html:button class="devtools-button sidebar-toggle" hidden=""></html:button>
</hbox>
<vbox id="storage-table" class="theme-sidebar" flex="1"/>

View File

@ -969,10 +969,11 @@ var focusSearchBoxUsingShortcut = async function(panelWin, callback) {
const focused = once(searchBox, "focus");
panelWin.focus();
const strings = Services.strings.createBundle(
"chrome://devtools/locale/storage.properties"
const shortcut = await panelWin.document.l10n.formatValue(
"storage-filter-key"
);
synthesizeKeyShortcut(strings.GetStringFromName("storage.filter.key"));
synthesizeKeyShortcut(shortcut);
await focused;

View File

@ -4,7 +4,7 @@
"use strict";
const EventEmitter = require("devtools/shared/event-emitter");
const { LocalizationHelper, ELLIPSIS } = require("devtools/shared/l10n");
const { ELLIPSIS } = require("devtools/shared/l10n");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const { parseItemValue } = require("devtools/shared/storage/utils");
const { KeyCodes } = require("devtools/client/shared/keycodes");
@ -35,22 +35,6 @@ loader.lazyImporter(
"resource://devtools/client/storage/VariablesView.jsm"
);
/**
* Localization convenience methods.
*/
const STORAGE_STRINGS = "devtools/client/locales/storage.properties";
const L10N = new LocalizationHelper(STORAGE_STRINGS, true);
const GENERIC_VARIABLES_VIEW_SETTINGS = {
lazyEmpty: true,
// ms
lazyEmptyDelay: 10,
searchEnabled: true,
contextMenuId: "variable-view-popup",
searchPlaceholder: L10N.getStr("storage.search.placeholder"),
preventDescriptorModifiers: true,
};
const REASON = {
NEW_ROW: "new-row",
NEXT_50_ITEMS: "next-50-items",
@ -62,6 +46,23 @@ const REASON = {
// trimmed with ellipsis if it's longer.
const ITEM_NAME_MAX_LENGTH = 32;
const HEADERS_L10N_IDS = {
Cache: {
status: "storage-table-headers-cache-status",
},
cookies: {
creationTime: "storage-table-headers-cookies-creation-time",
expires: "storage-table-headers-cookies-expires",
lastAccessed: "storage-table-headers-cookies-last-accessed",
name: "storage-table-headers-cookies-name",
size: "storage-table-headers-cookies-size",
value: "storage-table-headers-cookies-value",
},
extensionStorage: {
area: "storage-table-headers-extension-storage-area",
},
};
// We only localize certain table headers. The headers that we do not localize
// along with their English translation are stored in this Map for easy
// reference.
@ -94,15 +95,6 @@ const NON_L10N_STRINGS = new Map([
["sessionStorage.value", "Value"],
]);
// If a l10n ID has been changed since it was created we store it in this map
// along with it's new value for easy reference.
const NON_ORIGINAL_L10N_IDS = new Map([
["cookies.expires", "cookies.expires2"],
["cookies.lastAccessed", "cookies.lastAccessed2"],
["cookies.creationTime", "cookies.creationTime2"],
["indexedDB.keyPath", "indexedDB.keyPath2"],
]);
/**
* StorageUI is controls and builds the UI of the Storage Inspector.
*
@ -148,24 +140,19 @@ class StorageUI {
this.sidebar = this._panelDoc.getElementById("storage-sidebar");
this.sidebar.setAttribute("width", "300");
this.view = new VariablesView(
this.sidebar.firstChild,
GENERIC_VARIABLES_VIEW_SETTINGS
);
this.view = new VariablesView(this.sidebar.firstChild, {
lazyEmpty: true,
// ms
lazyEmptyDelay: 10,
searchEnabled: true,
contextMenuId: "variable-view-popup",
preventDescriptorModifiers: true,
});
this.filterItems = this.filterItems.bind(this);
this.onPaneToggleButtonClicked = this.onPaneToggleButtonClicked.bind(this);
this.setupToolbar();
const shortcuts = new KeyShortcuts({
window: this._panelDoc.defaultView,
});
const key = L10N.getStr("storage.filter.key");
shortcuts.on(key, event => {
event.preventDefault();
this.searchBox.focus();
});
this.handleKeypress = this.handleKeypress.bind(this);
this._panelDoc.addEventListener("keypress", this.handleKeypress);
@ -199,10 +186,6 @@ class StorageUI {
this._refreshButton = this._panelDoc.getElementById("refresh-button");
this._refreshButton.addEventListener("click", this.onRefreshTable);
this._refreshButton.setAttribute(
"title",
L10N.getFormatStr("storage.popupMenu.refreshItemLabel")
);
this._addButton = this._panelDoc.getElementById("add-button");
this._addButton.addEventListener("click", this.onAddItem);
@ -274,6 +257,18 @@ class StorageUI {
// but rather the storage specific front, i.e. a storage resource. Storage resources are fronts.
this.storageResources = {};
await this._initL10NStringsMap();
// This can only be done after l10n strings were retrieved as we're using "storage-filter-key"
const shortcuts = new KeyShortcuts({
window: this._panelDoc.defaultView,
});
const key = this._l10nStrings.get("storage-filter-key");
shortcuts.on(key, event => {
event.preventDefault();
this.searchBox.focus();
});
this._onTargetAvailable = this._onTargetAvailable.bind(this);
this._onTargetDestroyed = this._onTargetDestroyed.bind(this);
await this._commands.targetCommand.watchTargets({
@ -302,6 +297,32 @@ class StorageUI {
);
}
async _initL10NStringsMap() {
const ids = [
"storage-filter-key",
"storage-table-headers-cookies-name",
"storage-table-headers-cookies-value",
"storage-table-headers-cookies-expires",
"storage-table-headers-cookies-size",
"storage-table-headers-cookies-last-accessed",
"storage-table-headers-cookies-creation-time",
"storage-table-headers-cache-status",
"storage-table-headers-extension-storage-area",
"storage-tree-labels-cookies",
"storage-tree-labels-local-storage",
"storage-tree-labels-session-storage",
"storage-tree-labels-indexed-db",
"storage-tree-labels-cache",
"storage-tree-labels-extension-storage",
"storage-expires-session",
];
const results = await this._panelDoc.l10n.formatValues(
ids.map(s => ({ id: s }))
);
this._l10nStrings = new Map(ids.map((id, i) => [id, results[i]]));
}
async _onTargetAvailable({ targetFront }) {
// Only support top level target and navigation to new processes.
// i.e. ignore additional targets created for remote <iframes>
@ -467,18 +488,18 @@ class StorageUI {
}
updateSidebarToggleButton() {
let title;
let dataL10nId;
this.sidebarToggleBtn.hidden = !this.table.hasSelectedRow;
if (this.sidebar.hidden) {
this.sidebarToggleBtn.classList.add("pane-collapsed");
title = L10N.getStr("storage.expandPane");
dataL10nId = "storage-expand-pane";
} else {
this.sidebarToggleBtn.classList.remove("pane-collapsed");
title = L10N.getStr("storage.collapsePane");
dataL10nId = "storage-collapse-pane";
}
this.sidebarToggleBtn.setAttribute("title", title);
this._panelDoc.l10n.setAttributes(this.sidebarToggleBtn, dataL10nId);
}
/**
@ -641,6 +662,32 @@ class StorageUI {
}
}
/**
* Get a string for a column name automatically choosing whether or not the
* string should be localized.
*
* @param {String} type
* The storage type.
* @param {String} name
* The field name that may need to be localized.
*/
_getColumnName(type, name) {
// If the ID exists in NON_L10N_STRINGS then we do not translate it
const columnName = NON_L10N_STRINGS.get(`${type}.${name}`);
if (columnName) {
return columnName;
}
// otherwise we get it from the L10N Map (populated during init)
const l10nId = HEADERS_L10N_IDS[type]?.[name];
if (l10nId && this._l10nStrings.has(l10nId)) {
return this._l10nStrings.get(l10nId);
}
// If the string isn't localized, we will just use the field name.
return name;
}
/**
* Handle added items received by onEdit
*
@ -886,18 +933,7 @@ class StorageUI {
const [type, host] = item;
// Add is only supported if the selected item has a host.
const canAdd = this.supportsAddItem(type, host) && host;
if (canAdd) {
this._addButton.hidden = false;
this._addButton.setAttribute(
"title",
L10N.getFormatStr("storage.popupMenu.addItemLabel")
);
} else {
this._addButton.hidden = true;
this._addButton.removeAttribute("title");
}
this._addButton.hidden = !host || !this.supportsAddItem(type, host);
}
/**
@ -942,7 +978,7 @@ class StorageUI {
for (const [type, resources] of Object.entries(this.storageResources)) {
let typeLabel = type;
try {
typeLabel = L10N.getStr("tree.labels." + type);
typeLabel = this.getStorageTypeLabel(type);
} catch (e) {
console.error("Unable to localize tree label type:" + type);
}
@ -961,6 +997,35 @@ class StorageUI {
}
}
getStorageTypeLabel(type) {
let dataL10nId;
switch (type) {
case "cookies":
dataL10nId = "storage-tree-labels-cookies";
break;
case "localStorage":
dataL10nId = "storage-tree-labels-local-storage";
break;
case "sessionStorage":
dataL10nId = "storage-tree-labels-session-storage";
break;
case "indexedDB":
dataL10nId = "storage-tree-labels-indexed-db";
break;
case "Cache":
dataL10nId = "storage-tree-labels-cache";
break;
case "extensionStorage":
dataL10nId = "storage-tree-labels-extension-storage";
break;
default:
throw new Error("Unknown storage type");
}
return this._l10nStrings.get(dataL10nId);
}
/**
* Populates the selected entry from the table in the sidebar for a more
* detailed view.
@ -994,7 +1059,7 @@ class StorageUI {
this.updateSidebarToggleButton();
this.view.empty();
const mainScope = this.view.addScope(L10N.getStr("storage.data.label"));
const mainScope = this.view.addScope("storage-data");
mainScope.expanded = true;
if (value) {
@ -1025,7 +1090,7 @@ class StorageUI {
continue;
}
const fieldName = getColumnName(this.table.datatype, prop);
const fieldName = this._getColumnName(this.table.datatype, prop);
rawObject[fieldName] = item[prop];
}
itemVar.populate(rawObject, { sorted: true });
@ -1091,8 +1156,7 @@ class StorageUI {
const view = this.view;
jsonObject[name] = obj;
const valueScope =
view.getScopeAtIndex(1) ||
view.addScope(L10N.getStr("storage.parsedValue.label"));
view.getScopeAtIndex(1) || view.addScope("storage-parsed-value");
valueScope.expanded = true;
const jsonVar = valueScope.addItem("", Object.create(null), {
relaxed: true,
@ -1211,7 +1275,7 @@ class StorageUI {
privateFields.push(f.name);
}
const columnName = getColumnName(type, f.name);
const columnName = this._getColumnName(type, f.name);
if (columnName) {
columns[f.name] = columnName;
} else if (!f.private) {
@ -1247,7 +1311,7 @@ class StorageUI {
if (item.expires != null) {
item.expires = item.expires
? new Date(item.expires).toUTCString()
: L10N.getStr("label.expires.session");
: this._l10nStrings.get("storage-expires-session");
}
if (item.creationTime != null) {
item.creationTime = new Date(item.creationTime).toUTCString();
@ -1360,21 +1424,19 @@ class StorageUI {
const separatorRegex = new RegExp(SEPARATOR_GUID, "g");
const label = addEllipsis((name + "").replace(separatorRegex, "-"));
this._tablePopupDelete.hidden = false;
this._tablePopupDelete.setAttribute(
"label",
L10N.getFormatStr("storage.popupMenu.deleteLabel", label)
"data-l10n-args",
JSON.stringify({
itemName: label,
})
);
this._tablePopupDelete.hidden = false;
} else {
this._tablePopupDelete.hidden = true;
}
if (this.supportsAddItem(type, host)) {
this._tablePopupAddItem.hidden = false;
this._tablePopupAddItem.setAttribute(
"label",
L10N.getFormatStr("storage.popupMenu.addItemLabel")
);
} else {
this._tablePopupAddItem.hidden = true;
}
@ -1391,11 +1453,13 @@ class StorageUI {
if (type === "cookies") {
const hostString = addEllipsis(data.host);
this._tablePopupDeleteAllFrom.hidden = false;
this._tablePopupDeleteAllFrom.setAttribute(
"label",
L10N.getFormatStr("storage.popupMenu.deleteAllFromLabel", hostString)
"data-l10n-args",
JSON.stringify({
host: hostString,
})
);
this._tablePopupDeleteAllFrom.hidden = false;
} else {
this._tablePopupDeleteAllFrom.hidden = true;
}
@ -1449,8 +1513,8 @@ class StorageUI {
if (showDelete) {
const itemName = addEllipsis(selectedItem[selectedItem.length - 1]);
this._treePopupDelete.setAttribute(
"label",
L10N.getFormatStr("storage.popupMenu.deleteLabel", itemName)
"data-l10n-args",
JSON.stringify({ itemName })
);
}
@ -1573,31 +1637,38 @@ class StorageUI {
}
}
removeDatabase(host, dbName) {
async removeDatabase(host, dbName) {
const front = this.getCurrentFront();
front
.removeDatabase(host, dbName)
.then(result => {
if (result.blocked) {
const notificationBox = this._toolbox.getNotificationBox();
notificationBox.appendNotification(
L10N.getFormatStr("storage.idb.deleteBlocked", dbName),
"storage-idb-delete-blocked",
null,
notificationBox.PRIORITY_WARNING_LOW
);
}
})
.catch(error => {
try {
const result = await front.removeDatabase(host, dbName);
if (result.blocked) {
const notificationBox = this._toolbox.getNotificationBox();
notificationBox.appendNotification(
L10N.getFormatStr("storage.idb.deleteError", dbName),
"storage-idb-delete-error",
null,
notificationBox.PRIORITY_CRITICAL_LOW
const message = await this._panelDoc.l10n.formatValue(
"storage-idb-delete-blocked",
{ dbName }
);
});
notificationBox.appendNotification(
message,
"storage-idb-delete-blocked",
null,
notificationBox.PRIORITY_WARNING_LOW
);
}
} catch (error) {
const notificationBox = this._toolbox.getNotificationBox();
const message = await this._panelDoc.l10n.formatValue(
"storage-idb-delete-error",
{ dbName }
);
notificationBox.appendNotification(
message,
"storage-idb-delete-error",
null,
notificationBox.PRIORITY_CRITICAL_LOW
);
}
}
removeCache(host, cacheName) {
@ -1633,33 +1704,3 @@ function addEllipsis(name) {
return name;
}
/**
* Get a string for a column name automatically choosing whether or not the
* string should be localized.
*
* @param {String} type
* The storage type.
* @param {String} name
* The field name that may need to be localized.
*/
function getColumnName(type, name) {
// Check if a l10n ID has been changed since it was created and map it to
// its new value if it has.
let id = `${type}.${name}`;
id = NON_ORIGINAL_L10N_IDS.get(id) || id;
// If the ID exists in NON_L10N_STRINGS then we do not translate it
// otherwise we get it from the L10N database. If it doesn't exist in the
// database we will just use the field name.
let columnName = NON_L10N_STRINGS.get(id);
if (!columnName) {
try {
columnName = L10N.getStr(`table.headers.${id}`);
} catch (e) {
columnName = name;
}
}
return columnName;
}

View File

@ -0,0 +1,123 @@
# coding=utf8
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
import fluent.syntax.ast as FTL
from fluent.migrate.transforms import COPY, REPLACE
from fluent.migrate.helpers import transforms_from, VARIABLE_REFERENCE
def migrate(ctx):
"""Bug 1763313 - Move storage.dtd to fluent, part {index}."""
ctx.add_transforms(
"devtools/client/storage.ftl",
"devtools/client/storage.ftl",
transforms_from(
"""
storage-search-box =
.placeholder = { COPY(from_path, "searchBox.placeholder") }
storage-context-menu-delete-all =
.label = { COPY(from_path, "storage.popupMenu.deleteAllLabel") }
storage-context-menu-delete-all-session-cookies =
.label = { COPY(from_path, "storage.popupMenu.deleteAllSessionCookiesLabel") }
storage-context-menu-copy =
.label = { COPY(from_path, "storage.popupMenu.copyLabel") }
""",
from_path="devtools/client/storage.dtd",
),
)
ctx.add_transforms(
"devtools/client/storage.ftl",
"devtools/client/storage.ftl",
[
FTL.Message(
id=FTL.Identifier("storage-context-menu-delete"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=REPLACE(
"devtools/client/storage.properties",
"storage.popupMenu.deleteLabel",
{
"%1$S": VARIABLE_REFERENCE("itemName"),
},
),
)
],
),
FTL.Message(
id=FTL.Identifier("storage-context-menu-delete-all-from"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=REPLACE(
"devtools/client/storage.properties",
"storage.popupMenu.deleteAllFromLabel",
{
"%1$S": VARIABLE_REFERENCE("host"),
},
),
)
],
),
FTL.Message(
id=FTL.Identifier("storage-idb-delete-blocked"),
value=REPLACE(
"devtools/client/storage.properties",
"storage.idb.deleteBlocked",
{"%1$S": VARIABLE_REFERENCE("dbName")},
),
),
FTL.Message(
id=FTL.Identifier("storage-idb-delete-error"),
value=REPLACE(
"devtools/client/storage.properties",
"storage.idb.deleteError",
{"%1$S": VARIABLE_REFERENCE("dbName")},
),
),
],
)
ctx.add_transforms(
"devtools/client/storage.ftl",
"devtools/client/storage.ftl",
transforms_from(
"""
storage-filter-key = { COPY(from_path, "storage.filter.key") }
storage-add-button =
.title = { COPY(from_path, "storage.popupMenu.addItemLabel") }
storage-refresh-button =
.title = { COPY(from_path, "storage.popupMenu.refreshItemLabel") }
storage-variable-view-search-box =
.placeholder = { COPY(from_path, "storage.search.placeholder") }
storage-context-menu-add-item =
.label = { COPY(from_path, "storage.popupMenu.addItemLabel") }
storage-expand-pane =
.title = { COPY(from_path, "storage.expandPane") }
storage-collapse-pane =
.title = { COPY(from_path, "storage.collapsePane") }
storage-expires-session = { COPY(from_path, "label.expires.session") }
storage-tree-labels-cookies = { COPY(from_path, "tree.labels.cookies") }
storage-tree-labels-local-storage = { COPY(from_path, "tree.labels.localStorage") }
storage-tree-labels-session-storage = { COPY(from_path, "tree.labels.sessionStorage") }
storage-tree-labels-indexed-db = { COPY(from_path, "tree.labels.indexedDB") }
storage-tree-labels-cache = { COPY(from_path, "tree.labels.Cache") }
storage-tree-labels-extension-storage = { COPY(from_path, "tree.labels.extensionStorage") }
storage-table-headers-cookies-name = { COPY(from_path, "table.headers.cookies.name") }
storage-table-headers-cookies-value = { COPY(from_path, "table.headers.cookies.value") }
storage-table-headers-cookies-expires = { COPY(from_path, "table.headers.cookies.expires2") }
storage-table-headers-cookies-size = { COPY(from_path, "table.headers.cookies.size") }
storage-table-headers-cookies-last-accessed = { COPY(from_path, "table.headers.cookies.lastAccessed2") }
storage-table-headers-cookies-creation-time = { COPY(from_path, "table.headers.cookies.creationTime2") }
storage-table-headers-cache-status = { COPY(from_path, "table.headers.Cache.status") }
storage-table-headers-extension-storage-area = { COPY(from_path, "table.headers.extensionStorage.area") }
storage-data = { COPY(from_path, "storage.data.label") }
storage-parsed-value = { COPY(from_path, "storage.parsedValue.label") }
""",
from_path="devtools/client/storage.properties",
),
)