With I2102f6d3397 OOUI's ButtonSelectWidgets behave differently on
limited parent element horizontal space.
We re-introduce `white-space: nowrap` here specfically.
Bug: T208705
Change-Id: I154c2d9c5469a789d365c5ccb771a9fc0cc018a4
While the new rows are being faded in, they each have an opacity not
equal to 1. Setting the opacity on something creates a new stacking
context, and because the rows are below the legend in the DOM, this made
them appear on top of the legend.
Fix this by setting a z-index on the legend so it stays on top.
Bug: T186841
Change-Id: I2eee201d1e0aed94974a85d6d46b86fd4a4db796
Follows-up dec800968e, which moved the processing of callbacks that require
'modules' from startup.js to mediawiki.base.js.
In doing so, it made an incorrect assumption. It assumed that the simple
signature of RLQ.push(Function) is not needed after 'mediawiki.base' loads.
It is true that RLQ.push() is mostly an internal interface, and we only
use it from within the HTML output, and that once the async pipeline has
finished and startup.js has processed the simple callbacks, only calls with
secondary signatures remain in the queue.
But, while it is true that we don't use RLQ.push() outside the HTML, it is
not true that the HTML will fully load and execute inline scripts before
any of the async scripts execute. As such, the call to RLQ.push() in the
HTML footer was sometimes being ignored because 'mediawiki.base' had already
loaded by now.
Bug: T208093
Change-Id: I25012a2c6f41968b1b4f85614a3bc0416512d530
FilterItemHighlightButtons have lost vertical alignment with
“Highlight results”. This patch reintroduces it.
Change-Id: If5578564efced5f0ba4d3ca7ed3d6787b5b0ce3a
Cleaning-up by
- adding border only between namespace and talk options
- adding namespace border on all but the first occurrence
Change-Id: Icfc87757deda9b7655c4ea5c919b5c2f2e2ae09e
Was caused by a stray margin-right rule that applied to the wrong divs,
and wasn't needed anyway.
Bug: T207808
Change-Id: Ib6cf518653fbadb0c7d67d6a175b4af92c8f8d87
Follows-Up: I76fec57ff237ba02afefcea28916d8348bac9b0e
Make the necessary UI changes to Special:Block in order to set/update
partial blocks.
Bug: T197109
Change-Id: Ib3067824b5dcbdd893ab1f165d169a35d0716cb2
Add the widget in both PHP and JS for OOUI, and into HTMLForm
definitions.
In JS, the widget uses the engine from mw.widgets.TitleWidget
with the async support from OO.ui.mixin.RequestManager.
The PHP version provides a textarea, like UsersMultiselectWidget.php
which is then infused if JS is available.
Also, add highlightSearchQuery option for TitleWidget to allow for
not highlighting the partial search query the user typed in, if the
UI requires it. This option (highlighting partial result) is already
optional in the TitleOptionWidget, so this config exposes that
optionality in the TitleWidget widget for its menu children.
Notes:
HTMLTitlesMultiselectField is a duplication of HTMLUsersMultiselectField
except for:
- The configuration variable changed to 'titles' (from 'users')
- OOUI modules were adjusted for the TitlesMultiselectWidget
- The PHP version instantiates a MediaWiki\Widget\TitlesMultiselectWidget
TitlesMultiselectWidget is a duplication of UsersMultiselectWidget
except for:
- $usersArray was renamed to $titlesArray
- getJavascriptClassName returns the correct js class for
mw.widgets.TitlesMultiselectWidget for infusion.
Bug: T197109
Depends-On: I675316dddf272fd0d6172ecad3882160752bf780
Change-Id: Ie96947a35f70b76731e16ae5b85de815dfa4a8ce
These are supposed to be unfilled, but the CSS wasn't being applied
because it didn't have -color in the class name. Put it back into the
&-color block where it belongs.
Bug: T207472
Change-Id: Ic517b9a1a0e8e95015f5b2bd3517c36bff3b20e8
Follows-Up: I76fec57ff237ba02afefcea28916d8348bac9b0e
- Correct highlights class name (mw-rcfilters-ui-highlights, not
mw-rcfilters-ui-changesListWrapperWidget-highlights)
- Remove -circle class suffix that the circles don't actually have
- Go back to using float: right; instead of text-align: right;
(you can't use text-align for things that aren't inline)
- For seen/unseen watched items, apply circle mixin to the actual
circles themselves, not their parents
Bug: T207472
Change-Id: Iade1af4299826a4e8a2108425ace9a406f0aa8cb
Follows-Up: I76fec57ff237ba02afefcea28916d8348bac9b0e
This restores the behavior for undefined params as it was before
I445f9194bb8b2ed35baafbda30d1d0d008b64e2c
Bug: T207397
Change-Id: I751f01a9b3cd8bb87d7d7def98c918a87ddf81cd
Piped tag list wrapped with parentheses taken care of via CSS.
Lists of links inside history are now a list of links rather than
2 anchors separated by text.
Remains translateable, and accessible but is now fully skinnable by
skins such as Timeless and MinervaNeue.
For now the approach only touches the contributions pager, but
can be used on other pages as we work more on them.
Additional changes:
* A CSS file is renamed to a LESS file
Bug: T205581
Change-Id: Ic4a9c75611fd7ce92511c00c32b73c0745081b50
I'm not sure what bug this is trying to work around. It mentions
T49395 but that doesn't look very relevant. Maybe the real problem was
confusion and bugs related to passing "raw params" (T33260#4675605).
Regardless, it's no longer needed now, because this code also works.
Change-Id: I580d46a5e4b7ff58989cb5fd6b7770e781fb5319
With this change, MediaWiki will no longer have a 'JavaScript-powered'
wikitext toolbar, and instead sysadmins will be required to choose one
(or more) of the several extensions available for this purpose if they
need the functionality. For over half a decade MediaWiki's tarball has
included the 2010-era replacement for this feature, WikiEditor. We are
now working on replacing even that, with the 2013-era visual editor, a
mode of which is the forthcoming 2017-era wikitext editor, and several
more specialised editors like CodeEditor.
Beyond this, the core editor toolbar is ancient, un-loved, and is used
only exceptionally rarely, mostly by accident. It is unhelpful to give
implicitly this as the primary editor for MediaWiki just because we've
not removed it from core when it is not a very good experience for any
kind of user, and has not received the attention that users deserve to
be worth retaining in core.
The old core preference, which was intended to govern whether this old
toolbar should be shown, has since mutated into whether the to run the
EditPageBeforeEditToolbar hook. The hook is used by several extensions
to provide toolbars in lieu of the core one. This preference has been,
in practice, a very confusing preference for MediaWiki users, who have
to interact with quite similar preferences to toggle their real editor
which sit next to this one on the preferences page. Consequently, this
preference is also removed.
The code could be made into an extension for those (very few) users of
MediaWiki who might want to keep on using it. However, the author will
offer their services but not their encouragement in said undertaking.
Bug: T30856
Bug: T32795
Change-Id: I2b05f0ca25873ad8e0b33a5e4938bef52c4e9347
No longer used anywhere(?); we'd rather not have to explain the temporary
variable in the MediaWiki 1.32.0 release notes if we can instead just not ship
it.
Bug: T192620
Change-Id: Icfb82f228512ed45f1a27ce3e565fbc5fc09f39c
jqueryMsg parsing is required when the message body contains wikitext,
but it is also required when one of the parameters is a jQuery object.
Without this change, the following doesn't work:
mw.messages.set( 'foo', 'Hello $1' );
mw.message( 'foo', $( '<a>' ).text( 'World' ) ).parse();
(But if the message contained e.g. {{PLURAL:}} syntax, it did work.)
Change-Id: I445f9194bb8b2ed35baafbda30d1d0d008b64e2c
SVG isn't used anywhere, was probably added as comparison overview
file for other icons, which doesn't belong into a production package.
Change-Id: I826bb9622365900c0715a303f8e78e9329cd0c85
Using `none none` value is also working on older IEs, which had
issues with shorthand only `list-style: none`.
Change-Id: I7d4b075ac90fd902e0c0fbc3e11449faf71fe9b2
MediaWiki uses a number of nonstandard codes which do not validate
according to the IANA language subtag registry. Some of them have
the wrong semantics entirely: MediaWiki's `sr-ec` variant maps to
BCP 47 `sr-EC` which is "Serbian as used in Ethiopia" (!).
Extend LanguageCode::bcp47() to map our nonstandard codes to valid
BCP 47 language codes. Export the mapping so that it can be used
in JavaScript's corresponding mw.language.bcp47() implementation
as well, and return the standard BCP 47 codes in the siteinfo
API.
Thanks to TheDJ (I10b4473c7e53f027812bbccf26bb47aec15fddfd) and
Fomafix (I93efc190714ba76247d30ba49fc21ae872fc3555) for previous
attempts at this!
Also removed a fixme for the name of 'Twi', dating back to 2004
(f59c3be23b) -- checking
tw.wikipedia.org it certainly appears that the autonym of 'Twi'
is correctly 'Twi'.
Tracking bugs for invalid language codes are T125073 and T145535.
Discussion of zh-XX => zh-HanX-XX mapping is at T198419.
This is a replay of an earlier merged patch,
8380f0173e, which had to be reverted
because it caused regressions in the Babel extension (T199941).
Bug: T34483
Bug: T106367
Bug: T120847
Depends-On: I27a5b8e45b34c6b57c1b612b11548001c88cd483
Change-Id: Iebbc604af21d7f2af9c1f1ab2574cb5f309bf6ed
Improve circle mixin, variables and apply standard icon size:
- Apply new icon size of 20px,
- Divert `.mw-rcfilters-mixin-circle()` into two mixins, applying former
only on circle classes to not repeat output of shared properties for
each color,
- Add new parameter to apply `px` based `min-width`/`min-height` to
circumvent rendering issues,
- Rename variables for unification with WikimediaUI Base vars and easier
readability
Bug: T190980
Change-Id: I76fec57ff237ba02afefcea28916d8348bac9b0e
Upstream Sinon.JS no longer supports the pre-2.x v1.17 branch
anymore but there have been a few post-2.0 releases to address
bugs in v1.17 that we can still benefit from.
https://github.com/sinonjs/sinon/blob/v1.17.7/Changelog.txt
Notable changes include:
* Fix Blob feature test to support running tests on Safari 9.
* Avoid calls to object-local 'hasOwnProperty'.
* Improve error messages and stricter signature/type checks so
that debugging code is easier when things go wrong.
* Fix various gaps in the XHR mock.
* Misc fixes for Node.js support.
Also remove outdated comment about ie-hacks from Sinon 1.15.
Change-Id: I66d1b461465b92798ad7eb2efcf4df2731cc78a4
Overriding OOUI's frameless button opacity, which is targeted in this
place for icons to show real highlight color.
Bug: T190980
Change-Id: Id3355ecad8ff01ef8159375eaa04f7d284fc598b
Not yet tagged as release, but updating ahead of that for the
performance improvement that landed with
https://github.com/santhoshtr/CLDRPluralRuleParser/commit/a7782ca.
This is particularly impactful on calls to mw.msg() during the
critical path, due to this library being called by jqueryMsg,
which is used by mw.Message#text, which mw.msg() calls by default.
Particularly seen in the startup code from ULS modules.
Bug: T127328
Change-Id: Ic865e7077d1053c65f7ae1633c60bf52104731b7
- Remove min-height so menu items are a little smaller and
their content is vertically-aligned.
- Force `vertical-align: middle;` for checkboxes by overriding
OOUI with extreme specificity.
Bug: T200364
Change-Id: If0b2b5c1b749f913badbd0c02a85852ec0007020
The JavaScript for the cloner fields has been updated to function
when using OOUI Cloners.
The buttons are packed quite tight together, but there does not
seem to be any CSS that enforced the spacing that I could find.
Bug: T171666
Change-Id: Iba6eed9d6cee922d56855bbe2e836956bfd90f42
Currently, these separators are unnecessarily hardcoded in HTML.
This is problematic, as it limits the extent to which skins such
as Minerva and Timeless can skin these pages.
If the pseudo selector is not available (e.g. legacy browsers)
this degrades nicely to whitespace and the page is still useable.
Right now, these changes are scoped to pages uses the ContribsPager
(used by Special:Contributions)
but we can adopt them in other pages once we have this approach set in
stone (CSS)
Bug: T205581
Change-Id: Ia2c485e6058ebf4282ed2f7eb7b046b2deb03f57
This header can be injected into api responses that include
search results to provide a link between the backend logs
and any frontend logging. Associating autocomplete tracking
with backend logs will allow us to determine autocomplete
examination probabilities, and more generally be able to
evaluate autocomplete effectiveness.
Bug: T205348
Change-Id: I1663906e2fd71f7df215e563b09a0b4fb8948ab8
Upstream commit 'd9aa703' is the commit right before the tagging
of v1.0.0. The only difference is the version string.
Change-Id: Icf7b7abacac583984977183da6f93f5fd02404fa
Screen reader users are not benefitting from the navigation hint message as
we're exposing ARIA roles on tabs to them and keyboard navigation hint is
sufficient.
Bug: T204861
Change-Id: I720bb34ad7bed96fc3925648e4606fb1efa7d5e9
Since OOUI v0.24.4, menus/popups of infused PHP widgets are placed
into the default overlay, so this workaround is not needed.
Change-Id: I4f8a76cb258e44ed4f2fbba41425100d2e0e9b45
The border choice for `#a2a9b1` was intended to provide AA conforming
contrast for widgets. In case of framed PanelLayout it is not necessary
due to it's different use case and the additional high padding.
Change-Id: Ia425594c1795d1d8668cd62daff32ff9b97d67c2
* Function#bind is a given per the explicit feature test for Grade A.
* Object.defineProperty is part of ES5 which is also tested for.
This code is from before we required ES5 and is now redundant.
Also remove the unused eslint-disable rule for global 'console' use,
the code in question does not use console as global unconditionally.
Change-Id: I6c77b25856da8b7717aeba8298b17a9231540d58
In course of icon overhaul we've also unified base widget font-size
to `14px` in I693d168d2ccf2. DateInputWidget has been left behind.
Let's amend it and also align other variables like
`transition-medium`.
Change-Id: I033bfa17b665d7e36f6d18c3adfa0315c4b26cb0
* Use prop() instead of attr().
- Avoids extra overhead from attr() relating to XML/SVG compat.
- Makes the code consistent with jquery.accessKeyLabel, which,
reads the node.accessKey value as property already, and we now
set it the same way.
* Use append() instead of wrap().parent().
Most use of adding portlets is as links in the sidebar or
as menu items for the page actions menu. For this, simply
assign the <li> directly, and append the link. Instead of
wrapping it in the DOM and then unwrapping the jQuery
collection. This also saves a bunch of clone operations
internally due to how jQuery objects keep a stack of
mutations.
* Set 'textContent' directly instead of text().
* Use $('<li>') instead of $().wrap('<li></li>') which allows jQuery
to take a fast path for element creation. Also consistent with
MW code conventions which say to avoid <tag></tag>.
* Use node.querySelector(str) instead of $(node).find(str).eq(0).
The latter does a lot more, including querySelectorAll(),
and twice a clone/push/merge of the jQuery collection.
* Use createElement() for the anchor tag, given that none of the
remaining code needed the jQuery object anymore. The return
value was already the DOM Node directly as well.
Bug: T204625
Change-Id: I9070f63a4c75411c0eff7757bd2d8aed46c182e9
Follows-up e5912535ae, and removes the last use of
Object.prototype-inheriting objects for map-like objects, in
mediawiki.js.
I'd like to consider using ES6 Map as well, with a partial shim
like we do for StringSet/Set, but that'll require some more
refactoring. It's also not clear whether it will improve
execution speed and/or memory use. Worth trying at a later time,
though.
The current change simply removes the inheritance and simplifies
the code. Apart from slightly smaller code, I could not find any
notable/reproducible improvement in either NavTiming metrics or
CPU time spent in 'Scripting'. The metrics are in the same range
as before this changes.
Change-Id: Ie0016667d9291dcfafde61289d5444817be3447d
* Remove redundant registry check in the internal
queueModuleScript function. Like most internal methods,
it can only be inside the mw.loader closure, and is only
called with extant module name.
* Remove redundant registry check from execute().
* Rename local variable in mw.loader.state() from 'modules'
to 'states' to avoid confusion.
* Shorten exception messages in mw.html to not prefix the method
names, because we don't do that anywhere else in RL JS.
Change-Id: Idf1a49fd98445477f2d940624ca0d079325bd15b
Follows-up Id6d13bbea6:
- '$': mw.loader.implement does this already.
- 'mw': Use the canonical name directly.
This replaces the following patterns:
File closures (common):
- `( function ( $, mw ) {` => `( function () {`
- `( function ( $ ) {` => `( function () {`
- `( function ( mw ) {` => `( function () {`
- `( function ( mw, $ ) {` => `( function () {`
File closures (rare):
- `( function ( mw, $, OO ) {` => `( function () {`
- `( function ( mw, OO, $ ) {` => `( function () {`
- `( function ( mw, document ) {` => `( function () {`
Combined dom-ready and file closure (rare):
- `jQuery( function ( $ ) {` => `$( function () {
- `jQuery( function () {` => `$( function () {
Remaining references in files without a closure, as found by
the new ESLint setting (rare):
- `jQuery` => `$`
- `mediaWiki` => `mw`
Change-Id: I7cf2426cde597259e8c6f3f6f615a1a81a0ca82b
aka "handlePending 2.0",
aka "don't recurse 300x before executing a module",
aka "don't break DevTools flame graphs".
== Impact
Comparison based on viewing the default Main page on Chrome stable.
The local MediaWiki has a few extensions installed (EventLogging,
ULS, Navigation Timing).
Measured by alternating between before/after and logging 'mediaWikiLoadEnd'
from ext.navigationTiming.js, and evaluating the following on the console:
```
({ responseStart: performance.timing.responseStart - performance.timing.navigationStart,
domInteractive: performance.timing.domInteractive - performance.timing.navigationStart,
domComplete: performance.timing.domComplete - performance.timing.navigationStart,
loadEventEnd: performance.timing.loadEventEnd - performance.timing.navigationStart });
```
This was repeated five times, and I picked three results based on similar
responseStart values. This provides a fairer comparison by avoiding bias of
fluctuation from the network/server. The actual values ended up slightly
favouring the older code.
| -------------- | ---------------- | ---------------- | -------- |
| | Before | After | Avg diff |
| -------------- | ---------------- | ---------------- | -------- |
| responseStart | 1044, 1001, 1016 | 1025, 1023, 1024 | +3ms |
| domInteractive | 2080, 2069, 2059 | 1872, 2101, 2050 | -61ms |
| domComplete | 4361, 4239, 3927 | 3691, 4023, 3981 | -227ms |
| loadEventEnd | 4366, 4244, 3932 | 3691, 4023, 3982 | -282ms |
| mwLoadEnd | 4524, 4416, 4113 | 3994, 4320, 4297 | -147ms |
| -------------- | ---------------- | ---------------- | -------- |
== Implementation
While technically a single logical change, this commit does
resolve multiple long-standing issues and inefficiencies.
* handlePending (now called doPropagation) was called way more
often than needed. When a load.php response arrived with calls
to mw.loader.implement(), each one could execute and immediately
call handlePending().
Now, the first implementation in a batch schedule one call
doPropagation(), and the later ones ride along that one call.
* Most calls to handlePending were only able to execute one
pending module, which in turn created its own handlePending
call that started all over again. This meant that by the time
control returned to an outer call, there was nothing left to
do, except it still needed to continue its iteration over the
whole registry before knowing there was nothing left to dos.
* Due to recursive calls to handlePending() from execute(), and
due to immediate execution from implement() - as called from
load.php or asyncEval - the stack was often already 100s of
level deep before *starting* the execution of a module.
Such deep stacks caused:
- a larger memory footprint (for the stacks themselves).
- confusing flame graphs. It was impossible to analyze
performance of module initialisation, I typically could only
look at code from dom-ready handlers or other events.
The stacks were so big, they actually caused rendering
bugs inside Chrome where higher parts of the stack would be
trimmed, and thus related code would no longer be visually
grouped by the same parent.
- confusing error messages (long stack traces).
* The eager execution from mw.loader.implement() calls meant
that it was not possible to separate the parsing/loading of
code (by load.php and asyncEval), from the execution of code.
Now, this is separated by a 1ms yield (in practice it's
larger than 1ms, but it has high priority). This means that the
batch of localStorage eval() and the batch response from
load.php can now first do one type of work (allocating of functions
and objects, stashing them in the registry), and then later the
other type of work (execution of the module code) - with some
breathing room allowed for rendering.
Bug: T127328
Bug: T202703
Change-Id: I499ae3f095545abcc03e8989f54422b1997738d3
This is using the non-standard value of word-break, instead of
overflow-wrap/word-wrap, since the latter doesn't produce the desired
effect. That may be due to figure being displayed as a table, without
the layout being "fixed".
Unfortunately, non-Webkit based browsers don't implement this and
break-all is a little heavy handed for the few cases this turns up in
practice.
Bug: T171761
Change-Id: If123d30addb4c707ebed63bafd18dccba9afe158
Generally, non-string parameters should not be passed to functions
that expect strings. But sometimes we don't document the types well
and our translators do unexpected things. :)
* If the function uses the parameter to look up some other value
(e.g. {{GENDER:$1|...}}, {{PLURAL:$1|...}}), convert it to a string
by removing all formatting (using textify()) and hope for the best.
* If the function applies some textual transformation to the parameter
(e.g. {{GRAMMAR:...|$1}}, {{formatnum:$1}}), do nothing and return
the original parameter.
Bug: T203892
Change-Id: I5c760bf666cc8dae4ce10e3319366e73e2d596e8
Would help statically detect accidental use of jQuery ($) before
it is defined within startup. It would also let ESLint in my editor
catch the issue I keep running into where I use a local variable
called 'module' in mediawiki.js before it is defined, which it
doesn't detect because 'module' is whitelisted in the project's
main .eslintrc file. None of the predefined globals for regular
modules are available inside the startup module.
Follows-up dec800968.
Bug: T192623
Change-Id: Icc102d59fff8eb7cd3f4d55f5c8b64866f2bc8af
In a microbenchmark this wouldn't register given it's just property
access, but the 'timing' and 'navigationStart' properties are
non-trivial getter accessors on their first call, and that's worth
deferring to when it is needed, instead of blocking the definition
of mw.loader early on.
Also remove the redundant wrapper around Date.now(), which is a
static and otherwise detachable function, it does not need to
be bound or wrapped. jQuery defines its shortcut the same way,
as `jQuery.now = Date.now`.
Change-Id: Id621b08fbcce886318bae76ea4c47d50fc9d88e9
Follows-up b7b84d55d4, which embedded the whole of the three 'mediawiki'
JS files inside of startup.js, but did so inside of the pre-existing
closure that was there.
This adds some overhead we don't get value of, so best to remove
it and embed it as a sibling of startup.js in the same top-level
scope.
In local testing (Chrome stable, CPU 1/6th) this reduced startup
run-time from 73-78ms to 63-65ms.
Also:
* Declare isCompatible() as a normal function.
Disable the implicit-globals warning given this isn't a regular module,
this file is intentionally in the global scope.
* Unfold the private startUp() function to its call site.
Change-Id: Ida51ab20898c9e4ae6cbf7ad2968d88d824a1483
Follows-up 6815e35575, and I24786f90bcfb2, which also removed
similar self-calling implementations to simplify stack depth,
and improve startup run-time performance.
Change-Id: Ieb4a7d1e1758856411a49566a69a840b9aeddaa6
The PHP interface still supports it without deprecation, but there
is no need for the client to support it given it is only ever called
from the startup module, which always passes it an object.
Follows-up 6815e35575, which did the same for `state(key, val)`.
Also simplify the implementation to avoid extra calls and optimise
for the only signature.
Mark the method as @private, and change the existing use of
@protected to @private (doesn't really make sense in JavaScript).
Change-Id: I24786f90bcfb256b2e7c37f7760bba1a5e399443
Give skins the ability to place the mw.notification notifications
in a div of their choosing rather than force the container to be
in the content area.
This is particularly useful for skins that deal with multiple
overlays that are created and displayed outside the content div,
which then in turn hides the notification area. A good example of
this behavior is in MobileFrontend's overlays that are created as
siblings to the content div, and so all mw.notify() notifications
are then hidden behind them.
Bug: T143837
Bug: T202466
Change-Id: I5a78949efef88083bdafc4207a6872c76b463970
Even though Array.prototype.forEach only works on arrays, and
$.each is more generic, I think it makes sense to begin discouraging
the usage of $.each now. This can be overriden by ignore lines or
by Array.prototype.forEach compatible lines. This doesn't seem too
much of an ask of engineers and helps future migrations
Bug: T200877
Change-Id: I339cff311a830699c8e32f07cec338a39870c53f
Also prevent further usage by an eslint rule.
jQuery.proxy is deprecated:
* https://github.com/jquery/jquery/issues/2958
Bug: T200877
Change-Id: I3a4977f9b90c2104db320d2d939a1cbaa1819de0
After execute() is finished and markModuleReady() is called,
the execute path reaches handlePending() which calls
mw.loader.store.set(). This prepares the contents of the module
we just finished executing, for localStorage.
While the writing to localStorage was already debounced via #update,
the stringification of all functions and stylesheets was still
happening within the execute path, and other checks as well.
This commit replaces the mw.loader.store.set() call with a new
method mw.loader.store.add(). The serialisation is now performed
as part of flushWrites(), which is the debounced update that
happens in an idle callback.
While mw.loader.store is itself private within mw.loader, this
commit also marks the set() and update() methods as @private
within mw.loader.store as they are not (and should not) called
from outside this class.
Bug: T202703
Bug: T127328
Change-Id: I23ef28e096acf3bab57b88922ba21366fed06155
Notable change:
- Ajax: In Safari, $.ajax() previously did not reject the
Deferred if the XHR timed out. It now does, just as
it previously did already in other browsers.
- Deferred: Fixed a memory leak with callback closures.
- All: Misc perf optimisations and removal of code that
provided compat for browsers no longer supported.
Full release notes at
- https://blog.jquery.com/2018/01/19/jquery-3-3-0-a-fragrant-bouquet
- https://blog.jquery.com/2018/01/20/jquery-3-3-1-fixed-dependencies
Change-Id: Ia141dd46dc5b97c0f9766b44fea7559b1148538a
There are two signatures for this method that we test, document,
and use:
1. register( name, version, ... )
2. register( [ [name,version,...], [name,version,..], ... ] )
But the code was also allowing a third kind that is never used,
and also not documented anywhere or tested.
3. register( [ name, name, ... ] )
Change-Id: I3ed69117affd83d03c4c629d352f19bad50395c9
The check above with the mw.loader.register( name ) ensures that
hasOwn.call( registry, name )
is always true.
Change-Id: I10e2a23bd5c11fbf53fd4fc59ba2e5d94f157254
We were incorrectly escaping them. They are supposed to be already
correctly escaped HTML.
Also improve documentation and really allow 'tooltips' to be optional.
Bug: T203325
Change-Id: I1f92479bf1989e1529b18b8b206b61db1257eb87
Replace jQuery.inArray with Array.prototype.indexOf.
Also enforce this via eslint rule.
Bug: T200877
Change-Id: Idbd06e6a1681300c4ab9142c7b57e4376f474041
Array was not properly initialized and thus browsers
that do not support Crypto API where displaying an error
on console.
The tests failed to catch this because assigning window.crypto
to `undefined` does not work (it is a read-only property). This
"fallback" test was actually testing the regular Crypto-based path
a second time.
Bug: T203275
Co-Authored-By: Timo Tijhof <krinklemail@gmail.com>
Change-Id: I8feecddf0878a739e560085f7897ebc3d8100c02
When capturing an Allocation Timeline in Chrome DevTools' Memory
panel, I noticed that there are a *ton* of strings and StringSet
memory allocations happening in ways I did not anticipate would
happen as part of calling mw.config.set().
Given that legacy globals are currently enabled on WMF wikis,
this means that when calling mw.config.set() in the startup
module, the page header, and page footer (typically about ~200
keys in total), it also calls mw.log.deprecate() to create the
global alias.
And mw.log.deprecate() was allocating the StringSet and log
messages when creating the property, instead of only if and when
a 'get' or 'set' is triggered, which itself should be rare given
that the aliases are deprecated. So even when they are never called,
they were still creating a lot of objects that aren't used.
This commit instead creates the StringSet object lazily.
Also, given that logging is deduplicated, create the log message
only where we use it (once), instead of storing in the outer
closure that persists.
Bug: T127328
Change-Id: I7a16277f743ff39d81f8746dd2147ed8797c1c7a
Shorter and more intuitive. All of these functions are always
called as methods on the mw.loader.store objects, not detached.
Change-Id: If26851eac1530f023228897392c5067c6e8927af
The outer expression already casts the result to a boolean.
Unit tests in startup.test.js also strictly assert that the
returned values are boolean.
Change-Id: I5709fcd0184b99d289b9cdfeccf8afa960806d59
== jQuery
ResourceLoader wraps and executes all modules in the system via
a closure that explicitly binds '$', 'jQuery'. This means there is
no point in aliasing jQuery to $ in every single file.
ResourceLoader already does this.
This is a very very old habit that was introduced in 2009 when we
didn't have ResourceLoader and were concerned with wikis loading
their own copy of jQuery that could redefine the global 'jQuery'
and '$' variables. We simply hoped that "our module" initialised
before "that module" cache the reference we got in the file closure.
Then in 2010, when building ResourceLoader, we found this didn't
always work. And we also sometimes forgot to add the closure.
Which is why in 2010 (before ResourceLoader went to prod, in 2011)
we fixed the above issue in ResourceLoader itself by "magically"
providing a private binding to '$' and 'jQuery' in every
mw.loader.implement() closure. (r79246, bd93eeb85).
So, these in-file closure references are redundant.
And have been since 2010.
== jQuery, again.
While redundant, they remained in most files. Harmless, right?
However, that same problem of duplicate jQuery copys on a page
came up again in 2013. Why did our magic binding not work?
It was *because* the file also did its own binding:
1. ResourceLoader stores reference to proper jQuery.
2. ResourceLoader provides private reference to it as '$'.
3. .. time passes ..
4. Module executes, and is given the proper jQuery via the
private '$' reference. The module file ignores this because
it instead looks up current jQuery, and caches that.
So, we expande the magic binding to also bind the name 'jQuery'.
(2013-2014; 5742c1f385).
== mediaWiki
We export the binding as 'mw' and 'mediaWiki'. We internally
mostly use 'mw' (in HTML, and documentation, and the canonical
name in the JSDuck index). But, rather than using the shorter name,
we use the longer name and alias it in every single file.
There was never a concern about this global being redefined
as far as I know. However, if that happens one day, we should..
provide a magic binding for it.
Change-Id: Id6d13bbea6927a4c7354ca1edd98f13f0fae30c1
When execute() begins, we are in a stack that is suitable for
script execution. In the case of styles needing to be inserted,
we end that stack by scheduling the styles insertion via
requestAnimationFrame. When that styles insertion is completed,
we are currently continuing the script execution in that same
stack, which is bad for performance and also creates a confusing
call stack, given that none of that code is paint or animation
related.
Before we can address that, e.g. by unwinding the stack via
requestIdleCallback, we first need to clean up the code to not
needlessly involve checkCssHandles for modules without styles.
Most modules are small and have only scripts, no styles, so this
is important to keep in a single stack and rapidly executed in
one batch - without interuption.
This commit moves the logic that was previously inside checkCssHandles
to the bottom of execute(), which is where checkCssHandles used to
be called from.
Bug: T202703
Change-Id: I13a996e01b48bab477e68ce933a5e2ef05b361aa
* Previously, the same function was used both for adding to the buffer,
and flushing the buffer (via self-calling alternate signatures).
The flushing logic was split off to a flushCssBuffer function.
* Previously, when encountering an '@import' statement, it performed a
synchronous flush, instead of the usual asynchronous ones. There was
no reason for this, other than my laziness. I suspect because I was
using strings, which can't be passed by reference, and I didn't
think of another way.
I'm now storing the string in an object, which can be passed by
reference to the flush function. This means, as before, we can
keep appending to its string after the flush is scheduled. But,
unlike before, it also means we can reset our local reference and
start a new buffer at any time and schedule that one, too.
Bug: T202703
Change-Id: Ifc6dd59e9e8885d65ba425bc579ecbfb09f2ac64
== Overview
This reverses a small part of the big dec800968e commit. That
commit eliminated the 2nd of 3 JS requests required on a page.
The first JS request is now modules=startup with the registry and
mw.loader. The main JS request for modules (now 2nd) is triggered
from RLQ, which fires when startup.js calls 'startUp()'.
== Regression
In commit dec800968e, I thought I was being smart by letting the
browser "take a breath" at the end of startup.js before calling
'startUp()'. I now believe that this artifical is a mistake.
I thought it was a good idea because, before dec800968e startup.js
made an http request (for jquery). During that network request the
main thread was free to do rendering. Then, when jquery came back,
we called startUp() to trigger our main request for modules (3rd,
now 2nd). While this request no longer exists, the idea was to keep
that thread yield at the end to startup.js.
I now think it was a mistake because requestIdleCallback is not
a "small" thread yield. Rather, I now find in production that in
the majority of cases (all mobile, and desktop first views) the
rIC is not called until *after* the page load event fires.
This means that during the entire page rendering time, the network
and javascript thread were sitting idle. Prioritising rendering and
making the page load complete 0.5s faster is great. But,
underutilising the network and delaying time to JS interation by 1-2
seconds, is bad.
== Cost
The startUp() function is quite light. All it does is a bunch of
pure JavaScript processing (no DOM), and then creates 1 script tag
On desktop (MacBook) with 6x CPU slowdown and mobile emulation
(enwiki/Obama) it clocks startUp() at 35ms.
On a real mobile device (Nexus 5, Android 4.4, the oldest currently
supported Android version/device I could find on BrowserStack) it
clocks startUp() on enwiki/Obama at 44ms.
This seems small enough to make it worth doing right away, without
artificial delay, so that it can kick off the network request in
the background while we're rendering - which is exactly how things
were before commmit dec800968e was merged.
This means the 'load' event will once again include the main JS
request. After dec800968e, this request unintentionally started
after the 'load' event. I'm hoping that with the eliminated
request for jquery+mediawiki.base, we'll still keep some of the
winnings we had from a much earlier 'load' event.
Bug: T192623
Change-Id: I4942bfd236c72b0cf4c23b0e2a0d5e5e069c0de0
* Document the conditional that disables mw.loader.store if
localStorage is unavailable. `raw === undefined` is correct,
but can be confusing given it can also be null, and we
specifically don't want to disable the store in that case.
We only disable the store if raw is undefined.
* Remove the call to mw.loader.store.update() in init().
If 0 modules were to load on the current page, there is little
value in flushing an empty `items` object.
If any modules load, they will be set() in the store and
schedule an update() at that time, which is preferred and avoids
unexpected overhead.
* Remove store.enabled check from prune(). It is only called
from update(), which checks it already.
* Remove boolean return from set(). Not used.
* Remove boolean return from prune(). Not used.
* Restore the 2-second setTimeout debounce from 2013 (c719401661),
which got lost in the 2015 refactor (4174b662f6).
This makes the documentation true again.
* Document the store singleton as @private. It will still be
indexed by JSDuck and listed in the sidebar (under "private"),
but will not be listed on the homepage, and the class' page
will have a notice about it being a private API.
Bug: T202598
Change-Id: Ic0d4a15c241df391ab5f824ca9e754c3938ea108
Opera 12 is no longer supported for Grade A. This regex was currently
running on every module script executed on every page in between
each module's execution.
execute() for module A -> handlePending -> mw.loader.store.set()
-> Opera 12 check -> schedule update -> yield to module B.
The serialisation logic in store.set() should probably more
generally deferred and taken out of this path, but until then,
every bit helps.
Change-Id: I8d4628b6d663fe25040c6ef0b4983a4b7eea94c6
The list of module names are known to be registered there.
Thus, no need for getState() and its hasOwn.call(registry) check.
If it was possible for an unknown module name to make its way to this code,
it would break both before and after this commit due to the setting of
registry[module].state which would throw TypeError for setting on undefined.
Change-Id: Ie3cccc7e4c57648279325852f362d5eb72128306
The optional 'dependencies' parameter is only ever used with
an array of strings, never just as a string.
Also fix the missing square-brackets around the parameter name
in the documentation that indicate the parameter as optional.
The code was already allowing it to be missing, and the server
does indeed omit it when a module has no dependencies.
(See ResourceLoaderStartupModuleTest for confirmation of that).
The server-side methods ResourceLoaderModule::getDependencies
and ResourceLoader::makeLoaderRegisterScript already don't
support string-type for this parameter.
Change-Id: I80a1e4b2eefb62e669c031e0d953bf74a9623264
The support for 'string' type of the 'dependencies' parameter is
unused. It is a private method and only called with an array
of strings.
Change-Id: I6252ec583242df46ad4c1913519cce4ef61759d7
23 bytes over 46, so even though it's slightly slower in Chrome this way
it's faster in Firefox and it's close enough that it's not worth worrying
over.
Change-Id: I5e917c6d6caee730e54d1d5bacb0d9b249b515e2
Object.create(null) is specifically for the use case of being
able to store any key/value without worrying about inherited
built-in methods.
As such, using Object.create(null) in these cases has two benefits:
* Removes the need for a hasOwn() call.
One can instead use the 'in' operator, or look for undefined,
without there being gotchas due to inherited methods.
* When the known values are always truthy (e.g. when only storing
value that are also objects or arrays), there is no need for
any explicit comparison (plain boolean evaluation).
Change-Id: I24f86f9938e4f5ccca81d15aa14a48ac3ddee341
generateRandomSessionId now generates a token with 80 bits and it is
unique in a 150 billion space.
The token is used to identify sessions in MediaWiki but also
as a generic random generator across the MediaWiki codebase.
Bug: T201124
Change-Id: I63a241a70b99d9f9e691eac25bb5cfe099a748fc
When enabling $wgResourceLoaderEnableJSProfiler, mw.loader gets instrumented
with the following timing values for each of the modules loaded on the page:
* 'total' - This measures the time spent in mw.loader#execute(), and
represents the initialisation of the module's implementation, including
the registration of messages, templates, and the execution of the 'script'
closure received from load.php.
* 'script' – This measures only the subset of time spent in the internal
runScript() function, and represents just the execution of the module's
JavaScript code as received through mw.loader.implement() from load.php.
For user scripts and site scripts, this measures the call to domEval
(formerly known as "globalEval").
* 'execute' - This measures the self time of mw.loader#execute(), which is
effectively `total - script`.
To view the report, enable the feature, then run `mw.inspect( 'time' )` from
the browser console, which will render a table with the initialisation
overhead from each module used on the page.
Bug: T133646
Change-Id: I68d1193b62c93c97cf09b7d344c896afb437c5ac
This is to make sure that the design is similar, but also so
that the widget can be read in JS where needed and that we
can toggle the disabled state on/off through the whole widget,
that is made from a series of checkbox widgets.
Bug: T199946
Change-Id: I9943b0aa1746fdfb60c7d4c88d6d4d7ac0589a2c
New name is more descriptive and consistent with
how we use it.
Given the couple of usages in code, deprecate
the old method
Bug: T201124
Change-Id: Id43e9d3f3e687133c98017d24c10c307a5a855a1