jenkins-bot has submitted this change and it was merged.
Change subject: resourceloader: Async all the way
......................................................................
resourceloader: Async all the way
Page startup:
* Due to the startup module and top queue being asynchronous now,
move client-nojs/client-js class handling to OutputPage to ensure
there is no flashes of wrongly styled or unstyled content.
To preserve compatibility for unsupported browsers, undo the
class swap at runtime after the isCompatible() check.
ResourceLoader startup module:
* Load the startup module with <script async>.
* Use DOM methods instead of 'document.write' to create base module request
(jquery|mediawiki).
mw.loader:
* Drop 'async' parameter from mw.loader.load().
* Remove the now-unused code paths for synchronous requests.
OutputPage:
* Drop '$loadCall' parameter from makeResourceLoaderLink().
Asynchronous is now the default and only way to load JavaScript.
This means the 'user' module "conditional document-write scripts"
are now a simple "mw.loader.load( url )" call.
* Fix incorrect @return of makeResourceLoaderLink(). This returns
an array not a string.
* Improve documentation of makeResourceLoaderLink().
* Drop '$inHead' parameter from getScriptsForBottomQueue(). No longer used.
Compatibility with the $wgResourceLoaderExperimentalAsyncLoading
feature is maintained. It just no longer needs to change the
way the queue works since it's always asynchronous. The feature
flag now only controls whether the bottom queue starts at the bottom
or starts at the top.
* Remove jQuery.ready() optimisation.
This was mostly there to avoid the setTimeout() loop jQuery does to detect
dom-ready in IE6/IE7 (which we no longer serve JavaScript at all).
And for a bug in Firefox with document.write (which is no longer used as of
this commit).
Bug: T107399
Change-Id: Icba6d7a87b239bf127a221bc6bc432cfa71a4a72
---
M includes/OutputPage.php
M includes/resourceloader/ResourceLoaderStartUpModule.php
M includes/specials/SpecialJavaScriptTest.php
M resources/src/mediawiki.page/mediawiki.page.patrol.ajax.js
M resources/src/mediawiki.page/mediawiki.page.startup.js
M resources/src/mediawiki.page/mediawiki.page.watch.ajax.js
M resources/src/mediawiki/mediawiki.js
M resources/src/startup.js
M tests/phpunit/includes/OutputPageTest.php
9 files changed, 150 insertions(+), 183 deletions(-)
Approvals:
Ori.livneh: Looks good to me, approved
jenkins-bot: Verified
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
index 07c1a8a..0f0b2f5 100644
--- a/includes/OutputPage.php
+++ b/includes/OutputPage.php
@@ -2703,7 +2703,6 @@
}
$ret .= $this->buildCssLinks() . "\n";
-
$ret .= $this->getHeadScripts() . "\n";
foreach ( $this->mHeadItems as $item ) {
@@ -2762,18 +2761,16 @@
}
/**
- * @todo Document
+ * Construct neccecary html and loader preset states to load modules on
a page.
+ *
+ * Use getHtmlFromLoaderLinks() to convert this array to HTML.
+ *
* @param array|string $modules One or more module names
* @param string $only ResourceLoaderModule TYPE_ class constant
- * @param array $extraQuery Array with extra query parameters to add to
each
- * request. array( param => value ).
- * @param bool $loadCall If true, output an (asynchronous)
mw.loader.load()
- * call rather than a "<script src='...'>" tag.
- * @return string The html "<script>", "<link>" and "<style>" tags
+ * @param array $extraQuery [optional] Array with extra query
parameters for the request
+ * @return array A list of HTML strings and array of client loader
preset states
*/
- public function makeResourceLoaderLink( $modules, $only, array
$extraQuery = array(),
- $loadCall = false
- ) {
+ public function makeResourceLoaderLink( $modules, $only, array
$extraQuery = array() ) {
$modules = (array)$modules;
$links = array(
@@ -2796,7 +2793,7 @@
if ( ResourceLoader::inDebugMode() ) {
// Recursively call us for every item
foreach ( $modules as $name ) {
- $link = $this->makeResourceLoaderLink(
$name, $only );
+ $link = $this->makeResourceLoaderLink(
$name, $only, $extraQuery );
$links['html'] = array_merge(
$links['html'], $link['html'] );
$links['states'] += $link['states'];
}
@@ -2908,29 +2905,18 @@
// Automatically select style/script elements
if ( $only ===
ResourceLoaderModule::TYPE_STYLES ) {
$link = Html::linkedStyle( $url );
- } elseif ( $loadCall ) {
- $link =
ResourceLoader::makeInlineScript(
- Xml::encodeJsCall(
'mw.loader.load', array( $url, 'text/javascript', true ) )
- );
} else {
- $link = Html::linkedScript( $url );
- if ( !$context->getRaw() && !$isRaw ) {
- // Wrap only=script /
only=combined requests in a conditional as
- // browsers not supported by
the startup module would unconditionally
- // execute this module.
Otherwise users will get "ReferenceError: mw is
- // undefined" or "jQuery is
undefined" from e.g. a "site" module.
+ if ( $context->getRaw() || $isRaw ) {
+ // Startup module can't load
itself, needs to use <script> instead of mw.loader.load
+ $link = Html::element(
'script', array(
+ // In
SpecialJavaScriptTest, QUnit must load synchronous
+ 'async' => !isset(
$extraQuery['sync'] ),
+ 'src' => $url
+ ) );
+ } else {
$link =
ResourceLoader::makeInlineScript(
- Xml::encodeJsCall(
'document.write', array( $link ) )
+ Xml::encodeJsCall(
'mw.loader.load', array( $url ) )
);
- }
-
- // For modules requested directly in
the html via <link> or <script>,
- // tell mw.loader they are being
loading to prevent duplicate requests.
- foreach ( $grpModules as $key =>
$module ) {
- // Don't output state=loading
for the startup module..
- if ( $key !== 'startup' ) {
- $links['states'][$key]
= 'loading';
- }
}
}
@@ -2980,8 +2966,20 @@
* @return string HTML fragment
*/
function getHeadScripts() {
- // Startup - this will immediately load jquery and mediawiki
modules
$links = array();
+
+ // Client profile classes for <html>. Allows for easy
hiding/showing of UI components.
+ // Must be done synchronously on every page to avoid flashes of
wrong content.
+ // Note: This class distinguishes MediaWiki-supported
JavaScript from the rest.
+ // The "rest" includes browsers that support JavaScript but not
supported by our runtime.
+ // For the performance benefit of the majority, this is added
unconditionally here and is
+ // then fixed up by the startup module for unsupported browsers.
+ $links[] = Html::inlineScript(
+ 'document.documentElement.className =
document.documentElement.className'
+ . '.replace( /(^|\s)client-nojs(\s|$)/, "$1client-js$2"
);'
+ );
+
+ // Startup - this provides the client with the module manifest
and loads jquery and mediawiki base modules
$links[] = $this->makeResourceLoaderLink( 'startup',
ResourceLoaderModule::TYPE_SCRIPTS );
// Load config before anything else
@@ -3013,7 +3011,7 @@
);
if ( $this->getConfig()->get(
'ResourceLoaderExperimentalAsyncLoading' ) ) {
- $links[] = $this->getScriptsForBottomQueue( true );
+ $links[] = $this->getScriptsForBottomQueue();
}
return self::getHtmlFromLoaderLinks( $links );
@@ -3026,23 +3024,21 @@
* 'bottom', legacy scripts ($this->mScripts), user preferences, site JS
* and user JS.
*
- * @param bool $inHead If true, this HTML goes into the "<head>",
- * if false it goes into the "<body>".
+ * @param bool $unused Previously used to let this method change its
output based
+ * on whether it was called by getHeadScripts() or getBottomScripts().
* @return string
*/
- function getScriptsForBottomQueue( $inHead ) {
+ function getScriptsForBottomQueue( $unused = null ) {
// Scripts "only" requests marked for bottom inclusion
// If we're in the <head>, use load() calls rather than <script
src="..."> tags
$links = array();
$links[] = $this->makeResourceLoaderLink(
$this->getModuleScripts( true, 'bottom' ),
- ResourceLoaderModule::TYPE_SCRIPTS, /* $extraQuery = */
array(),
- /* $loadCall = */ $inHead
+ ResourceLoaderModule::TYPE_SCRIPTS
);
$links[] = $this->makeResourceLoaderLink(
$this->getModuleStyles( true, 'bottom' ),
- ResourceLoaderModule::TYPE_STYLES, /* $extraQuery = */
array(),
- /* $loadCall = */ $inHead
+ ResourceLoaderModule::TYPE_STYLES
);
// Modules requests - let the client calculate dependencies and
batch requests as it likes
@@ -3050,7 +3046,7 @@
$modules = $this->getModules( true, 'bottom' );
if ( $modules ) {
$links[] = ResourceLoader::makeInlineScript(
- Xml::encodeJsCall( 'mw.loader.load', array(
$modules, null, true ) )
+ Xml::encodeJsCall( 'mw.loader.load', array(
$modules ) )
);
}
@@ -3069,7 +3065,7 @@
// We're on a preview of a JS subpage. Exclude this
page from the user module (T28283)
// and include the draft contents as a raw script
instead.
$links[] = $this->makeResourceLoaderLink( 'user',
ResourceLoaderModule::TYPE_COMBINED,
- array( 'excludepage' =>
$this->getTitle()->getPrefixedDBkey() ), $inHead
+ array( 'excludepage' =>
$this->getTitle()->getPrefixedDBkey() )
);
// Load the previewed JS
$links[] = ResourceLoader::makeInlineScript(
@@ -3093,15 +3089,11 @@
// the excluded subpage.
} else {
// Include the user module normally, i.e., raw to avoid
it being wrapped in a closure.
- $links[] = $this->makeResourceLoaderLink( 'user',
ResourceLoaderModule::TYPE_COMBINED,
- /* $extraQuery = */ array(), /* $loadCall = */
$inHead
- );
+ $links[] = $this->makeResourceLoaderLink( 'user',
ResourceLoaderModule::TYPE_COMBINED );
}
// Group JS is only enabled if site JS is enabled.
- $links[] = $this->makeResourceLoaderLink( 'user.groups',
ResourceLoaderModule::TYPE_COMBINED,
- /* $extraQuery = */ array(), /* $loadCall = */ $inHead
- );
+ $links[] = $this->makeResourceLoaderLink( 'user.groups',
ResourceLoaderModule::TYPE_COMBINED );
return self::getHtmlFromLoaderLinks( $links );
}
@@ -3114,17 +3106,11 @@
// In case the skin wants to add bottom CSS
$this->getSkin()->setupSkinUserCss( $this );
- // Optimise jQuery ready event cross-browser.
- // This also enforces $.isReady to be true at </body> which
fixes the
- // mw.loader bug in Firefox with using document.write between
</body>
- // and the DOMContentReady event (bug 47457).
- $html = Html::inlineScript( 'if(window.jQuery)jQuery.ready();'
);
-
- if ( !$this->getConfig()->get(
'ResourceLoaderExperimentalAsyncLoading' ) ) {
- $html .= $this->getScriptsForBottomQueue( false );
+ if ( $this->getConfig()->get(
'ResourceLoaderExperimentalAsyncLoading' ) ) {
+ // Already handled by getHeadScripts()
+ return '';
}
-
- return $html;
+ return $this->getScriptsForBottomQueue();
}
/**
diff --git a/includes/resourceloader/ResourceLoaderStartUpModule.php
b/includes/resourceloader/ResourceLoaderStartUpModule.php
index 16424a0..bdf1b21 100644
--- a/includes/resourceloader/ResourceLoaderStartUpModule.php
+++ b/includes/resourceloader/ResourceLoaderStartUpModule.php
@@ -335,7 +335,7 @@
}, array(
'$VARS.wgLegacyJavaScriptGlobals' =>
$this->getConfig()->get( 'LegacyJavaScriptGlobals' ),
'$VARS.configuration' => $this->getConfigSettings(
$context ),
- '$VARS.baseModulesScript' => Html::linkedScript(
self::getStartupModulesUrl( $context ) ),
+ '$VARS.baseModulesUri' => self::getStartupModulesUrl(
$context ),
) );
$pairs['$CODE.registrations()'] = str_replace( "\n", "\n\t",
trim( $this->getModuleRegistrations( $context ) ) );
diff --git a/includes/specials/SpecialJavaScriptTest.php
b/includes/specials/SpecialJavaScriptTest.php
index 442b764..b5f3815 100644
--- a/includes/specials/SpecialJavaScriptTest.php
+++ b/includes/specials/SpecialJavaScriptTest.php
@@ -210,7 +210,20 @@
$query['only'] = 'scripts';
$startupContext = new ResourceLoaderContext( $rl, new
FauxRequest( $query ) );
+ $query['raw'] = true;
+
$modules = $rl->getTestModuleNames( 'qunit' );
+
+ // Disable autostart because we load modules asynchronously. By
default, QUnit would start
+ // at domready when there are no tests loaded and also fire
'QUnit.done' which then instructs
+ // Karma to end the run before the tests even started.
+ $qunitConfig = 'QUnit.config.autostart = false;'
+ . 'if (window.__karma__) {'
+ // karma-qunit's use of autostart=false and QUnit.start
conflicts with ours.
+ // Hack around this by replacing 'karma.loaded' with a
no-op and call it ourselves later.
+ // See
<https://github.com/karma-runner/karma-qunit/issues/27>.
+ . 'window.__karma__.loaded = function () {};'
+ . '}';
// The below is essentially a pure-javascript version of
OutputPage::getHeadScripts.
$startup = $rl->makeModuleResponse( $startupContext, array(
@@ -225,35 +238,39 @@
'user.options' => $rl->getModule( 'user.options' ),
'user.tokens' => $rl->getModule( 'user.tokens' ),
) );
- $code .= Xml::encodeJsCall( 'mw.loader.load', array( $modules )
);
+ // Catch exceptions (such as "dependency missing" or "unknown
module") so that we
+ // always start QUnit. Re-throw so that they are caught and
reported as global exceptions
+ // by QUnit and Karma.
+ $code .= '(function () {'
+ . 'var start = window.__karma__ ?
window.__karma__.start : QUnit.start;'
+ . 'try {'
+ . 'mw.loader.using( ' . Xml::encodeJsVar( $modules ) .
' ).always( start );'
+ . '} catch ( e ) { start(); throw e; }'
+ . '}());';
header( 'Content-Type: text/javascript; charset=utf-8' );
header( 'Cache-Control: private, no-cache, must-revalidate' );
header( 'Pragma: no-cache' );
+ echo $qunitConfig;
echo $startup;
- echo "\n";
- // Note: The following has to be wrapped in a script tag
because the startup module also
- // writes a script tag (the one loading mediawiki.js). Script
tags are synchronous, block
- // each other, and run in order. But they don't nest. The code
appended after the startup
- // module runs before the added script tag is parsed and
executed.
- echo Xml::encodeJsCall( 'document.write', array(
Html::inlineScript( $code ) ) );
+ // The following has to be deferred via RLQ because the startup
module is asynchronous.
+ echo ResourceLoader::makeLoaderConditionalScript( $code );
}
private function plainQUnit() {
$out = $this->getOutput();
$out->disable();
- $url = $this->getPageTitle( 'qunit/export' )->getFullURL( array(
- 'debug' => ResourceLoader::inDebugMode() ? 'true' :
'false',
- ) );
-
$styles = $out->makeResourceLoaderLink( 'jquery.qunit',
ResourceLoaderModule::TYPE_STYLES
);
- // Use 'raw' since this is a plain HTML page without
ResourceLoader
+
+ // Use 'raw' because QUnit loads before ResourceLoader
initialises (omit mw.loader.state call)
+ // Use 'test' to ensure OutputPage doesn't use the "async"
attribute because QUnit must
+ // load before qunit/export.
$scripts = $out->makeResourceLoaderLink( 'jquery.qunit',
ResourceLoaderModule::TYPE_SCRIPTS,
- array( 'raw' => 'true' )
+ array( 'raw' => true, 'sync' => true )
);
$head = implode( "\n", array_merge( $styles['html'],
$scripts['html'] ) );
@@ -265,6 +282,10 @@
$summary
<div id="qunit"></div>
HTML;
+
+ $url = $this->getPageTitle( 'qunit/export' )->getFullURL( array(
+ 'debug' => ResourceLoader::inDebugMode() ? 'true' :
'false',
+ ) );
$html .= "\n" . Html::linkedScript( $url );
header( 'Content-Type: text/html; charset=utf-8' );
diff --git a/resources/src/mediawiki.page/mediawiki.page.patrol.ajax.js
b/resources/src/mediawiki.page/mediawiki.page.patrol.ajax.js
index cc72e16..3da1d14 100644
--- a/resources/src/mediawiki.page/mediawiki.page.patrol.ajax.js
+++ b/resources/src/mediawiki.page/mediawiki.page.patrol.ajax.js
@@ -17,7 +17,7 @@
var $spinner, href, rcid, apiRequest;
// Start preloading the notification module (normally
loaded by mw.notify())
- mw.loader.load( ['mediawiki.notification'], null, true
);
+ mw.loader.load( 'mediawiki.notification' );
// Hide the link and create a spinner to show it inside
the brackets.
$spinner = $.createSpinner( {
diff --git a/resources/src/mediawiki.page/mediawiki.page.startup.js
b/resources/src/mediawiki.page/mediawiki.page.startup.js
index ddd4f0c..708dcb5 100644
--- a/resources/src/mediawiki.page/mediawiki.page.startup.js
+++ b/resources/src/mediawiki.page/mediawiki.page.startup.js
@@ -1,12 +1,11 @@
( function ( mw, $ ) {
- mw.page = {};
+ // Support: MediaWiki < 1.26
+ // Cached HTML will not yet have this from OutputPage::getHeadScripts.
+ document.documentElement.className = document.documentElement.className
+ .replace( /(^|\s)client-nojs(\s|$)/, '$1client-js$2' );
- // Client profile classes for <html>
- // Allows for easy hiding/showing of JS or no-JS-specific UI elements
- $( document.documentElement )
- .addClass( 'client-js' )
- .removeClass( 'client-nojs' );
+ mw.page = {};
$( function () {
mw.util.init();
diff --git a/resources/src/mediawiki.page/mediawiki.page.watch.ajax.js
b/resources/src/mediawiki.page/mediawiki.page.watch.ajax.js
index 50f280a..cc1ffb7 100644
--- a/resources/src/mediawiki.page/mediawiki.page.watch.ajax.js
+++ b/resources/src/mediawiki.page/mediawiki.page.watch.ajax.js
@@ -116,7 +116,7 @@
var action, api, $link;
// Start preloading the notification module (normally
loaded by mw.notify())
- mw.loader.load( ['mediawiki.notification'], null, true
);
+ mw.loader.load( 'mediawiki.notification' );
action = mwUriGetAction( this.href );
diff --git a/resources/src/mediawiki/mediawiki.js
b/resources/src/mediawiki/mediawiki.js
index 7825f22..3c5a5f5 100644
--- a/resources/src/mediawiki/mediawiki.js
+++ b/resources/src/mediawiki/mediawiki.js
@@ -1111,39 +1111,24 @@
}
/**
- * Adds a script tag to the DOM, either using
document.write or low-level DOM manipulation,
- * depending on whether document-ready has occurred yet
and whether we are in async mode.
+ * Load and execute a script with callback.
*
* @private
* @param {string} src URL to script, will be used as
the src attribute in the script tag
* @param {Function} [callback] Callback which will be
run when the script is done
- * @param {boolean} [async=false] Whether to load
modules asynchronously.
- * Ignored (and defaulted to `true`) if the
document-ready event has already occurred.
*/
- function addScript( src, callback, async ) {
- // Using isReady directly instead of storing it
locally from a $().ready callback (bug 31895)
- if ( $.isReady || async ) {
- $.ajax( {
- url: src,
- dataType: 'script',
- // Force jQuery behaviour to be
for crossDomain. Otherwise jQuery would use
- // XHR for a same domain
request instead of <script>, which changes the request
- // headers (potentially missing
a cache hit), and reduces caching in general
- // since browsers cache XHR
much less (if at all). And XHR means we retreive
- // text, so we'd need to
$.globalEval, which then messes up line numbers.
- crossDomain: true,
- cache: true,
- async: true
- } ).always( callback );
- } else {
- /*jshint evil:true */
- document.write( mw.html.element(
'script', { 'src': src }, '' ) );
- if ( callback ) {
- // Document.write is
synchronous, so this is called when it's done.
- // FIXME: That's a lie.
doc.write isn't actually synchronous.
- callback();
- }
- }
+ function addScript( src, callback ) {
+ $.ajax( {
+ url: src,
+ dataType: 'script',
+ // Force jQuery behaviour to be for
crossDomain. Otherwise jQuery would use
+ // XHR for a same domain request
instead of <script>, which changes the request
+ // headers (potentially missing a cache
hit), and reduces caching in general
+ // since browsers cache XHR much less
(if at all). And XHR means we retreive
+ // text, so we'd need to $.globalEval,
which then messes up line numbers.
+ crossDomain: true,
+ cache: true
+ } ).always( callback );
}
/**
@@ -1196,7 +1181,7 @@
registry[module].state
= 'ready';
handlePending( module );
};
- nestedAddScript = function (
arr, callback, async, i ) {
+ nestedAddScript = function (
arr, callback, i ) {
// Recursively call
addScript() in its own callback
// for each element of
arr.
if ( i >= arr.length ) {
@@ -1206,12 +1191,12 @@
}
addScript( arr[i],
function () {
-
nestedAddScript( arr, callback, async, i + 1 );
- }, async );
+
nestedAddScript( arr, callback, i + 1 );
+ } );
};
if ( $.isArray( script ) ) {
- nestedAddScript(
script, markModuleReady, registry[module].async, 0 );
+ nestedAddScript(
script, markModuleReady, 0 );
} else if ( $.isFunction(
script ) ) {
// Pass jQuery twice so
that the signature of the closure which wraps
// the script can bind
both '$' and 'jQuery'.
@@ -1261,37 +1246,29 @@
mw.templates.set( module,
registry[module].templates );
}
- if ( $.isReady || registry[module].async ) {
- // Make sure we don't run the scripts
until all (potentially asynchronous)
- // stylesheet insertions have completed.
- ( function () {
- var pending = 0;
- checkCssHandles = function () {
- // cssHandlesRegistered
ensures we don't take off too soon, e.g. when
- // one of the
cssHandles is fired while we're still creating more handles.
- if (
cssHandlesRegistered && pending === 0 && runScript ) {
- runScript();
- runScript =
undefined; // Revoke
+ // Make sure we don't run the scripts until all
stylesheet insertions have completed.
+ ( function () {
+ var pending = 0;
+ checkCssHandles = function () {
+ // cssHandlesRegistered ensures
we don't take off too soon, e.g. when
+ // one of the cssHandles is
fired while we're still creating more handles.
+ if ( cssHandlesRegistered &&
pending === 0 && runScript ) {
+ runScript();
+ runScript = undefined;
// Revoke
+ }
+ };
+ cssHandle = function () {
+ var check = checkCssHandles;
+ pending++;
+ return function () {
+ if ( check ) {
+ pending--;
+ check();
+ check =
undefined; // Revoke
}
};
- cssHandle = function () {
- var check =
checkCssHandles;
- pending++;
- return function () {
- if ( check ) {
-
pending--;
- check();
- check =
undefined; // Revoke
- }
- };
- };
- }() );
- } else {
- // We are in blocking mode, and so we
can't afford to wait for CSS
- cssHandle = function () {};
- // Run immediately
- checkCssHandles = runScript;
- }
+ };
+ }() );
// Process styles (see also mw.loader.implement)
// * back-compat: { <media>: css }
@@ -1358,10 +1335,8 @@
* @param {string|string[]} dependencies Module name or
array of string module names
* @param {Function} [ready] Callback to execute when
all dependencies are ready
* @param {Function} [error] Callback to execute when
any dependency fails
- * @param {boolean} [async=false] Whether to load
modules asynchronously.
- * Ignored (and defaulted to `true`) if the
document-ready event has already occurred.
*/
- function request( dependencies, ready, error, async ) {
+ function request( dependencies, ready, error ) {
// Allow calling by single module name
if ( typeof dependencies === 'string' ) {
dependencies = [dependencies];
@@ -1392,9 +1367,6 @@
return;
}
queue.push( module );
- if ( async ) {
- registry[module].async
= true;
- }
}
} );
@@ -1435,25 +1407,22 @@
}
/**
- * Asynchronously append a script tag to the end of the
body
- * that invokes load.php
+ * Load modules from load.php
* @private
* @param {Object} moduleMap Module map, see
#buildModulesString
* @param {Object} currReqBase Object with other
parameters (other than 'modules') to use in the request
* @param {string} sourceLoadScript URL of load.php
- * @param {boolean} async Whether to load modules
asynchronously.
- * Ignored (and defaulted to `true`) if the
document-ready event has already occurred.
*/
- function doRequest( moduleMap, currReqBase,
sourceLoadScript, async ) {
+ function doRequest( moduleMap, currReqBase,
sourceLoadScript ) {
var request = $.extend(
{ modules: buildModulesString(
moduleMap ) },
currReqBase
);
request = sortQuery( request );
// Support: IE6
- // Append &* to satisfy load.php's
WebRequest::checkUrlExtension test. This script
- // isn't actually used in IE6, but MediaWiki
enforces it in general.
- addScript( sourceLoadScript + '?' + $.param(
request ) + '&*', null, async );
+ // Append &* to satisfy load.php's
WebRequest::checkUrlExtension test.
+ // This script isn't actually used in IE6, but
MediaWiki enforces it in general.
+ addScript( sourceLoadScript + '?' + $.param(
request ) + '&*' );
}
/**
@@ -1502,7 +1471,7 @@
var reqBase, splits,
maxQueryLength, q, b, bSource, bGroup, bSourceGroup,
source, concatSource,
origBatch, group, i, modules, sourceLoadScript,
currReqBase, currReqBaseLength,
moduleMap, l,
- lastDotIndex, prefix, suffix,
bytesAdded, async;
+ lastDotIndex, prefix, suffix,
bytesAdded;
// Build a list of request parameters
common to all requests.
reqBase = {
@@ -1615,7 +1584,6 @@
currReqBase.user = mw.config.get( 'wgUserName' );
}
currReqBaseLength =
$.param( currReqBase ).length;
- async = true;
// We may need to split
up the request to honor the query string length limit,
// so build it piece by
piece.
l = currReqBaseLength +
9; // '&modules='.length == 9
@@ -1639,9 +1607,8 @@
if (
maxQueryLength > 0 && !$.isEmptyObject( moduleMap ) && l + bytesAdded >
maxQueryLength ) {
// This
request would become too long, create a new one
// and
fire off the old one
-
doRequest( moduleMap, currReqBase, sourceLoadScript, async );
+
doRequest( moduleMap, currReqBase, sourceLoadScript );
moduleMap = {};
- async =
true;
l =
currReqBaseLength + 9;
mw.track( 'resourceloader.splitRequest', { maxQueryLength: maxQueryLength } );
}
@@ -1649,17 +1616,11 @@
moduleMap[prefix] = [];
}
moduleMap[prefix].push( suffix );
- if (
!registry[modules[i]].async ) {
- // If
this module is blocking, make the entire request blocking
- // This
is slightly suboptimal, but in practice mixing of blocking
- // and
async modules will only occur in debug mode.
- async =
false;
- }
l += bytesAdded;
}
// If there's anything
left in moduleMap, request that too
if ( !$.isEmptyObject(
moduleMap ) ) {
- doRequest(
moduleMap, currReqBase, sourceLoadScript, async );
+ doRequest(
moduleMap, currReqBase, sourceLoadScript );
}
}
}
@@ -1888,11 +1849,8 @@
* @param {string} [type='text/javascript']
MIME type to use if calling with a URL of an
* external script or style; acceptable values
are "text/css" and
* "text/javascript"; if no type is provided,
text/javascript is assumed.
- * @param {boolean} [async] Whether to load
modules asynchronously.
- * Ignored (and defaulted to `true`) if the
document-ready event has already occurred.
- * Defaults to `true` if loading a URL,
`false` otherwise.
*/
- load: function ( modules, type, async ) {
+ load: function ( modules, type ) {
var filtered, l;
// Validate input
@@ -1902,10 +1860,6 @@
// Allow calling with an external url
or single dependency as a string
if ( typeof modules === 'string' ) {
if ( /^(https?:)?\/\//.test(
modules ) ) {
- if ( async ===
undefined ) {
- // Assume async
for bug 34542
- async = true;
- }
if ( type ===
'text/css' ) {
// Support: IE
7-8
// Use
properties instead of attributes as IE throws security
@@ -1918,7 +1872,7 @@
return;
}
if ( type ===
'text/javascript' || type === undefined ) {
- addScript(
modules, null, async );
+ addScript(
modules );
return;
}
// Unknown type
@@ -1948,7 +1902,7 @@
return;
}
// Since some modules are not yet
ready, queue up a request.
- request( filtered, undefined,
undefined, async );
+ request( filtered, undefined, undefined
);
},
/**
diff --git a/resources/src/startup.js b/resources/src/startup.js
index 97fa134..d4cfa02 100644
--- a/resources/src/startup.js
+++ b/resources/src/startup.js
@@ -93,5 +93,14 @@
// Conditional script injection
if ( isCompatible() ) {
- document.write( $VARS.baseModulesScript );
+ ( function () {
+ var script = document.createElement( 'script' );
+ script.src = $VARS.baseModulesUri;
+ document.getElementsByTagName( 'head' )[0].appendChild( script
);
+ }() );
+} else {
+ // Undo class swapping in case of an unsupported browser.
+ // See OutputPage::getHeadScripts().
+ document.documentElement.className = document.documentElement.className
+ .replace( /(^|\s)client-js(\s|$)/, '$1client-nojs$2' );
}
diff --git a/tests/phpunit/includes/OutputPageTest.php
b/tests/phpunit/includes/OutputPageTest.php
index 69f55c3..6a25d8b 100644
--- a/tests/phpunit/includes/OutputPageTest.php
+++ b/tests/phpunit/includes/OutputPageTest.php
@@ -142,15 +142,13 @@
array(
array( 'test.foo',
ResourceLoaderModule::TYPE_SCRIPTS ),
"<script>var RLQ = RLQ || []; RLQ.push(
function () {\n"
- . 'document.write("\u003Cscript
src=\"http://127.0.0.1:8080/w/load.php?'
- .
'debug=false\u0026amp;lang=en\u0026amp;modules=test.foo\u0026amp;only'
- .
'=scripts\u0026amp;skin=fallback\u0026amp;*\"\u003E\u003C/script\u003E");'
+ .
'mw.loader.load("http://127.0.0.1:8080/w/load.php?debug=false\u0026lang=en\u0026modules=test.foo\u0026only=scripts\u0026skin=fallback\u0026*");'
. "\n} );</script>"
),
array(
// Don't condition wrap raw modules (like the
startup module)
array( 'test.raw',
ResourceLoaderModule::TYPE_SCRIPTS ),
- '<script
src="http://127.0.0.1:8080/w/load.php?debug=false&lang=en&modules=test.raw&only=scripts&skin=fallback&*"></script>'
+ '<script async
src="http://127.0.0.1:8080/w/load.php?debug=false&lang=en&modules=test.raw&only=scripts&skin=fallback&*"></script>'
),
// Load module styles only
// This also tests the order the modules are put into
the url
@@ -188,10 +186,10 @@
array(
array( array( 'test.group.foo',
'test.group.bar' ), ResourceLoaderModule::TYPE_COMBINED ),
"<script>var RLQ = RLQ || []; RLQ.push(
function () {\n"
- . 'document.write("\u003Cscript
src=\"http://127.0.0.1:8080/w/load.php?debug=false\u0026amp;lang=en\u0026amp;modules=test.group.bar\u0026amp;skin=fallback\u0026amp;*\"\u003E\u003C/script\u003E");'
+ .
'mw.loader.load("http://127.0.0.1:8080/w/load.php?debug=false\u0026lang=en\u0026modules=test.group.bar\u0026skin=fallback\u0026*");'
. "\n} );</script>\n"
. "<script>var RLQ = RLQ || [];
RLQ.push( function () {\n"
- . 'document.write("\u003Cscript
src=\"http://127.0.0.1:8080/w/load.php?debug=false\u0026amp;lang=en\u0026amp;modules=test.group.foo\u0026amp;skin=fallback\u0026amp;*\"\u003E\u003C/script\u003E");'
+ .
'mw.loader.load("http://127.0.0.1:8080/w/load.php?debug=false\u0026lang=en\u0026modules=test.group.foo\u0026skin=fallback\u0026*");'
. "\n} );</script>"
),
);
--
To view, visit https://gerrit.wikimedia.org/r/227627
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Icba6d7a87b239bf127a221bc6bc432cfa71a4a72
Gerrit-PatchSet: 21
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Krinkle <[email protected]>
Gerrit-Reviewer: Edokter <[email protected]>
Gerrit-Reviewer: Jack Phoenix <[email protected]>
Gerrit-Reviewer: Jdlrobson <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: Legoktm <[email protected]>
Gerrit-Reviewer: Ori.livneh <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits