зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1704528 - migrate first section performance docs r=firefox-source-docs-reviewers,ahal
Differential Revision: https://phabricator.services.mozilla.com/D111864
This commit is contained in:
Родитель
b2ba954d3b
Коммит
e928155e9b
|
@ -0,0 +1,89 @@
|
|||
# Benchmarking
|
||||
|
||||
## Debug Builds
|
||||
|
||||
Debug builds (\--enable-debug) and non-optimized builds
|
||||
(\--disable-optimize) are *much* slower. Any performance metrics
|
||||
gathered by such builds are largely unrelated to what would be found in
|
||||
a release browser.
|
||||
|
||||
## Rust optimization level
|
||||
|
||||
Local optimized builds are [compiled with rust optimization level 1 by
|
||||
default](https://groups.google.com/forum/#!topic/mozilla.dev.platform/pN9O5EB_1q4),
|
||||
unlike Nightly builds, which use rust optimization level 2. This setting
|
||||
reduces build times significantly but comes with a serious hit to
|
||||
runtime performance for any rust code ([for example stylo and
|
||||
webrender](https://groups.google.com/d/msg/mozilla.dev.platform/pN9O5EB_1q4/ooXNuqMECAAJ)).
|
||||
Add the following to your
|
||||
[mozconfig](/setup/configuring_build_options.html#using-a-mozconfig-configuration-file)
|
||||
in order to build with level 2:
|
||||
|
||||
``` {.notranslate}
|
||||
ac_add_options RUSTC_OPT_LEVEL=2
|
||||
```
|
||||
|
||||
## GC Poisoning
|
||||
|
||||
Many Firefox builds have a diagnostic tool that causes crashes to happen
|
||||
sooner and produce much more actionable information, but also slow down
|
||||
regular usage substantially. In particular, \"GC poisoning\" is used in
|
||||
all debug builds, and in optimized Nightly builds (but not opt Developer
|
||||
Edition or Beta builds). The poisoning can be disabled by setting the
|
||||
environment variable
|
||||
|
||||
``` {.notranslate}
|
||||
JSGC_DISABLE_POISONING=1
|
||||
```
|
||||
|
||||
before starting the browser.
|
||||
|
||||
## Async Stacks
|
||||
|
||||
Async stacks no longer impact performance since **Firefox 78**, as
|
||||
{{bug(1601179)}} limits async stack capturing to when DevTools is
|
||||
opened.
|
||||
|
||||
Another option that is on by default in non-release builds is the
|
||||
preference javascript.options.asyncstack, which provides better
|
||||
debugging information to developers. Set it to false to match a release
|
||||
build. (This may be disabled for many situations in the future. See
|
||||
{{bug(1280819)}}.
|
||||
|
||||
## Accelerated Graphics
|
||||
|
||||
Especially on Linux, accelerated graphics can sometimes lead to severe
|
||||
performance problems even if things look ok visually. Normally you would
|
||||
want to leave acceleration enabled while profiling, but on Linux you may
|
||||
wish to disable accelerated graphics (Preferences -\> Advanced -\>
|
||||
General -\> Use hardware acceleration when available).
|
||||
|
||||
## Flash Plugin
|
||||
|
||||
If you are profiling real websites, you should disable the Adobe Flash
|
||||
plugin so you are testing Firefox code and not Flash jank problems. In
|
||||
about:addons \> Plugins, set Shockwave Flash to \"Never Activate\".
|
||||
|
||||
## Timer Precision
|
||||
|
||||
Firefox reduces the precision of the Performance APIs and other clock
|
||||
and timer APIs accessible to Web Content. They are currently reduce to a
|
||||
multiple of 2ms; which is controlled by the privacy.reduceTimerPrecision
|
||||
about:config flag.
|
||||
|
||||
The exact value of the precision is controlled by the
|
||||
privacy.resistFingerprinting.reduceTimerPrecision.microseconds
|
||||
about:config flag.
|
||||
|
||||
## Profiling tools
|
||||
|
||||
Currently the Gecko Profiler has limitations in the UI for inverted call
|
||||
stack top function analysis which is very useful for finding heavy
|
||||
functions that call into a whole bunch of code. Currently such functions
|
||||
may be easy to miss looking at a profile, so feel free to *also* use
|
||||
your favorite native profiler. It also lacks features such as
|
||||
instruction level profiling which can be helpful in low level profiling,
|
||||
or finding the hot loop inside a large function, etc. Some example tools
|
||||
include Instruments on OSX (part of XCode), [RotateRight
|
||||
Zoom](http://www.rotateright.com/) on Linux (uses perf underneath), and
|
||||
Intel VTune on Windows or Linux.
|
|
@ -0,0 +1,43 @@
|
|||
# GPU Performance
|
||||
|
||||
Doing performance work with GPUs is harder than with CPUs because of the
|
||||
asynchronous and massively parallel architecture.
|
||||
|
||||
## Tools
|
||||
|
||||
PIX - Can do timing of Direct3D calls. Works reasonably well with
|
||||
Firefox. See also [Debugging With
|
||||
PIX](/en/Debugging_With_PIX "en/Debugging With PIX").
|
||||
|
||||
NVIDIA PerfHUD - Last I checked required a special build to be used.
|
||||
|
||||
NVIDIA Parallel Nsight - Haven\'t tried.
|
||||
|
||||
AMD GPU ShaderAnalyzer - Will compile a shader and show the machine code
|
||||
and give static pipeline estimations. Not that useful for Firefox
|
||||
because all of our shaders are pretty simple.
|
||||
|
||||
AMD GPU PerfStudio - I had trouble getting this to work, and can\'t
|
||||
remember whether I actually did or not.
|
||||
|
||||
[Intel Graphics Performance Analyzers](http://software.intel.com/en-us/articles/intel-gpa/ "http://software.intel.com/en-us/articles/intel-gpa/")
|
||||
- Haven\'t tried.
|
||||
|
||||
[APITrace](https://github.com/apitrace/apitrace "https://github.com/apitrace/apitrace")
|
||||
- Open source, works OK.
|
||||
|
||||
[PVRTrace](http://www.imgtec.com/powervr/insider/pvrtrace.asp "http://www.imgtec.com/powervr/insider/pvrtrace.asp")
|
||||
- Doesn\'t seem to emit traces on android/Nexus S. Looks like it\'s
|
||||
designed for X11-based linux-ARM devices, OMAP3 is mentioned a lot in
|
||||
the docs \...
|
||||
|
||||
## Guides
|
||||
|
||||
[Accurately Profiling Direct3D API Calls (Direct3D
|
||||
9)](http://msdn.microsoft.com/en-us/library/bb172234%28v=vs.85%29.aspx "http://msdn.microsoft.com/en-us/library/bb172234(v=vs.85).aspx")
|
||||
Suggests avoiding normal profilers like xperf and instead measuring the
|
||||
time to flush the command buffer.
|
||||
|
||||
[OS X - Best Practices for Working with Texture
|
||||
Data](http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/opengl_texturedata.html "http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/opengl_texturedata.html")
|
||||
- Sort of old, but still useful.
|
|
@ -0,0 +1,24 @@
|
|||
# Automated performance testing and sheriffing
|
||||
|
||||
We have several test harnesses that test Firefox for various performance
|
||||
characteristics (page load time, startup time, etc.). We also generate
|
||||
some metrics as part of the build process (like installer size) that are
|
||||
interesting to track over time. Currently we aggregate this information
|
||||
in the [Perfherder web
|
||||
application](https://wiki.mozilla.org/Auto-tools/Projects/Perfherder)
|
||||
where performance sheriffs watch for significant regressions, filing
|
||||
bugs as appropriate.
|
||||
|
||||
Current list of automated systems we are tracking (at least to some
|
||||
degree):
|
||||
|
||||
- [Talos](https://wiki.mozilla.org/TestEngineering/Performance/Talos): The main
|
||||
performance system, run on virtually every check-in to an
|
||||
integration branch
|
||||
- [build_metrics](/setup/configuring_build_options.html):
|
||||
A grab bag of performance metrics generated by the build system
|
||||
- [AreWeFastYet](https://arewefastyet.com/): A generic JavaScript and
|
||||
Web benchmarking system
|
||||
tool
|
||||
- [Platform microbenchmarks](platform_microbenchmarks/platform_microbenchmarks.md)
|
||||
- [Build Metrics](build_metrics/build_metrics.md)
|
|
@ -0,0 +1,31 @@
|
|||
# Build Metrics
|
||||
|
||||
**Build Metrics** is a catch-all term for performance measures that are
|
||||
generated by the Firefox build system and tracked by Perfherder.
|
||||
|
||||
### num_constructors
|
||||
|
||||
Number of static constructors found by the compiler in the Firefox C++
|
||||
codebase. Lower is better. Static constructors are undesirable because
|
||||
their initialization imposes an unavoidable time penalty every time
|
||||
Firefox is started.
|
||||
|
||||
### installer size
|
||||
|
||||
Size in bytes of the Firefox installer. Lower is better here, especially
|
||||
on space-restricted platforms like Android.
|
||||
|
||||
### build times
|
||||
|
||||
Amount of time it takes to build Firefox in automation on a specific
|
||||
platform / configuration. Lower is better.
|
||||
|
||||
### compiler warnings
|
||||
|
||||
Number of compiler warnings detected during a build. Lower is better.
|
||||
|
||||
Due to the way the build system works, compiler warnings are not
|
||||
consistently detected. So the value may fluctuate from build to build
|
||||
even if the number of compiler warnings didn\'t actually change. Since
|
||||
Perfherder alerts are calculated based on the mean value of a range, a
|
||||
regression may be reported as a fractional value.
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 324 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 8.2 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 10 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 27 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 20 KiB |
|
@ -0,0 +1,21 @@
|
|||
# Platform microbenchmarks
|
||||
|
||||
Platform microbenchmarks benchmarks specific low-level operations used
|
||||
by the gecko platform. If a test regresses, it could result in the
|
||||
degradation in the performance of some user-visible feature.
|
||||
|
||||
The list of tests and their descriptions is currently incomplete. If
|
||||
something is missing, please search for it in the gecko source and
|
||||
update this page (or ask the original author to do so, if you're still
|
||||
not sure).
|
||||
|
||||
## String tests
|
||||
|
||||
* PerfStripWhitespace
|
||||
* PerfCompressWhitespace
|
||||
* PerfStripCharsWhitespace
|
||||
* PerfStripCRLF
|
||||
* PerfStripCharsCRLF
|
||||
|
||||
These tests measure the amount of time it takes to perform a large
|
||||
number of operations on low-level strings.
|
|
@ -0,0 +1,94 @@
|
|||
# Reporting a Performance Problem
|
||||
|
||||
This article will guide you in reporting a performance problem using the
|
||||
built-in Gecko Profiler tool.
|
||||
|
||||
## Enabling the Profiler toolbar button
|
||||
|
||||
These steps only work in Firefox 75+.
|
||||
|
||||
1. Visit [https://profiler.firefox.com/](https://profiler.firefox.com/)
|
||||
2. Click on *Enable Profiler Menu Button*
|
||||
3. The profiler toolbar button will show up in the top right of the URL
|
||||
bar as a small stopwatch icon.
|
||||
|
||||
![image1](img/reportingperf1.png)
|
||||
|
||||
4. You can right-click on the button and remove it from the toolbar
|
||||
when you're done with it.
|
||||
|
||||
## Using the Profiler
|
||||
|
||||
When enabled, the profiler toolbar button is not recording by default.
|
||||
Recording can be done by clicking on the toolbar icon to open its panel.
|
||||
Make sure to choose an appropriate setting for the recording (if you\'re
|
||||
not sure, choose Firefox Platform), and then choosing **Start
|
||||
Recording**. The toolbar icon turns blue when it is recording.
|
||||
|
||||
The profiler uses a fixed size buffer to store sample data. When it runs
|
||||
out of space in its buffer, it discards old entries so you may want to
|
||||
increase the buffer size if you find you are unable to capture the
|
||||
profile quickly enough after you notice a performance problem. If you
|
||||
choose Custom Settings (and then clicking Edit Settings) for the
|
||||
profiler, you can adjust the size of the buffer (presently defaults to
|
||||
90 MB) and the time interval between data collection (presently defaults
|
||||
to 1 ms). Note that increasing the buffer size uses more memory and can
|
||||
make capturing a profile take longer.
|
||||
|
||||
![image2](img/reportingperf2.png)
|
||||
|
||||
Using the keyboard shortcuts is often more convenient than using the
|
||||
mouse to interact with the UI:
|
||||
|
||||
* Ctrl+Shift+1 - Start/Stop the profiler
|
||||
* Ctrl+Shift+2 - Take a profile and launch the viewer to view it
|
||||
|
||||
## Capturing and sharing a profile
|
||||
|
||||
1. While the profiler is recording, reproduce the performance problem.
|
||||
If possible let the problem manifest itself for 5-10 seconds.
|
||||
2. Press **Ctrl+Shift+2** or click on the profiler toolbar icon in the
|
||||
top right and select **Capture**. Try to do this within a few
|
||||
seconds from reproducing the performance problem as only the last
|
||||
few seconds are recorded. If the timeline has a large red block
|
||||
it's a good sign. ![Jank markers appearing in the Perf.html profile analysis tool.](img/PerfDotHTMLRedLines.png)
|
||||
3. The data will open in a new tab. Wait until the \"Symbolicating call
|
||||
stacks\" notification disappears before sharing the profile.
|
||||
4. There will be a button in the top right labeled **Publish** which
|
||||
will allow you to upload this profile and once completed will write
|
||||
out a link. Before uploading, the publish button asks you what data
|
||||
you'd like to publish to our servers.
|
||||
5. Note that while it\'s possible to strip profiles of potentially
|
||||
privacy sensitive information, the less information a profile
|
||||
contains, *the harder it is to analyze and turn into actionable
|
||||
data.*
|
||||
6. Once uploaded, copy permalink URL to your clipboard by right
|
||||
clicking and [add the profile URL to a bug](https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Performance)
|
||||
for your performance problem and/or send it to the appropriate
|
||||
person. Try to give some context about what you were doing when the
|
||||
performance problem arose such as the URL you were viewing and what
|
||||
actions were you doing (ex. scrolling on gmail.com).
|
||||
|
||||
![image3](img/reportingperf3.png)
|
||||
|
||||
## Viewing addon performance in GeckoView
|
||||
|
||||
Sometimes an addon or more are slowing down Firefox. These addons might
|
||||
be using the extension API in ways that were not meant to. You can see
|
||||
which of these addons are causing problems by adding the
|
||||
**moz-extension** filter.
|
||||
|
||||
![moz-extension filter print screen](img/EJCrt4N.png)
|
||||
|
||||
Make sure you are selecting the process that is using up the CPU since
|
||||
all of the processes are shown. You might have a content process using
|
||||
up the CPU and not the main one.
|
||||
|
||||
Make sure you are doing whatever it is that slows down Firefox while
|
||||
recording the profile. For example you might have one addon that slows down page load
|
||||
and another one that slows down tab switch.
|
||||
|
||||
Your first reflex once you find what addon is slowing down the profile
|
||||
might be to disable it and search for alternatives. Before you do this,
|
||||
please share the performance profile with the addon authors through a
|
||||
bug report. Gecko profiler allows you to share a link with the profile.
|
|
@ -0,0 +1,177 @@
|
|||
# Scroll-linked effects
|
||||
|
||||
The definition of a scroll-linked effect is an effect implemented on a
|
||||
webpage where something changes based on the scroll position, for
|
||||
example updating a positioning property with the aim of producing a
|
||||
parallax scrolling effect. This article discusses scroll-linked effects,
|
||||
their effect on performance, related tools, and possible mitigation
|
||||
techniques.
|
||||
|
||||
## Scrolling effects explained
|
||||
|
||||
Often scrolling effects are implemented by listening for the `scroll`
|
||||
event and then updating elements on the page in some way (usually the
|
||||
CSS
|
||||
[`position`](/en-US/docs/Web/CSS/position "The position CSS property sets how an element is positioned in a document. The top, right, bottom, and left properties determine the final location of positioned elements.")
|
||||
or
|
||||
[`transform`](/en-US/docs/Web/CSS/transform "The transform CSS property lets you rotate, scale, skew, or translate an element. It modifies the coordinate space of the CSS visual formatting model.")
|
||||
property.) You can find a sampling of such effects at [CSS Scroll API:
|
||||
Use
|
||||
Cases](https://github.com/RByers/css-houdini-drafts/blob/master/css-scroll-api/UseCases.md).
|
||||
|
||||
These effects work well in browsers where the scrolling is done
|
||||
synchronously on the browser\'s main thread. However, most browsers now
|
||||
support some sort of asynchronous scrolling in order to provide a
|
||||
consistent 60 frames per second experience to the user. In the
|
||||
asynchronous scrolling model, the visual scroll position is updated in
|
||||
the compositor thread and is visible to the user before the `scroll`
|
||||
event is updated in the DOM and fired on the main thread. This means
|
||||
that the effects implemented will lag a little bit behind what the user
|
||||
sees the scroll position to be. This can cause the effect to be laggy,
|
||||
janky, or jittery --- in short, something we want to avoid.
|
||||
|
||||
Below are a couple of examples of effects that would not work well with
|
||||
asynchronous scrolling, along with equivalent versions that would work
|
||||
well:
|
||||
|
||||
### Example 1: Sticky positioning
|
||||
|
||||
Here is an implementation of a sticky-positioning effect, where the
|
||||
\"toolbar\" div will stick to the top of the screen as you scroll down.
|
||||
|
||||
``` {.brush: .html}
|
||||
<body style="height: 5000px" onscroll="document.getElementById('toolbar').style.top = Math.max(100, window.scrollY) + 'px'">
|
||||
<div id="toolbar" style="position: absolute; top: 100px; width: 100px; height: 20px; background-color: green"></div>
|
||||
</body>
|
||||
```
|
||||
|
||||
This implementation of sticky positioning relies on the scroll event
|
||||
listener to reposition the \"toolbar\" div. As the scroll event listener
|
||||
runs in the JavaScript on the browser\'s main thread, it will be
|
||||
asynchronous relative to the user-visible scrolling. Therefore, with
|
||||
asynchronous scrolling, the event handler will be delayed relative to
|
||||
the user-visible scroll, and so the div will not stay visually fixed as
|
||||
intended. Instead, it will move with the user\'s scrolling, and then
|
||||
\"snap\" back into position when the scroll event handler runs. This
|
||||
constant moving and snapping will result in a jittery visual effect. One
|
||||
way to implement this without the scroll event listener is to use the
|
||||
CSS property designed for this purpose:
|
||||
|
||||
``` {.brush: .html}
|
||||
<body style="height: 5000px">
|
||||
<div id="toolbar" style="position: sticky; top: 0px; margin-top: 100px; width: 100px; height: 20px; background-color: green"></div>
|
||||
</body>
|
||||
```
|
||||
|
||||
This version works well with asynchronous scrolling because position of
|
||||
the \"toolbar\" div is updated by the browser as the user scrolls.
|
||||
|
||||
### Example 2: Scroll snapping
|
||||
|
||||
Below is an implementation of scroll snapping, where the scroll position
|
||||
snaps to a particular destination when the user\'s scrolling stops near
|
||||
that destination.
|
||||
|
||||
``` {.brush: .html}
|
||||
<body style="height: 5000px">
|
||||
<script>
|
||||
function snap(destination) {
|
||||
if (Math.abs(destination - window.scrollY) < 3) {
|
||||
scrollTo(window.scrollX, destination);
|
||||
} else if (Math.abs(destination - window.scrollY) < 200) {
|
||||
scrollTo(window.scrollX, window.scrollY + ((destination - window.scrollY) / 2));
|
||||
setTimeout(snap, 20, destination);
|
||||
}
|
||||
}
|
||||
var timeoutId = null;
|
||||
addEventListener("scroll", function() {
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(snap, 200, parseInt(document.getElementById('snaptarget').style.top));
|
||||
}, true);
|
||||
</script>
|
||||
<div id="snaptarget" class="snaptarget" style="position: relative; top: 200px; width: 100%; height: 200px; background-color: green"></div>
|
||||
</body>
|
||||
```
|
||||
|
||||
In this example, there is a scroll event listener which detects if the
|
||||
scroll position is within 200 pixels of the top of the \"snaptarget\"
|
||||
div. If it is, then it triggers an animation to \"snap\" the scroll
|
||||
position to the top of the div. As this animation is driven by
|
||||
JavaScript on the browser\'s main thread, it can be interrupted by other
|
||||
JavaScript running in other tabs or other windows. Therefore, the
|
||||
animation can end up looking janky and not as smooth as intended.
|
||||
Instead, using the CSS snap-points property will allow the browser to
|
||||
run the animation asynchronously, providing a smooth visual effect to
|
||||
the user.
|
||||
|
||||
``` {.brush: .html}
|
||||
<body style="height: 5000px">
|
||||
<style>
|
||||
body, /* blink currently has bug that requires declaration on `body` */
|
||||
html {
|
||||
scroll-snap-type: y proximity;
|
||||
}
|
||||
.snaptarget {
|
||||
scroll-snap-align: start;
|
||||
position: relative;
|
||||
top: 200px;
|
||||
height: 200px;
|
||||
background-color: green;
|
||||
}
|
||||
</style>
|
||||
<div class="snaptarget"></div>
|
||||
</body>
|
||||
```
|
||||
|
||||
This version can work smoothly in the browser even if there is
|
||||
slow-running Javascript on the browser\'s main thread.
|
||||
|
||||
### Other effects
|
||||
|
||||
In many cases, scroll-linked effects can be reimplemented using CSS and
|
||||
made to run on the compositor thread. However, in some cases the current
|
||||
APIs offered by the browser do not allow this. In all cases, however,
|
||||
Firefox will display a warning to the developer console (starting in
|
||||
version 46) if it detects the presence of a scroll-linked effect on a
|
||||
page. Pages that use scrolling effects without listening for scroll
|
||||
events in JavaScript will not get this warning. See the [Asynchronous
|
||||
scrolling in Firefox](https://staktrace.com/spout/entry.php?id=834) blog
|
||||
post for some more examples of effects that can be implemented using CSS
|
||||
to avoid jank.
|
||||
|
||||
## Future improvements
|
||||
|
||||
Going forward, we would like to support more effects in the compositor.
|
||||
In order to do so, we need you (yes, you!) to tell us more about the
|
||||
kinds of scroll-linked effects you are trying to implement, so that we
|
||||
can find good ways to support them in the compositor. Currently there
|
||||
are a few proposals for APIs that would allow such effects, and they all
|
||||
have their advantages and disadvantages. The proposals currently under
|
||||
consideration are:
|
||||
|
||||
- [Web Animations](https://w3c.github.io/web-animations/): A new API
|
||||
for precisely controlling web animations in JavaScript, with an
|
||||
[additional
|
||||
proposal](https://wiki.mozilla.org/Platform/Layout/Extended_Timelines)
|
||||
to map scroll position to time and use that as a timeline for the
|
||||
animation.
|
||||
- [CompositorWorker](https://docs.google.com/document/d/18GGuTRGnafai17PDWjCHHAvFRsCfYUDYsi720sVPkws/edit?pli=1#heading=h.iy9r1phg1ux4):
|
||||
Allows JavaScript to be run on the compositor thread in small
|
||||
chunks, provided it doesn\'t cause the framerate to drop.
|
||||
- [Scroll
|
||||
Customization](https://docs.google.com/document/d/1VnvAqeWFG9JFZfgG5evBqrLGDZYRE5w6G5jEDORekPY/edit?pli=1):
|
||||
Introduces a new API for content to dictate how a scroll delta is
|
||||
applied and consumed. As of this writing, Mozilla does not plan to
|
||||
support this proposal, but it is included for completeness.
|
||||
|
||||
### Call to action
|
||||
|
||||
If you have thoughts or opinions on:
|
||||
|
||||
- Any of the above proposals in the context of scroll-linked effects.
|
||||
- Scroll-linked effects you are trying to implement.
|
||||
- Any other related issues or ideas.
|
||||
|
||||
Please get in touch with us! You can join the discussion on the
|
||||
[public-houdini](https://lists.w3.org/Archives/Public/public-houdini/)
|
||||
mailing list.
|
Загрузка…
Ссылка в новой задаче