Last modified: 2012-06-20 08:28:18 UTC

Wikimedia Bugzilla is closed!

Wikimedia migrated from Bugzilla to Phabricator. Bug reports are handled in Wikimedia Phabricator.
This static website is read-only and for historical purposes. It is not possible to log in and except for displaying bug reports and their history, links might be broken. See T32446, the corresponding Phabricator task for complete and up-to-date bug report information.
Bug 30446 - mw.loader.using always calls ready callback for unregistered modules
mw.loader.using always calls ready callback for unregistered modules
Status: RESOLVED WORKSFORME
Product: MediaWiki
Classification: Unclassified
ResourceLoader (Other open bugs)
unspecified
All All
: Normal normal (vote)
: ---
Assigned To: Nobody - You can work on this!
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2011-08-18 16:31 UTC by darklama
Modified: 2012-06-20 08:28 UTC (History)
4 users (show)

See Also:
Web browser: ---
Mobile Platform: ---
Assignee Huggle Beta Tester: ---


Attachments

Description darklama 2011-08-18 16:31:35 UTC
mw.loader.using always calls ready callback for unregistered modules,
when the callback shouldn't get called at all.

Examples:

mw.loader.using('', function() { alert('running...'); });
mw.loader.using([], function() { alert('running...'); });
mw.loader.using([''], function() { alert('running...'); });
mw.loader.using('foobar', function() { alert('running...'); });
mw.loader.using(['foobar'], function() { alert('running...'); });

I've been able to trace this back to a few problems that are
probably also causing bugs in other parts of mediawiki.js as well.

First, resolve() returns [] for each module that isn't previously
registered. In most places the return value of resolve() is put
back into the same variable as was passed (like
dependencies = resolve(dependencies)), when the original value
might be needed for comparison.

Second, the variable passed as the second argument of filter()
is replaced with all registered modules when passed []. As
can happen when the return value of resolve() is used and it
is passed unregistered modules. Again the original value of
the second argument might be needed for comparison.

Third, compare() may return true or false in ways not intended
at times. Like when used to compare filtered and unfiltered
return results of resolve(unregistered modules). The filtered
and unfiltered results may be the same at times depending on
the current state of all registered modules. The ready()
or error() callback function is called immediately if the
filtered and unfiltered list of all registered modules
happens to be the same at the time.

Four, if somehow both compare tests manage to fail, every
registered module will be passed to request(). Whatever happens
to be the first registered module will have its dependencies
appended to everything else, this new list will be added to
jobs and queue, and work() will do a lot of extra work.

Finally, AFAIK if request() is somehow called neither callbacks
are called because request() doesn't call them and doesn't pass
them onto queue, so work() has no way of calling them either.
Comment 1 Brion Vibber 2011-08-23 18:51:11 UTC
Presumably we should call the error callback in this case...?

Adding needs-unittest keyword as these things should be tested in the loader tests.
Comment 2 darklama 2011-08-23 20:09:04 UTC
No. An unregistered module needs to inject a request and let
the load system know we want to know about any future modules
that are registered by the given name, so it can call the
ready or error callback when an attempt to load the module is
finally made and either succeeds or fails.

mw.loader.using could presumably return true if at the time
it is called all requested modules are registered or false
otherwise.
Comment 3 darklama 2011-08-23 20:36:15 UTC
To explain a bit more,

mw.loader.using might be called before:

1. One or more modules are known about.
2. Any dependencies are known about.
3. Dependencies have reached ready or error status.
4. One or more modules have reached ready or error status.

mw.loader.using might be called after:

5. Some modules are known about and others are not.
6. Some dependencies are known about and others are not.
7. Some dependencies have reached ready status and others have not.
8. Some dependencies have reached error status.
9. All modules and dependencies have reached ready status.

1 through 7 require deferred checking until 8 or 9 is true. 8 can
call the error callback immediately. 9 can call the ready callback
immediately.
Comment 4 Brion Vibber 2011-08-23 20:41:59 UTC
... so you agree with me that the error callback should be called at load time when you did a mediawiki.loader.using() call for an unregistered module that stays unregistered?
Comment 5 darklama 2011-08-23 23:17:37 UTC
(In reply to comment #4)
> ... so you agree with me that the error callback should be called at load time
> when you did a mediawiki.loader.using() call for an unregistered module that
> stays unregistered?

Yes, if you mean mw.loader.load(module) is called before or without
mw.loader.register(module) or mw.loader.implement(module).
Comment 6 Krinkle 2011-08-24 23:08:57 UTC
(In reply to comment #5)
> (In reply to comment #4)
> > ... so you agree with me that the error callback should be called at load time
> > when you did a mediawiki.loader.using() call for an unregistered module that
> > stays unregistered?
> 
> Yes, if you mean mw.loader.load(module) is called before or without
> mw.loader.register(module) or mw.loader.implement(module).


.load() indeed, as well as for .using()
Comment 7 Krinkle 2011-08-24 23:09:21 UTC
(In reply to comment #3)
> mw.loader.using might be called before:
> 1. One or more modules are known about.

What about that case though ?
Comment 8 darklama 2011-08-25 01:06:02 UTC
(In reply to comment #7)
> (In reply to comment #3)
> > mw.loader.using might be called before:
> > 1. One or more modules are known about.
> 
> What about that case though ?

Deffer, as I said in comment #3. mw.loader.using should
work similarly for modules as how $(document).ready does
for a document.
Comment 9 darklama 2011-08-25 01:10:45 UTC
(In reply to comment #6)
> (In reply to comment #5)
> > (In reply to comment #4)
> > > ... so you agree with me that the error callback should be called at load time
> > > when you did a mediawiki.loader.using() call for an unregistered module that
> > > stays unregistered?
> > 
> > Yes, if you mean mw.loader.load(module) is called before or without
> > mw.loader.register(module) or mw.loader.implement(module).
> 
> 
> .load() indeed, as well as for .using()

I disagree. .using() should be safe to call before .register and
.implement. Just as $(document).ready is safe to call before the
document is ready and the callback isn't called until the document
is ready.
Comment 10 Krinkle 2012-06-20 08:27:38 UTC
(In reply to comment #0)
> mw.loader.using always calls ready callback for unregistered modules,
> when the callback shouldn't get called at all.
> 
> Examples:
> 
> mw.loader.using('', function() { alert('running...'); });
> mw.loader.using([], function() { alert('running...'); });
> mw.loader.using([''], function() { alert('running...'); });
> mw.loader.using('foobar', function() { alert('running...'); });
> mw.loader.using(['foobar'], function() { alert('running...'); });
> 

This has been fixed in 1.19.0 or 1.20-alpha (not sure) and was recorded under another bug as well.

These examples now result in:
> Error: Unknown dependency: foobar

(except for mw.loader.using( [] ), which as expected requires no modules.)

Note You need to log in before you can comment on or make changes to this bug.


Navigation
Links