Bartosz Dziewoński has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/172591

Change subject: ResourceLoader: Stop passing around errors as /**/-comments
......................................................................

ResourceLoader: Stop passing around errors as /**/-comments

They are prone to being stripped by CSS and JS minification and can't
be used for non-CSS non-JS responses, like images.

We've already been keeping some state related to errors in the
$hasErrors member variable, let's go all the way and replace it with
$errors array, which tracks all errors accumulated during current
respond() call. When processing completes, all errors are added to the
response, if possible.

Change-Id: I6643f010174cb1f17780622e8a63db03cffe19e1
---
M includes/resourceloader/ResourceLoader.php
1 file changed, 55 insertions(+), 46 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/91/172591/1

diff --git a/includes/resourceloader/ResourceLoader.php 
b/includes/resourceloader/ResourceLoader.php
index 76acc9a..a38cd3a 100644
--- a/includes/resourceloader/ResourceLoader.php
+++ b/includes/resourceloader/ResourceLoader.php
@@ -63,8 +63,11 @@
         */
        protected $sources = array();
 
-       /** @var bool */
-       protected $hasErrors = false;
+       /**
+        * Errors accumulated during current respond() call.
+        * @var array
+        */
+       protected $errors = array();
 
        /**
         * Load information stored in the database about modules.
@@ -209,9 +212,7 @@
                } catch ( Exception $e ) {
                        MWExceptionHandler::logException( $e );
                        wfDebugLog( 'resourceloader', __METHOD__ . ": 
minification failed: $e" );
-                       $this->hasErrors = true;
-                       // Return exception as a comment
-                       $result = self::formatException( $e );
+                       $this->errors[] = self::formatExceptionNoComment( $e );
                }
 
                wfProfileOut( __METHOD__ );
@@ -579,7 +580,6 @@
                ob_start();
 
                wfProfileIn( __METHOD__ );
-               $errors = '';
 
                // Find out which modules are missing and instantiate the others
                $modules = array();
@@ -591,10 +591,7 @@
                                // This is a security issue, see bug 34907.
                                if ( $module->getGroup() === 'private' ) {
                                        wfDebugLog( 'resourceloader', 
__METHOD__ . ": request for private module '$name' denied" );
-                                       $this->hasErrors = true;
-                                       // Add exception to the output as a 
comment
-                                       $errors .= self::makeComment( "Cannot 
show private module \"$name\"" );
-
+                                       $this->errors[] = "Cannot show private 
module \"$name\"";
                                        continue;
                                }
                                $modules[$name] = $module;
@@ -609,9 +606,7 @@
                } catch ( Exception $e ) {
                        MWExceptionHandler::logException( $e );
                        wfDebugLog( 'resourceloader', __METHOD__ . ": 
preloading module info failed: $e" );
-                       $this->hasErrors = true;
-                       // Add exception to the output as a comment
-                       $errors .= self::formatException( $e );
+                       $this->errors[] = self::formatExceptionNoComment( $e );
                }
 
                wfProfileIn( __METHOD__ . '-getModifiedTime' );
@@ -629,9 +624,7 @@
                        } catch ( Exception $e ) {
                                MWExceptionHandler::logException( $e );
                                wfDebugLog( 'resourceloader', __METHOD__ . ": 
calculating maximum modified time failed: $e" );
-                               $this->hasErrors = true;
-                               // Add exception to the output as a comment
-                               $errors .= self::formatException( $e );
+                               $this->errors[] = 
self::formatExceptionNoComment( $e );
                        }
                }
 
@@ -649,23 +642,11 @@
                // Capture any PHP warnings from the output buffer and append 
them to the
                // error list if we're in debug mode.
                if ( $context->getDebug() && strlen( $warnings = 
ob_get_contents() ) ) {
-                       $errors .= self::makeComment( $warnings );
-                       $this->hasErrors = true;
-               }
-
-               if ( $this->hasErrors ) {
-                       if ( $context->getImageObj() ) {
-                               // Bail, we can't show both the error messages 
and the response when it's not CSS or JS.
-                               // sendResponseHeaders() will handle this 
sensibly.
-                               $response = $errors;
-                       } else {
-                               // Prepend comments indicating exceptions
-                               $response = $errors . $response;
-                       }
+                       $this->errors[] = $warnings;
                }
 
                // Save response to file cache unless there are errors
-               if ( isset( $fileCache ) && !$this->hasErrors && !count( 
$missing ) ) {
+               if ( isset( $fileCache ) && !$this->errors && !count( $missing 
) ) {
                        // Cache single modules and images...and other requests 
if there are enough hits
                        if ( ResourceFileCache::useFileCache( $context ) ) {
                                if ( $fileCache->isCacheWorthy() ) {
@@ -677,10 +658,28 @@
                }
 
                // Send content type and cache related headers
-               $this->sendResponseHeaders( $context, $mtime, $this->hasErrors 
);
+               $this->sendResponseHeaders( $context, $mtime, 
(bool)$this->errors );
 
                // Remove the output buffer and output the response
                ob_end_clean();
+
+               if ( $context->getImageObj() && $this->errors ) {
+                       // We can't show both the error messages and the 
response when it's an image.
+                       $errorText = '';
+                       foreach ( $this->errors as $error ) {
+                               $errorText .= $error . "\n";
+                       }
+                       $response = $errorText;
+               } elseif ( $this->errors ) {
+                       // Prepend comments indicating errors
+                       $errorText = '';
+                       foreach ( $this->errors as $error ) {
+                               $errorText .= self::makeComment( $error );
+                       }
+                       $response = $errorText . $response;
+               }
+
+               $this->errors = array();
                echo $response;
 
                wfProfileOut( __METHOD__ );
@@ -690,7 +689,7 @@
         * Send content type and last modified headers to the client.
         * @param ResourceLoaderContext $context
         * @param string $mtime TS_MW timestamp to use for last-modified
-        * @param bool $errors Whether there are commented-out errors in the 
response
+        * @param bool $errors Whether there are errors in the response
         * @return void
         */
        protected function sendResponseHeaders( ResourceLoaderContext $context, 
$mtime, $errors ) {
@@ -708,6 +707,7 @@
                        $smaxage = $rlMaxage['versioned']['server'];
                }
                if ( $context->getImageObj() ) {
+                       // Output different headers if we're outputting textual 
errors.
                        if ( $errors ) {
                                header( 'Content-Type: text/plain; 
charset=utf-8' );
                        } else {
@@ -837,15 +837,26 @@
         * Handle exception display.
         *
         * @param Exception $e Exception to be shown to the user
-        * @return string Sanitized text that can be returned to the user
+        * @return string Sanitized text in a CSS/JS comment that can be 
returned to the user
         */
        public static function formatException( $e ) {
+               return self::makeComment( $this->formatExceptionNoComment( $e ) 
);
+       }
+
+       /**
+        * Handle exception display.
+        *
+        * @since 1.25
+        * @param Exception $e Exception to be shown to the user
+        * @return string Sanitized text that can be returned to the user
+        */
+       protected static function formatExceptionNoComment( $e ) {
                global $wgShowExceptionDetails;
 
                if ( $wgShowExceptionDetails ) {
-                       return self::makeComment( $e->__toString() );
+                       return $e->__toString();
                } else {
-                       return self::makeComment( wfMessage( 'internalerror' 
)->text() );
+                       return wfMessage( 'internalerror' )->text();
                }
        }
 
@@ -861,7 +872,6 @@
                array $modules, array $missing = array()
        ) {
                $out = '';
-               $exceptions = '';
                $states = array();
 
                if ( !count( $modules ) && !count( $missing ) ) {
@@ -878,6 +888,10 @@
                                $context->getVariant(),
                                $context->getFormat()
                        );
+                       if ( $data === false ) {
+                               $data = '';
+                               $this->errors[] = 'Image generation failed';
+                       }
                        wfProfileOut( __METHOD__ );
                        return $data;
                }
@@ -892,9 +906,7 @@
                                        'resourceloader',
                                        __METHOD__ . ": pre-fetching blobs from 
MessageBlobStore failed: $e"
                                );
-                               $this->hasErrors = true;
-                               // Add exception to the output as a comment
-                               $exceptions .= self::formatException( $e );
+                               $this->errors[] = 
self::formatExceptionNoComment( $e );
                        }
                } else {
                        $blobs = array();
@@ -1018,9 +1030,7 @@
                        } catch ( Exception $e ) {
                                MWExceptionHandler::logException( $e );
                                wfDebugLog( 'resourceloader', __METHOD__ . ": 
generating module package failed: $e" );
-                               $this->hasErrors = true;
-                               // Add exception to the output as a comment
-                               $exceptions .= self::formatException( $e );
+                               $this->errors[] = 
self::formatExceptionNoComment( $e );
 
                                // Respond to client with error-state instead 
of module implementation
                                $states[$name] = 'error';
@@ -1046,9 +1056,8 @@
                        }
                } else {
                        if ( count( $states ) ) {
-                               $exceptions .= self::makeComment(
-                                       'Problematic modules: ' . 
FormatJson::encode( $states, ResourceLoader::inDebugMode() )
-                               );
+                               $this->errors[] = 'Problematic modules: ' .
+                                       FormatJson::encode( $states, 
ResourceLoader::inDebugMode() );
                        }
                }
 
@@ -1061,7 +1070,7 @@
                }
 
                wfProfileOut( __METHOD__ );
-               return $exceptions . $out;
+               return $out;
        }
 
        /* Static Methods */

-- 
To view, visit https://gerrit.wikimedia.org/r/172591
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6643f010174cb1f17780622e8a63db03cffe19e1
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Bartosz Dziewoński <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to