зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 4 changesets (bug 1635117) for bc failures on browser_aboutprocesses_show_all_frames.js CLOSED TREE
Backed out changeset c03d67845003 (bug 1635117) Backed out changeset 6bc615756d14 (bug 1635117) Backed out changeset 7abc4ea63c85 (bug 1635117) Backed out changeset d91cc348528f (bug 1635117)
This commit is contained in:
Родитель
3622eac00b
Коммит
4f46d210b3
|
@ -26,21 +26,21 @@ about-processes-column-cpu-total = CPU
|
|||
|
||||
about-processes-browser-process-name = { -brand-short-name } (process { $pid })
|
||||
about-processes-web-process-name = Web (process { $pid }, shared)
|
||||
about-processes-web-isolated-process-name = Web (process { $pid }) for { $origin }
|
||||
about-processes-web-large-allocation = Web (process { $pid }, large) for { $origin }
|
||||
about-processes-with-coop-coep-process-name = Web (process { $pid }, cross-origin isolated) for { $origin }
|
||||
about-processes-webIsolated-process-name = Web (process { $pid }) for { $origin }
|
||||
about-processes-webLargeAllocation = Web (process { $pid }, large) for { $origin }
|
||||
about-processes-withCoopCoep-process-name = Web (process { $pid }, cross-origin isolated) for { $origin }
|
||||
about-processes-file-process-name = Files (process { $pid })
|
||||
about-processes-extension-process-name = Extensions (process { $pid })
|
||||
about-processes-privilegedabout-process-name = About (process { $pid })
|
||||
about-processes-plugin-process-name = Plugins (process { $pid })
|
||||
about-processes-privilegedmozilla-process-name = Web (process { $pid }) for { -vendor-short-name } sites
|
||||
about-processes-gmp-plugin-process-name = Gecko Media Plugins (process { $pid })
|
||||
about-processes-gmpPlugin-process-name = Gecko Media Plugins (process { $pid })
|
||||
about-processes-gpu-process-name = GPU (process { $pid })
|
||||
about-processes-vr-process-name = VR (process { $pid })
|
||||
about-processes-rdd-process-name = Data Decoder (process { $pid })
|
||||
about-processes-socket-process-name = Network (process { $pid })
|
||||
about-processes-remote-sandbox-broker-process-name = Remote Sandbox Broker (process { $pid })
|
||||
about-processes-fork-server-process-name = Fork Server (process { $pid })
|
||||
about-processes-remoteSandboxBroker-process-name = Remote Sandbox Broker (process { $pid })
|
||||
about-processes-forkServer-process-name = Fork Server (process { $pid })
|
||||
about-processes-preallocated-process-name = Preallocated (process { $pid })
|
||||
about-processes-unknown-process-name = Other ({ $type }, process { $pid })
|
||||
|
||||
|
@ -82,38 +82,3 @@ about-processes-frame-name-one = Subframe: { $url }
|
|||
# $number (Number) The number of subframes in this group. Always ≥ 1.
|
||||
# $shortUrl (String) The shared prefix for the subframes in the group.
|
||||
about-processes-frame-name-many = Subframes ({ $number }): { $shortUrl }
|
||||
|
||||
## Displaying CPU (percentage and total)
|
||||
## Variables:
|
||||
## $percent (Number) The percentage of CPU used by the process or thread.
|
||||
## Always > 0, generally <= 200.
|
||||
## $total (Number) The amount of time used by the process or thread since
|
||||
## its start.
|
||||
## $unit (String) The unit in which to display $total. One of "ns", "µs", "ms",
|
||||
## "s", "m", "h", "d".
|
||||
|
||||
# Common case.
|
||||
about-processes-cpu-user-and-kernel = { NUMBER($percent, maximumSignificantDigits: 2, style: "percent") } ({ NUMBER($total, maximumFractionDigits: 0) }{ $unit })
|
||||
|
||||
# Special case: data is not available yet.
|
||||
about-processes-cpu-user-and-kernel-not-ready = (measuring)
|
||||
|
||||
# Special case: process or thread is currently idle.
|
||||
about-processes-cpu-user-and-kernel-idle = idle ({ NUMBER($total, maximumFractionDigits: 2) }{ $unit })
|
||||
|
||||
## Displaying Memory (total and delta)
|
||||
## Variables:
|
||||
## $total (Number) The amount of memory currently used by the process.
|
||||
## $totalUnit (String) The unit in which to display $total. One of "B",
|
||||
## "KB", "MB", "GB".
|
||||
## $delta (Number) The absolute value of the amount of memory added recently.
|
||||
## $deltaSign (String) Either "+" if the amount of memory has increased
|
||||
## or "-" if it has decreased.
|
||||
## $deltaUnit (String) The unit in which to display $delta. One of "B",
|
||||
## "KB", "MB", "GB".
|
||||
|
||||
# Common case.
|
||||
about-processes-total-memory-size = { NUMBER($total, maximumFractionDigits:0) }{ $totalUnit } ({ $deltaSign }{ NUMBER($delta, maximumFractionDigits:0) }{ $deltaUnit })
|
||||
|
||||
# Special case: no change.
|
||||
about-processes-total-memory-size-no-change = { NUMBER($total, maximumFractionDigits:0) }{ $totalUnit }
|
|
@ -8,7 +8,7 @@
|
|||
<title data-l10n-id="about-processes-title"></title>
|
||||
<link rel="icon" id="favicon" href="chrome://global/skin/icons/performance.svg">
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
|
||||
<link rel="localization" href="toolkit/about/aboutProcesses.ftl">
|
||||
<link rel="localization" href="preview/aboutProcesses.ftl">
|
||||
<link rel="localization" href="branding/brand.ftl"/>
|
||||
<script src="chrome://global/content/aboutProcesses.js"></script>
|
||||
<link rel="stylesheet" href="chrome://global/content/aboutProcesses.css">
|
||||
|
|
|
@ -19,12 +19,8 @@ const BUFFER_DURATION_MS = 10000;
|
|||
// How often we should update
|
||||
const UPDATE_INTERVAL_MS = 2000;
|
||||
|
||||
const NS_PER_US = 1000;
|
||||
const NS_PER_MS = 1000000;
|
||||
const MS_PER_NS = 1000000;
|
||||
const NS_PER_S = 1000000000;
|
||||
const NS_PER_MIN = NS_PER_S * 60;
|
||||
const NS_PER_HOUR = NS_PER_MIN * 60;
|
||||
const NS_PER_DAY = NS_PER_HOUR * 24;
|
||||
|
||||
const ONE_GIGA = 1024 * 1024 * 1024;
|
||||
const ONE_MEGA = 1024 * 1024;
|
||||
|
@ -317,7 +313,7 @@ var State = {
|
|||
if (prev.pid != cur.pid) {
|
||||
throw new Error("Assertion failed: A process cannot change pid.");
|
||||
}
|
||||
let deltaT = (cur.date - prev.date) * NS_PER_MS;
|
||||
let deltaT = (cur.date - prev.date) * MS_PER_NS;
|
||||
let threads = null;
|
||||
if (SHOW_THREADS) {
|
||||
let prevThreads = new Map();
|
||||
|
@ -423,10 +419,10 @@ var View = {
|
|||
fluentName = "about-processes-web-process-name";
|
||||
break;
|
||||
case "webIsolated":
|
||||
fluentName = "about-processes-web-isolated-process-name";
|
||||
fluentName = "about-processes-webIsolated-process-name";
|
||||
break;
|
||||
case "webLargeAllocation":
|
||||
fluentName = "about-processes-web-large-allocation-process-name";
|
||||
fluentName = "about-processes-webLargeAllocation-process-name";
|
||||
break;
|
||||
case "file":
|
||||
fluentName = "about-processes-file-process-name";
|
||||
|
@ -439,7 +435,7 @@ var View = {
|
|||
fluentName = "about-processes-privilegedabout-process-name";
|
||||
break;
|
||||
case "withCoopCoep":
|
||||
fluentName = "about-processes-with-coop-coep-process-name";
|
||||
fluentName = "about-processes-withCoopCoep-process-name";
|
||||
break;
|
||||
case "browser":
|
||||
fluentName = "about-processes-browser-process-name";
|
||||
|
@ -448,7 +444,7 @@ var View = {
|
|||
fluentName = "about-processes-plugin-process-name";
|
||||
break;
|
||||
case "gmpPlugin":
|
||||
fluentName = "about-processes-gmp-plugin-process-name";
|
||||
fluentName = "about-processes-gmpPlugin-process-name";
|
||||
break;
|
||||
case "gpu":
|
||||
fluentName = "about-processes-gpu-process-name";
|
||||
|
@ -463,10 +459,10 @@ var View = {
|
|||
fluentName = "about-processes-socket-process-name";
|
||||
break;
|
||||
case "remoteSandboxBroker":
|
||||
fluentName = "about-processes-remote-sandbox-broker-process-name";
|
||||
fluentName = "about-processes-remoteSandboxBroker-process-name";
|
||||
break;
|
||||
case "forkServer":
|
||||
fluentName = "about-processes-fork-server-process-name";
|
||||
fluentName = "about-processes-forkServer-process-name";
|
||||
break;
|
||||
case "preallocated":
|
||||
fluentName = "about-processes-preallocated-process-name";
|
||||
|
@ -530,60 +526,29 @@ var View = {
|
|||
|
||||
// Column: Resident size
|
||||
{
|
||||
let formattedTotal = this._formatMemory(data.totalResidentUniqueSize);
|
||||
if (data.deltaResidentUniqueSize) {
|
||||
let formattedDelta = this._formatMemory(data.deltaResidentUniqueSize);
|
||||
this._addCell(row, {
|
||||
fluentName: "about-processes-total-memory-size",
|
||||
fluentArgs: {
|
||||
total: formattedTotal.amount,
|
||||
totalUnit: formattedTotal.unit,
|
||||
delta: Math.abs(formattedDelta.amount),
|
||||
deltaUnit: formattedDelta.unit,
|
||||
deltaSign: data.deltaResidentUniqueSize > 0 ? "+" : "-",
|
||||
},
|
||||
classes: ["totalMemorySize"],
|
||||
});
|
||||
} else {
|
||||
this._addCell(row, {
|
||||
fluentName: "about-processes-total-memory-size-no-change",
|
||||
fluentArgs: {
|
||||
total: formattedTotal.amount,
|
||||
totalUnit: formattedTotal.unit,
|
||||
},
|
||||
classes: ["totalMemorySize"],
|
||||
});
|
||||
}
|
||||
let { formatedDelta, formatedValue } = this._formatMemoryAndDelta(
|
||||
data.totalResidentUniqueSize,
|
||||
data.deltaResidentUniqueSize
|
||||
);
|
||||
let content = formatedDelta
|
||||
? `${formatedValue}${formatedDelta}`
|
||||
: formatedValue;
|
||||
this._addCell(row, {
|
||||
content,
|
||||
classes: ["totalMemorySize"],
|
||||
});
|
||||
}
|
||||
|
||||
// Column: CPU: User and Kernel
|
||||
{
|
||||
let { duration, unit } = this._getDuration(data.totalCpu);
|
||||
if (data.slopeCpu == null) {
|
||||
this._addCell(row, {
|
||||
fluentName: "about-processes-cpu-user-and-kernel-not-ready",
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else if (data.slopeCpu == 0) {
|
||||
this._addCell(row, {
|
||||
fluentName: "about-processes-cpu-user-and-kernel-idle",
|
||||
fluentArgs: {
|
||||
total: duration,
|
||||
unit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else {
|
||||
this._addCell(row, {
|
||||
fluentName: "about-processes-cpu-user-and-kernel",
|
||||
fluentArgs: {
|
||||
percent: data.slopeCpu * 100,
|
||||
total: duration,
|
||||
unit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
}
|
||||
let slope = this._formatPercentage(data.slopeCpu);
|
||||
let content = `${slope} (${(
|
||||
data.totalCpu / MS_PER_NS
|
||||
).toLocaleString(undefined, { maximumFractionDigits: 0 })}ms)`;
|
||||
this._addCell(row, {
|
||||
content,
|
||||
classes: ["cpu"],
|
||||
});
|
||||
}
|
||||
|
||||
// Column: Kill button – but not for all processes.
|
||||
|
@ -785,32 +750,14 @@ var View = {
|
|||
|
||||
// Column: CPU: User and Kernel
|
||||
{
|
||||
let { duration, unit } = this._getDuration(data.totalCpu);
|
||||
if (data.slopeCpu == null) {
|
||||
this._addCell(row, {
|
||||
fluentName: "about-processes-cpu-user-and-kernel-not-ready",
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else if (data.slopeCpu == 0) {
|
||||
this._addCell(row, {
|
||||
fluentName: "about-processes-cpu-user-and-kernel-idle",
|
||||
fluentArgs: {
|
||||
total: duration,
|
||||
unit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
} else {
|
||||
this._addCell(row, {
|
||||
fluentName: "about-processes-cpu-user-and-kernel",
|
||||
fluentArgs: {
|
||||
percent: data.slopeCpu * 100,
|
||||
total: duration,
|
||||
unit,
|
||||
},
|
||||
classes: ["cpu"],
|
||||
});
|
||||
}
|
||||
let slope = this._formatPercentage(data.slopeCpu);
|
||||
let text = `${slope} (${(
|
||||
data.totalCpu / MS_PER_NS
|
||||
).toLocaleString(undefined, { maximumFractionDigits: 0 })} ms)`;
|
||||
this._addCell(row, {
|
||||
content: text,
|
||||
classes: ["cpu"],
|
||||
});
|
||||
}
|
||||
|
||||
// Column: Buttons (empty)
|
||||
|
@ -838,26 +785,43 @@ var View = {
|
|||
return elt;
|
||||
},
|
||||
|
||||
_getDuration(rawDurationNS) {
|
||||
if (rawDurationNS <= NS_PER_US) {
|
||||
return { duration: rawDurationNS, unit: "ns" };
|
||||
/**
|
||||
* Utility method to format an optional percentage.
|
||||
*
|
||||
* As a special case, we also handle `null`, which represents the case in which we do
|
||||
* not have sufficient information to compute a percentage.
|
||||
*
|
||||
* @param {Number?} value The value to format. Must be either `null` or a non-negative number.
|
||||
* A value of 1 means 100%. A value larger than 1 is possible as processes can use several
|
||||
* cores.
|
||||
* @return {String}
|
||||
*/
|
||||
_formatPercentage(value) {
|
||||
if (value == null) {
|
||||
return "?";
|
||||
}
|
||||
if (rawDurationNS <= NS_PER_MS) {
|
||||
return { duration: rawDurationNS / NS_PER_US, unit: "µs" };
|
||||
if (value < 0 || typeof value != "number") {
|
||||
throw new Error(`Invalid percentage value ${value}`);
|
||||
}
|
||||
if (rawDurationNS <= NS_PER_S) {
|
||||
return { duration: rawDurationNS / NS_PER_MS, unit: "ms" };
|
||||
if (value == 0) {
|
||||
// Let's make sure that we do not confuse idle and "close to 0%",
|
||||
// otherwise this results in weird displays.
|
||||
return "idle";
|
||||
}
|
||||
if (rawDurationNS <= NS_PER_MIN) {
|
||||
return { duration: rawDurationNS / NS_PER_S, unit: "s" };
|
||||
// Now work with actual percentages.
|
||||
let percentage = value * 100;
|
||||
if (percentage < 0.01) {
|
||||
// Tiny percentage, let's display something more useful than "0".
|
||||
return "~0%";
|
||||
}
|
||||
if (rawDurationNS <= NS_PER_HOUR) {
|
||||
return { duration: rawDurationNS / NS_PER_MIN, unit: "m" };
|
||||
if (percentage < 1) {
|
||||
// Still a small percentage, but it should fit within 2 digits.
|
||||
return `${percentage.toLocaleString(undefined, {
|
||||
maximumFractionDigits: 2,
|
||||
})}%`;
|
||||
}
|
||||
if (rawDurationNS <= NS_PER_DAY) {
|
||||
return { duration: rawDurationNS / NS_PER_HOUR, unit: "h" };
|
||||
}
|
||||
return { duration: rawDurationNS / NS_PER_DAY, unit: "d" };
|
||||
// For other percentages, just return a round number.
|
||||
return `${Math.round(percentage)}%`;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -874,31 +838,69 @@ var View = {
|
|||
if (value == null) {
|
||||
return { unit: "?", amount: 0 };
|
||||
}
|
||||
if (typeof value != "number") {
|
||||
if (value < 0 || typeof value != "number") {
|
||||
throw new Error(`Invalid memory value ${value}`);
|
||||
}
|
||||
let abs = Math.abs(value);
|
||||
if (abs >= ONE_GIGA) {
|
||||
if (value >= ONE_GIGA) {
|
||||
return {
|
||||
unit: "GB",
|
||||
amount: value / ONE_GIGA,
|
||||
amount: Math.ceil((value / ONE_GIGA) * 100) / 100,
|
||||
};
|
||||
}
|
||||
if (abs >= ONE_MEGA) {
|
||||
if (value >= ONE_MEGA) {
|
||||
return {
|
||||
unit: "MB",
|
||||
amount: value / ONE_MEGA,
|
||||
amount: Math.ceil((value / ONE_MEGA) * 100) / 100,
|
||||
};
|
||||
}
|
||||
if (abs >= ONE_KILO) {
|
||||
if (value >= ONE_KILO) {
|
||||
return {
|
||||
unit: "KB",
|
||||
amount: value / ONE_KILO,
|
||||
amount: Math.ceil((value / ONE_KILO) * 100) / 100,
|
||||
};
|
||||
}
|
||||
return {
|
||||
unit: "B",
|
||||
amount: value,
|
||||
amount: Math.round(value),
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Format a value representing an amount of memory and a delta.
|
||||
*
|
||||
* @param {Number?} value The value to format. Must be either `null` or a non-negative number.
|
||||
* @param {Number?} value The delta to format. Must be either `null` or a non-negative number.
|
||||
* @return {
|
||||
* {unitValue: "GB" | "MB" | "KB" | B" | "?"},
|
||||
* formatedValue: string,
|
||||
* {unitDelta: "GB" | "MB" | "KB" | B" | "?"},
|
||||
* formatedDelta: string
|
||||
* }
|
||||
*/
|
||||
_formatMemoryAndDelta(value, delta) {
|
||||
let formatedDelta;
|
||||
let unitDelta;
|
||||
if (delta == null) {
|
||||
formatedDelta == "";
|
||||
unitDelta = null;
|
||||
} else if (delta == 0) {
|
||||
formatedDelta = null;
|
||||
unitDelta = null;
|
||||
} else if (delta >= 0) {
|
||||
let { unit, amount } = this._formatMemory(delta);
|
||||
formatedDelta = ` (+${amount}${unit})`;
|
||||
unitDelta = unit;
|
||||
} else {
|
||||
let { unit, amount } = this._formatMemory(-delta);
|
||||
formatedDelta = ` (-${amount}${unit})`;
|
||||
unitDelta = unit;
|
||||
}
|
||||
let { unit: unitValue, amount } = this._formatMemory(value);
|
||||
return {
|
||||
unitValue,
|
||||
unitDelta,
|
||||
formatedDelta,
|
||||
formatedValue: `${amount}${unitValue}`,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
@ -30,11 +30,11 @@ const HARDCODED_ASSUMPTIONS_THREAD = {
|
|||
};
|
||||
|
||||
// How close we accept our rounding up/down.
|
||||
const APPROX_FACTOR = 1.51;
|
||||
const APPROX_FACTOR = 1.1;
|
||||
const MS_PER_NS = 1000000;
|
||||
const MEMORY_REGEXP = /([0-9.,]+)(TB|GB|MB|KB|B)( \(([-+]?)([0-9.,]+)(GB|MB|KB|B)\))?/;
|
||||
//Example: "383.55MB (-12.5MB)"
|
||||
const CPU_REGEXP = /(\~0%|idle|[0-9.,]+%|[?]) \(([0-9.,]+) ?(ns|ms|s|min)\)/;
|
||||
const CPU_REGEXP = /(\~0%|idle|[0-9.,]+%|[?]) \(([0-9.,]+) ?(ms)\)/;
|
||||
//Example: "13% (4,470ms)"
|
||||
|
||||
// Wait for `about:processes` to be updated.
|
||||
|
@ -126,26 +126,13 @@ function getMemoryMultiplier(unit, sign = "+") {
|
|||
}
|
||||
|
||||
function getTimeMultiplier(unit) {
|
||||
switch (unit) {
|
||||
case "ns":
|
||||
return 0.0000001;
|
||||
case "µs":
|
||||
return 0.0001;
|
||||
case "ms":
|
||||
return 1;
|
||||
case "s":
|
||||
return 1000;
|
||||
case "m":
|
||||
return 60000;
|
||||
if (unit == "ms") {
|
||||
return 1;
|
||||
}
|
||||
throw new Error("Invalid time unit: " + unit);
|
||||
}
|
||||
function testCpu(string, total, slope, assumptions) {
|
||||
info(`Testing CPU display ${string} vs total ${total}, slope ${slope}`);
|
||||
if (string == "(measuring)") {
|
||||
info("Still measuring");
|
||||
return;
|
||||
}
|
||||
let [, extractedPercentage, extractedTotal, extractedUnit] = CPU_REGEXP.exec(
|
||||
string
|
||||
);
|
||||
|
@ -580,31 +567,12 @@ async function testAboutProcessesWithConfig({ showAllFrames, showThreads }) {
|
|||
Assert.ok(
|
||||
numberOfThreads <= HARDCODED_ASSUMPTIONS_PROCESS.maximalNumberOfThreads
|
||||
);
|
||||
Assert.equal(
|
||||
numberOfThreads,
|
||||
row.process.threads.length,
|
||||
"The number we display should be the number of threads"
|
||||
);
|
||||
Assert.equal(numberOfThreads, row.process.threads.length);
|
||||
|
||||
info("Testing that we can open the list of threads");
|
||||
let twisty = threads.row.getElementsByClassName("twisty")[0];
|
||||
twisty.click();
|
||||
|
||||
// Since `twisty.click()` is partially async, we need to wait
|
||||
// until all the threads are properly displayed.
|
||||
await promiseAboutProcessesUpdated({ doc, tbody, tabAboutProcesses });
|
||||
let numberOfThreadsFound = 0;
|
||||
await TestUtils.waitForCondition(() => {
|
||||
numberOfThreadsFound = 0;
|
||||
for (
|
||||
let threadRow = threads.row.nextSibling;
|
||||
threadRow && threadRow.classList.contains("thread");
|
||||
threadRow = threadRow.nextSibling
|
||||
) {
|
||||
numberOfThreadsFound++;
|
||||
}
|
||||
return numberOfThreadsFound == numberOfThreads;
|
||||
}, `We should see ${numberOfThreads} threads, found ${numberOfThreadsFound}`);
|
||||
for (
|
||||
let threadRow = threads.row.nextSibling;
|
||||
threadRow && threadRow.classList.contains("thread");
|
||||
|
@ -614,6 +582,7 @@ async function testAboutProcessesWithConfig({ showAllFrames, showThreads }) {
|
|||
let cpuContent = children[2].textContent;
|
||||
let tidContent = document.l10n.getAttributes(children[0].children[0])
|
||||
.args.tid;
|
||||
++numberOfThreadsFound;
|
||||
|
||||
info("Sanity checks: tid");
|
||||
let tid = Number.parseInt(tidContent);
|
||||
|
@ -628,6 +597,11 @@ async function testAboutProcessesWithConfig({ showAllFrames, showThreads }) {
|
|||
HARDCODED_ASSUMPTIONS_THREAD
|
||||
);
|
||||
}
|
||||
Assert.equal(
|
||||
numberOfThreads,
|
||||
numberOfThreadsFound,
|
||||
"Found as many threads as expected"
|
||||
);
|
||||
}
|
||||
|
||||
// Testing subframes.
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
[localization] @AB_CD@.jar:
|
||||
crashreporter (%crashreporter/**/*.ftl)
|
||||
toolkit (%toolkit/**/*.ftl)
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
preview/aboutProcesses.ftl (../components/aboutprocesses/content/aboutProcesses.ftl)
|
||||
#endif
|
||||
|
||||
@AB_CD@.jar:
|
||||
% locale global @AB_CD@ %locale/@AB_CD@/global/
|
||||
|
|
Загрузка…
Ссылка в новой задаче