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

Reply via email to