Krinkle has uploaded a new change for review.
https://gerrit.wikimedia.org/r/135085
Change subject: [WIP] resourceloader: Implement "skip function" feature
......................................................................
[WIP] resourceloader: Implement "skip function" feature
Change-Id: I87a0ea888d791ad39f114380c42e2daeca470961
---
M includes/resourceloader/ResourceLoader.php
M includes/resourceloader/ResourceLoaderFileModule.php
M includes/resourceloader/ResourceLoaderModule.php
M includes/resourceloader/ResourceLoaderStartUpModule.php
M tests/phpunit/ResourceLoaderTestCase.php
M tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php
6 files changed, 135 insertions(+), 13 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/85/135085/1
diff --git a/includes/resourceloader/ResourceLoader.php
b/includes/resourceloader/ResourceLoader.php
index 680bd99..b5194d4 100644
--- a/includes/resourceloader/ResourceLoader.php
+++ b/includes/resourceloader/ResourceLoader.php
@@ -1074,15 +1074,15 @@
* Returns JS code which calls mw.loader.register with the given
* parameters. Has three calling conventions:
*
- * - ResourceLoader::makeLoaderRegisterScript( $name, $version,
$dependencies, $group, $source ):
+ * - ResourceLoader::makeLoaderRegisterScript( $name, $version,
$dependencies, $group, $source, $skip ):
* Register a single module.
*
* - ResourceLoader::makeLoaderRegisterScript( array( $name1, $name2
) ):
* Register modules with the given names.
*
* - ResourceLoader::makeLoaderRegisterScript( array(
- * array( $name1, $version1, $dependencies1, $group1, $source1 ),
- * array( $name2, $version2, $dependencies1, $group2, $source2 ),
+ * array( $name1, $version1, $dependencies1, $group1, $source1,
$skip1 ),
+ * array( $name2, $version2, $dependencies1, $group2, $source2,
$skip2 ),
* ...
* ) ):
* Registers modules with the given names and parameters.
@@ -1092,10 +1092,11 @@
* @param array $dependencies List of module names on which this module
depends
* @param string $group Group which the module is in
* @param string $source Source of the module, or 'local' if not foreign
+ * @param XmlJsCode $skip Skip function
* @return string
*/
public static function makeLoaderRegisterScript( $name, $version = null,
- $dependencies = null, $group = null, $source = null
+ $dependencies = null, $group = null, $source = null, $skip =
null
) {
if ( is_array( $name ) ) {
return Xml::encodeJsCall(
@@ -1107,7 +1108,7 @@
$version = (int)$version > 1 ? (int)$version : 1;
return Xml::encodeJsCall(
'mw.loader.register',
- array( $name, $version, $dependencies, $group,
$source ),
+ array( $name, $version, $dependencies, $group,
$source, $skip ),
ResourceLoader::inDebugMode()
);
}
diff --git a/includes/resourceloader/ResourceLoaderFileModule.php
b/includes/resourceloader/ResourceLoaderFileModule.php
index 382bdd9..f5adc54 100644
--- a/includes/resourceloader/ResourceLoaderFileModule.php
+++ b/includes/resourceloader/ResourceLoaderFileModule.php
@@ -107,6 +107,11 @@
protected $dependencies = array();
/**
+ * @var string File name containing the body of the skip function
+ */
+ protected $skipFunction = null;
+
+ /**
* @var array List of message keys used by this module
* @par Usage:
* @code
@@ -204,6 +209,10 @@
* 'group' => [group name string],
* // Position on the page to load this module at
* 'position' => ['bottom' (default) or 'top']
+ * // Function that, if it returns true, makes the loader skip
this module
+ * // The path is expected to contain a javascript block
(starting with "{"
+ * // and ending with "}")
+ * 'skipFunction' => [file path]
* )
* @endcode
*/
@@ -267,6 +276,7 @@
case 'position':
case 'localBasePath':
case 'remoteBasePath':
+ case 'skipFunction':
$this->{$member} = (string)$option;
break;
// Single booleans
@@ -411,6 +421,28 @@
}
/**
+ * Get the skip function.
+ *
+ * @return XmlJsCode|null
+ */
+ public function getSkipFunction() {
+ if ( !$this->skipFunction) {
+ return null;
+ }
+
+ global $wgResourceLoaderValidateStaticJS;
+ $localPath = $this->getLocalPath( $this->skipFunction );
+ if ( !file_exists( $localPath ) ) {
+ throw new MWException( __METHOD__ . ": skip function
file not found: \"$localPath\"" );
+ }
+ $contents = file_get_contents( $localPath );
+ if ( $wgResourceLoaderValidateStaticJS ) {
+ $contents = $this->validateScriptFile( $fileName,
$contents );
+ }
+ return new XmlJsCode( "function () $contents" );
+ }
+
+ /**
* @return bool
*/
public function isRaw() {
@@ -463,6 +495,9 @@
self::tryForKey( $this->skinScripts,
$context->getSkin(), 'default' ),
$this->loaderScripts
);
+ if ( $this->skipFunction ) {
+ $files[] = $this->skipFunction;
+ }
$files = array_map( array( $this, 'getLocalPath' ), $files );
// File deps need to be treated separately because they're
already prefixed
$files = array_merge( $files, $this->getFileDependencies(
$context->getSkin() ) );
@@ -511,6 +546,7 @@
'targets',
'group',
'position',
+ 'skipFunction',
'localBasePath',
'remoteBasePath',
'debugRaw',
diff --git a/includes/resourceloader/ResourceLoaderModule.php
b/includes/resourceloader/ResourceLoaderModule.php
index 9ddd184..bcaf791 100644
--- a/includes/resourceloader/ResourceLoaderModule.php
+++ b/includes/resourceloader/ResourceLoaderModule.php
@@ -294,6 +294,21 @@
}
/**
+ * Get the skip function.
+ *
+ * Modules that provide fallback functionality can provide a "skip
function".
+ * This function, when provided, will be passed along to the module
registry
+ * on the client. When the module is dependent on, this function is
executed
+ * first. If the function returns true, the module will instantly be
considered
+ * "ready" without requesting the associated module contents.
+ *
+ * @return XmlJsCode|null A javascript function returning a boolean
value or null
+ */
+ public function getSkipFunction() {
+ return null;
+ }
+
+ /**
* Get the files this module depends on indirectly for a given skin.
* Currently these are only image files referenced by the module's CSS.
*
diff --git a/includes/resourceloader/ResourceLoaderStartUpModule.php
b/includes/resourceloader/ResourceLoaderStartUpModule.php
index 63a444b..6731de3 100644
--- a/includes/resourceloader/ResourceLoaderStartUpModule.php
+++ b/includes/resourceloader/ResourceLoaderStartUpModule.php
@@ -228,6 +228,7 @@
'group' => $module->getGroup(),
'source' => $module->getSource(),
'loader' => $module->getLoaderScript(),
+ 'skip' => $module->getSkipFunction(),
);
}
@@ -255,17 +256,25 @@
if (
!count( $data['dependencies'] ) &&
$data['group'] === null &&
- $data['source'] === 'local'
+ $data['source'] === 'local' &&
+ $data['skip'] === null
) {
- // Modules without dependencies, a group or a
foreign source;
+ // Modules with no dependencies, group, foreign
source or skip function;
// call mw.loader.register(name, timestamp)
$registrations[] = array( $name,
$data['version'] );
- } elseif ( $data['group'] === null && $data['source']
=== 'local' ) {
- // Modules with dependencies but no group or
foreign source;
+ } elseif (
+ $data['group'] === null &&
+ $data['source'] === 'local' &&
+ $data['skip'] === null
+ ) {
+ // Modules with dependencies but no group,
foreign source or skip function;
// call mw.loader.register(name, timestamp,
dependencies)
$registrations[] = array( $name,
$data['version'], $data['dependencies'] );
- } elseif ( $data['source'] === 'local' ) {
- // Modules with a group but no foreign source;
+ } elseif (
+ $data['source'] === 'local' &&
+ $data['skip'] === null
+ ) {
+ // Modules with a group but no foreign source
or skip function;
// call mw.loader.register(name, timestamp,
dependencies, group)
$registrations[] = array(
$name,
@@ -273,8 +282,8 @@
$data['dependencies'],
$data['group']
);
- } else {
- // Modules with a foreign source;
+ } elseif ( $data['skip'] === null ) {
+ // Modules with a foreign source but no skip
function;
// call mw.loader.register(name, timestamp,
dependencies, group, source)
$registrations[] = array(
$name,
@@ -283,6 +292,17 @@
$data['group'],
$data['source']
);
+ } else {
+ // Modules with a skip function;
+ // call mw.loader.register(name, timestamp,
dependencies, group, source, skip)
+ $registrations[] = array(
+ $name,
+ $data['version'],
+ $data['dependencies'],
+ $data['group'],
+ $data['source'],
+ $data['skip']
+ );
}
}
diff --git a/tests/phpunit/ResourceLoaderTestCase.php
b/tests/phpunit/ResourceLoaderTestCase.php
index f8c4c6c..1ad69f4 100644
--- a/tests/phpunit/ResourceLoaderTestCase.php
+++ b/tests/phpunit/ResourceLoaderTestCase.php
@@ -42,6 +42,7 @@
protected $dependencies = array();
protected $group = null;
protected $source = 'local';
+ protected $skipFunction = null;
protected $targets = array( 'test' );
public function __construct( $options = array() ) {
@@ -61,6 +62,12 @@
public function getSource() {
return $this->source;
}
+
+ public function getSkipFunction() {
+ return $this->skipFunction ?
+ new XmlJsCode( $this->skipFunction ) :
+ null;
+ }
}
class ResourceLoaderFileModuleTestModule extends ResourceLoaderFileModule {
diff --git
a/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php
b/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php
index c4412de..a8eb628 100644
--- a/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php
+++ b/tests/phpunit/includes/resourceloader/ResourceLoaderStartupModuleTest.php
@@ -116,6 +116,49 @@
] );'
) ),
array( array(
+ 'msg' => 'Conditional dependency function',
+ 'modules' => array(
+ 'test.x.core' => new
ResourceLoaderTestModule(),
+ 'test.x.polyfill' => new
ResourceLoaderTestModule( array(
+ 'skipFunction' => 'function ()
{ return true; }'
+ ) ),
+ 'test.x.foo' => new
ResourceLoaderTestModule( array(
+ 'dependencies' => array(
+ 'test.x.core',
+ 'test.x.polyfil',
+ ),
+ ) ),
+ ),
+ 'out' => '
+mw.loader.addSource( {
+ "local": {
+ "loadScript": "/w/load.php",
+ "apiScript": "/w/api.php"
+ }
+} );mw.loader.register( [
+ [
+ "test.x.core",
+ "1388534400"
+ ],
+ [
+ "test.x.polyfill",
+ "1388534400",
+ [],
+ null,
+ "local",
+ function () { return true; }
+ ],
+ [
+ "test.x.foo",
+ "1388534400",
+ [
+ "test.x.core",
+ "test.x.polyfil"
+ ]
+ ]
+] );',
+ ) ),
+ array( array(
// This may seem like an edge case, but a plain
MediaWiki core install
// with a few extensions installed is likely
far more complex than this
// even, not to mention an install like
Wikipedia.
--
To view, visit https://gerrit.wikimedia.org/r/135085
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I87a0ea888d791ad39f114380c42e2daeca470961
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Krinkle <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits