v3.25.0 review: the access-rules open-state lifecycle
- profilerEdit resets the expanded-block map, so opening another profile (or a freshly scanned one) starts with every access-rules block collapsed instead of inheriting the previous profile's state. - Deleting a rule re-keys the map (profilerArrShiftOpen): the entries below stay, the ones above shift down, so the expanded blocks stay with their rules. - The declaration sits before the render function that reads it. - Test fixture: a real command-description tag (0x85) instead of the RESIZE branch's 0x84. Tests: the shift helper, the profilerEdit reset pin, the existing editor tests. 883 frontend + 858 Python; sw cache simple-v424.
This commit is contained in:
+20
-4
@@ -19545,6 +19545,8 @@ function profilerEdit(i) {
|
|||||||
const p = profiles[i];
|
const p = profiles[i];
|
||||||
profilerEditId = p.id;
|
profilerEditId = p.id;
|
||||||
profilerDraft = JSON.parse(JSON.stringify(p));
|
profilerDraft = JSON.parse(JSON.stringify(p));
|
||||||
|
// a fresh draft starts with every access-rules block collapsed
|
||||||
|
_profilerArrOpen = {};
|
||||||
profilerSetView('editor');
|
profilerSetView('editor');
|
||||||
document.getElementById('profiler-name').value = profilerDraft.name;
|
document.getElementById('profiler-name').value = profilerDraft.name;
|
||||||
profilerRenderEditor();
|
profilerRenderEditor();
|
||||||
@@ -19733,6 +19735,7 @@ function profilerAddRule() {
|
|||||||
function profilerDeleteRule(i) {
|
function profilerDeleteRule(i) {
|
||||||
if (!profilerDraft) return;
|
if (!profilerDraft) return;
|
||||||
profilerDraft.rules.splice(i, 1);
|
profilerDraft.rules.splice(i, 1);
|
||||||
|
_profilerArrOpen = profilerArrShiftOpen(_profilerArrOpen, i);
|
||||||
profilerRenderEditor();
|
profilerRenderEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19807,6 +19810,11 @@ function profilerRenderRule(i, r) {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The expanded access-rule blocks of the profile editor (transient UI state,
|
||||||
|
// kept across the editor re-renders; None hides the block entirely; a fresh
|
||||||
|
// draft resets it and a deleted rule shifts the keys).
|
||||||
|
let _profilerArrOpen = {};
|
||||||
|
|
||||||
// Access-rules check editor (v3.25.0): captured rules open as structured rows
|
// Access-rules check editor (v3.25.0): captured rules open as structured rows
|
||||||
// (operation bits / command + security conditions) and hand-made rules can be
|
// (operation bits / command + security conditions) and hand-made rules can be
|
||||||
// built the same way; every change re-encodes the canonical decoded line the
|
// built the same way; every change re-encodes the canonical decoded line the
|
||||||
@@ -19844,15 +19852,23 @@ function profilerRenderAccessRulesEditor(i, r) {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The expanded access-rule blocks of the profile editor (transient UI state,
|
|
||||||
// kept across the editor re-renders; None hides the block entirely).
|
|
||||||
let _profilerArrOpen = {};
|
|
||||||
|
|
||||||
function profilerArrToggleOpen(i) {
|
function profilerArrToggleOpen(i) {
|
||||||
_profilerArrOpen[i] = !_profilerArrOpen[i];
|
_profilerArrOpen[i] = !_profilerArrOpen[i];
|
||||||
profilerRenderEditor();
|
profilerRenderEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-key the open-state map after a rule at `removed` was deleted: the
|
||||||
|
// entries below stay, the ones above shift down by one.
|
||||||
|
function profilerArrShiftOpen(open, removed) {
|
||||||
|
const next = {};
|
||||||
|
for (const k of Object.keys(open)) {
|
||||||
|
const n = Number(k);
|
||||||
|
if (n < removed) next[n] = open[k];
|
||||||
|
else if (n > removed) next[n - 1] = open[k];
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
// One expected rule row: the operation form + bit checkboxes (or the RESIZE /
|
// One expected rule row: the operation form + bit checkboxes (or the RESIZE /
|
||||||
// INCREASE command), the condition rows and the canonical preview line.
|
// INCREASE command), the condition rows and the canonical preview line.
|
||||||
function profilerArrRuleHtml(i, ri, line) {
|
function profilerArrRuleHtml(i, ri, line) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function extractFunc(src, name, asyncFn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FNS = ['profilerNormHex', 'profilerNormHexStrict', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile', 'profilerCustomNameForPath', 'pysimCustomNormPath', 'profilerUpdateRulePath', 'profilerResultAspects', 'profilerAspectSummary', 'profilerNumRanges', 'profilerMatchedRecordsText', 'profilerCloneName', 'profilerClone', 'profilerNewId', 'esc', 'escHtml', 'profilerRawDataCheck', 'profilerRenderReport', 'parseBerLen', 'parseTlvList', 'fcpInt', 'fcpParseTlvs', 'fcpFileDescriptor', 'fcpLifeCycle', 'fcpSfi', 'fcpDo', 'fcpDecode',
|
const FNS = ['profilerNormHex', 'profilerNormHexStrict', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile', 'profilerCustomNameForPath', 'pysimCustomNormPath', 'profilerUpdateRulePath', 'profilerResultAspects', 'profilerAspectSummary', 'profilerNumRanges', 'profilerMatchedRecordsText', 'profilerCloneName', 'profilerClone', 'profilerNewId', 'esc', 'escHtml', 'profilerRawDataCheck', 'profilerRenderReport', 'parseBerLen', 'parseTlvList', 'fcpInt', 'fcpParseTlvs', 'fcpFileDescriptor', 'fcpLifeCycle', 'fcpSfi', 'fcpDo', 'fcpDecode',
|
||||||
'fcpArrRef', 'fcpSeidName', 'fcpArrRules', 'fcpArrRefFromFci', 'fcpWithoutArrRef', 'fcpArrSplitTop', 'fcpArrKeyRefFromName', 'fcpArrKeyRefOptions', 'fcpArrScParse', 'fcpArrScEncode', 'fcpArrRuleParse', 'fcpArrRuleEncode', 'fcpArrRuleToggleBit', 'fcpArrRuleSetKind', 'fcpArrRuleSetSc', 'fcpArrRuleSetUq', 'fcpArrRuleAddSc', 'fcpArrRuleRemoveSc', 'profilerRenderAccessRulesEditor', 'profilerArrRuleHtml', 'profilerArrScSelect', 'profilerArrUqSelect', 'profilerArrToggleOpen', 'profilerAccessRulesDiffHtml', 'profilerSnapshotArr', 'efBytes', 'efHex', 'efTlv', 'efDecArr', 'efArrAmLabel', 'efArrSc', 'efArrAmDo', 'efFidFromPath',
|
'fcpArrRef', 'fcpSeidName', 'fcpArrRules', 'fcpArrRefFromFci', 'fcpWithoutArrRef', 'fcpArrSplitTop', 'fcpArrKeyRefFromName', 'fcpArrKeyRefOptions', 'fcpArrScParse', 'fcpArrScEncode', 'fcpArrRuleParse', 'fcpArrRuleEncode', 'fcpArrRuleToggleBit', 'fcpArrRuleSetKind', 'fcpArrRuleSetSc', 'fcpArrRuleSetUq', 'fcpArrRuleAddSc', 'fcpArrRuleRemoveSc', 'profilerRenderAccessRulesEditor', 'profilerArrRuleHtml', 'profilerArrScSelect', 'profilerArrUqSelect', 'profilerArrToggleOpen', 'profilerArrShiftOpen', 'profilerAccessRulesDiffHtml', 'profilerSnapshotArr', 'efBytes', 'efHex', 'efTlv', 'efDecArr', 'efArrAmLabel', 'efArrSc', 'efArrAmDo', 'efFidFromPath',
|
||||||
'pinKeyRefName', 'fcpPsTemplate', 'fcpDiffHtml', 'profilerFciPreviewItems', 'profilerUpdateFciPreview', 'profilerUpdateRule', 'profilerFciInput', 'profilerScanToggleAll', 'profilerScanIgnoreAllState', 'swapNibbles', 'decIccid', 'profilerSnapshotIccid', 'profilerValidateSnapshot', 'profilerListSwitch', 'profilerScanRefreshOptions', 'profilerLiveSource', 'profilerSnapshotSource', 'profilerVisibleResults', 'profilerRulesFromSnapshot', 'profilerExtraFileResults', 'profilerScanNameKeydown', 'profilerTimingStats', 'profilerTimingAccumulator', 'profilerFormatMs', 'profilerRenderSnapshotSummary', 'profilerSnapshotCountLabel', 'pysimFsInfoHtml', 'profilerLabelText', 'profilerResultsHeaderText', 'profilerRenderResultsView', 'profilerBuildFileRuleFromSnapshot', 'profilerSnapshotPickListHtml', 'profilerScanSetTarget'];
|
'pinKeyRefName', 'fcpPsTemplate', 'fcpDiffHtml', 'profilerFciPreviewItems', 'profilerUpdateFciPreview', 'profilerUpdateRule', 'profilerFciInput', 'profilerScanToggleAll', 'profilerScanIgnoreAllState', 'swapNibbles', 'decIccid', 'profilerSnapshotIccid', 'profilerValidateSnapshot', 'profilerListSwitch', 'profilerScanRefreshOptions', 'profilerLiveSource', 'profilerSnapshotSource', 'profilerVisibleResults', 'profilerRulesFromSnapshot', 'profilerExtraFileResults', 'profilerScanNameKeydown', 'profilerTimingStats', 'profilerTimingAccumulator', 'profilerFormatMs', 'profilerRenderSnapshotSummary', 'profilerSnapshotCountLabel', 'pysimFsInfoHtml', 'profilerLabelText', 'profilerResultsHeaderText', 'profilerRenderResultsView', 'profilerBuildFileRuleFromSnapshot', 'profilerSnapshotPickListHtml', 'profilerScanSetTarget'];
|
||||||
let code = '';
|
let code = '';
|
||||||
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
||||||
@@ -1078,7 +1078,7 @@ test('fcpArrRuleParse/Encode round-trips the decoded rule forms', () => {
|
|||||||
for (const raw of [
|
for (const raw of [
|
||||||
'AM 0x02 (UPDATE/ERASE (EF) / CREATE FILE EF (DF)): (PIN app1 (verify) OR PIN app2 (verify))',
|
'AM 0x02 (UPDATE/ERASE (EF) / CREATE FILE EF (DF)): (PIN app1 (verify) OR PIN app2 (verify))',
|
||||||
'AM 0x01 (READ/SEARCH (EF) / DELETE FILE child (DF)): SM CRT 0xB4',
|
'AM 0x01 (READ/SEARCH (EF) / DELETE FILE child (DF)): SM CRT 0xB4',
|
||||||
'command description 0x84 (D4)',
|
'command description 0x85 (D4)',
|
||||||
]) {
|
]) {
|
||||||
const rm = fcpArrRuleParse(raw);
|
const rm = fcpArrRuleParse(raw);
|
||||||
assert.strictEqual(rm.raw, raw, raw);
|
assert.strictEqual(rm.raw, raw, raw);
|
||||||
@@ -1104,8 +1104,8 @@ test('fcpArrRule mutators re-encode the canonical line', () => {
|
|||||||
m = fcpArrRuleSetSc(m, 0, 'never');
|
m = fcpArrRuleSetSc(m, 0, 'never');
|
||||||
assert.strictEqual(fcpArrRuleEncode(m), 'RESIZE FILE (AM_DO 0x84, INS 0xD4): never');
|
assert.strictEqual(fcpArrRuleEncode(m), 'RESIZE FILE (AM_DO 0x84, INS 0xD4): never');
|
||||||
// a raw model is never modified
|
// a raw model is never modified
|
||||||
const raw = fcpArrRuleParse('command description 0x84 (D4)');
|
const raw = fcpArrRuleParse('command description 0x85 (D4)');
|
||||||
assert.strictEqual(fcpArrRuleEncode(fcpArrRuleToggleBit(raw, 0x01, true)), 'command description 0x84 (D4)');
|
assert.strictEqual(fcpArrRuleEncode(fcpArrRuleToggleBit(raw, 0x01, true)), 'command description 0x85 (D4)');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('the access-rules editor renders structured rows', () => {
|
test('the access-rules editor renders structured rows', () => {
|
||||||
@@ -1154,9 +1154,17 @@ test('the access-rules editor renders structured rows', () => {
|
|||||||
_profilerArrOpen = {};
|
_profilerArrOpen = {};
|
||||||
// Check opens the block, None hides it
|
// Check opens the block, None hides it
|
||||||
assert.ok(html.includes('_profilerArrOpen[i] = on;'), 'the radio drives the visibility');
|
assert.ok(html.includes('_profilerArrOpen[i] = on;'), 'the radio drives the visibility');
|
||||||
|
// a fresh draft resets the open blocks
|
||||||
|
assert.ok(html.includes("_profilerArrOpen = {};\n\tprofilerSetView('editor');"), 'profilerEdit resets the blocks');
|
||||||
delete global.t;
|
delete global.t;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('the access-rule open state survives rule deletion', () => {
|
||||||
|
assert.deepStrictEqual(profilerArrShiftOpen({ 0: true, 2: true, 3: true }, 1), { 0: true, 1: true, 2: true });
|
||||||
|
assert.deepStrictEqual(profilerArrShiftOpen({ 0: true, 2: true, 3: true }, 2), { 0: true, 2: true });
|
||||||
|
assert.deepStrictEqual(profilerArrShiftOpen({}, 0), {});
|
||||||
|
});
|
||||||
|
|
||||||
test('profilerResultAspects and the summary include Access rules', () => {
|
test('profilerResultAspects and the summary include Access rules', () => {
|
||||||
const res = { checks: [
|
const res = { checks: [
|
||||||
{ label: 'exists', ok: true },
|
{ label: 'exists', ok: true },
|
||||||
|
|||||||
Reference in New Issue
Block a user