Upstream EasyDeflate library from VisualEditor
The library is used to compress large documents before sending to the server, as HTTP doesn't support upload compression. (See T68914 for context.) N.B. Many of the polyfills in the original are omitted as MediaWiki doesn't support browsers which require them. Co-Authored-by: Kunal Mehta <legoktm@member.fsf.org> Change-Id: I12a5879188f46f17dc4c33bdad52317d218d4172
This commit is contained in:
parent
e03b96c9a4
commit
230e90b2af
10 changed files with 4668 additions and 2 deletions
|
|
@ -428,6 +428,7 @@ $wgAutoloadLocalClasses = [
|
|||
'DumpRev' => __DIR__ . '/maintenance/storage/dumpRev.php',
|
||||
'DumpStringOutput' => __DIR__ . '/includes/export/DumpStringOutput.php',
|
||||
'DuplicateJob' => __DIR__ . '/includes/jobqueue/jobs/DuplicateJob.php',
|
||||
'EasyDeflate' => __DIR__ . '/includes/libs/EasyDeflate.php',
|
||||
'EditAction' => __DIR__ . '/includes/actions/EditAction.php',
|
||||
'EditCLI' => __DIR__ . '/maintenance/edit.php',
|
||||
'EditPage' => __DIR__ . '/includes/EditPage.php',
|
||||
|
|
|
|||
72
includes/libs/EasyDeflate.php
Normal file
72
includes/libs/EasyDeflate.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Server-side helper for the easy-deflate library
|
||||
*
|
||||
* @since 1.32
|
||||
*/
|
||||
class EasyDeflate {
|
||||
|
||||
/**
|
||||
* Whether the content is deflated
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isDeflated( $data ) {
|
||||
return substr( $data, 0, 11 ) === 'rawdeflate,';
|
||||
}
|
||||
|
||||
/**
|
||||
* For content that has been compressed with deflate in the client,
|
||||
* try to uncompress it with inflate.
|
||||
*
|
||||
* If data is not prefixed with 'rawdeflate,' it will be returned unmodified.
|
||||
*
|
||||
* Data can be compressed in the client using the 'easy-deflate.deflate'
|
||||
* module:
|
||||
*
|
||||
* @code
|
||||
* mw.loader.using( 'easy-deflate.deflate' ).then( function () {
|
||||
* var deflated = EasyDeflate.deflate( myContent );
|
||||
* } );
|
||||
* @endcode
|
||||
*
|
||||
* @param string $data Deflated data
|
||||
* @return StatusValue Inflated data will be set as the value
|
||||
* @throws InvalidArgumentException If the data wasn't deflated
|
||||
*/
|
||||
public static function inflate( $data ) {
|
||||
if ( !self::isDeflated( $data ) ) {
|
||||
throw new InvalidArgumentException( 'Data does not begin with deflated prefix' );
|
||||
}
|
||||
$deflated = base64_decode( substr( $data, 11 ), true );
|
||||
if ( $deflated === false ) {
|
||||
return StatusValue::newFatal( 'easydeflate-invaliddeflate' );
|
||||
}
|
||||
Wikimedia\suppressWarnings();
|
||||
$inflated = gzinflate( $deflated );
|
||||
Wikimedia\restoreWarnings();
|
||||
if ( $inflated === false ) {
|
||||
return StatusValue::newFatal( 'easydeflate-invaliddeflate' );
|
||||
}
|
||||
return StatusValue::newGood( $inflated );
|
||||
}
|
||||
}
|
||||
|
|
@ -4481,5 +4481,6 @@
|
|||
"passwordpolicies-policy-passwordcannotmatchusername": "Password cannot be the same as username",
|
||||
"passwordpolicies-policy-passwordcannotmatchblacklist": "Password cannot match specifically blacklisted passwords",
|
||||
"passwordpolicies-policy-maximalpasswordlength": "Password must be less than $1 {{PLURAL:$1|character|characters}} long",
|
||||
"passwordpolicies-policy-passwordcannotbepopular": "Password cannot be {{PLURAL:$1|the popular password|in the list of $1 popular passwords}}"
|
||||
"passwordpolicies-policy-passwordcannotbepopular": "Password cannot be {{PLURAL:$1|the popular password|in the list of $1 popular passwords}}",
|
||||
"easydeflate-invaliddeflate": "Content provided is not properly deflated"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4680,5 +4680,6 @@
|
|||
"passwordpolicies-policy-passwordcannotmatchusername": "Password policy that enforces that the password of the account cannot be the same as the username",
|
||||
"passwordpolicies-policy-passwordcannotmatchblacklist": "Password policy that enforces that passwords are not on a list of blacklisted passwords (often previously used during MediaWiki automated testing)",
|
||||
"passwordpolicies-policy-maximalpasswordlength": "Password policy that enforces a maximum number of characters a password must be. $1 - maximum number of characters that a password can be",
|
||||
"passwordpolicies-policy-passwordcannotbepopular": "Password policy that enforces that a password is not in a list of $1 number of \"popular\" passwords. $1 - number of popular passwords the password will be checked against"
|
||||
"passwordpolicies-policy-passwordcannotbepopular": "Password policy that enforces that a password is not in a list of $1 number of \"popular\" passwords. $1 - number of popular passwords the password will be checked against",
|
||||
"easydeflate-invaliddeflate": "Error message if the content passed to easydeflate was not deflated (compressed) properly"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2792,6 +2792,25 @@ return [
|
|||
'raw' => true,
|
||||
],
|
||||
|
||||
/* EasyDeflate */
|
||||
|
||||
'easy-deflate.core' => [
|
||||
'scripts' => [ 'resources/lib/easy-deflate/easydeflate.js' ],
|
||||
'targets' => [ 'desktop', 'mobile' ],
|
||||
],
|
||||
|
||||
'easy-deflate.deflate' => [
|
||||
'scripts' => [ 'resources/lib/easy-deflate/deflate.js' ],
|
||||
'dependencies' => [ 'easy-deflate.core' ],
|
||||
'targets' => [ 'desktop', 'mobile' ],
|
||||
],
|
||||
|
||||
'easy-deflate.inflate' => [
|
||||
'scripts' => [ 'resources/lib/easy-deflate/inflate.js' ],
|
||||
'dependencies' => [ 'easy-deflate.core' ],
|
||||
'targets' => [ 'desktop', 'mobile' ],
|
||||
],
|
||||
|
||||
/* OOjs */
|
||||
'oojs' => [
|
||||
'scripts' => [
|
||||
|
|
|
|||
38
resources/lib/easy-deflate/README.md
Normal file
38
resources/lib/easy-deflate/README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
Modified version of Easy-Deflate https://github.com/Jacob-Christian-Munch-Andersen/Easy-Deflate
|
||||
|
||||
This version: https://github.com/edg2s/Easy-Deflate
|
||||
|
||||
* Added semi-colons to easydeflate.js so it can be minified
|
||||
* Namespaced functions inside global EasyDeflate object
|
||||
* Base64 lib replaced with one with detailed license info
|
||||
|
||||
Modifications by Ed Sanders, Public Domain.
|
||||
|
||||
Easy-Deflate
|
||||
============
|
||||
|
||||
Library for compressing and decompressing strings in JavaScript, feature full Unicode support and is compatible with most browsers.
|
||||
|
||||
Use:
|
||||
====
|
||||
Copy the script inclusion from demo.html.<br>
|
||||
Call EasyDeflate.deflate(foo) in order to compress a string.<br>
|
||||
Call EasyDeflate.inflate(bar) in order to decompress a string compressed in this manner.<br>
|
||||
Both functions return a string, or null in case of illegal input.
|
||||
|
||||
The compression works by first UTF-8 encoding the input, then compressing it to a raw deflate stream. The stream is then base64 encoded, and finally the identifier "rawdeflate," is prepended.
|
||||
|
||||
Credits:
|
||||
========
|
||||
Gildas Lormeau made the JavaScript conversion of a Deflate utility: https://github.com/gildas-lormeau/zip.js<br>
|
||||
Jacob Christian Munch-Andersen made this package in order to make simple use easier and compatible with older browsers.
|
||||
|
||||
The following shims are included:<br>
|
||||
es5-shim by Kristopher Michael Kowal https://github.com/kriskowal/es5-shim<br>
|
||||
JSON 3 by Kit Cambridge http://bestiejs.github.com/json3/<br>
|
||||
Typed arrays light shim by Jacob Christian Munch-Andersen https://github.com/Jacob-Christian-Munch-Andersen/Typed-arrays-light-shim<br>
|
||||
<s>base64 by Yaffle https://gist.github.com/1284012</s>
|
||||
|
||||
License:
|
||||
========
|
||||
Main packages come with a BSD licence, the shims, except for base64 that include no license text, each has a permissive license.
|
||||
2088
resources/lib/easy-deflate/deflate.js
Normal file
2088
resources/lib/easy-deflate/deflate.js
Normal file
File diff suppressed because it is too large
Load diff
219
resources/lib/easy-deflate/easydeflate.js
Normal file
219
resources/lib/easy-deflate/easydeflate.js
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/**
|
||||
Copyright (c) 2013, Specialisterne.
|
||||
http://specialisterne.com/dk/
|
||||
All rights reserved.
|
||||
Authors:
|
||||
Jacob Christian Munch-Andersen
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**/
|
||||
// For information and latest version see: https://github.com/Jacob-Christian-Munch-Andersen/Easy-Deflate
|
||||
(function(){
|
||||
|
||||
var zip={};
|
||||
function UTF8encode(str){
|
||||
var out=[];
|
||||
var a;
|
||||
var c,c2;
|
||||
for(a=0;a<str.length;a++){
|
||||
c=str.charCodeAt(a);
|
||||
if(c<128){
|
||||
out.push(c);
|
||||
}
|
||||
else if(c<2048){
|
||||
out.push((c >> 6)+192);
|
||||
out.push((c & 63)+128);
|
||||
}
|
||||
else if(c<65536){
|
||||
if(c>=0xD800 && c<0xDC00){
|
||||
a++;
|
||||
if(a>=str.length){
|
||||
return null;
|
||||
}
|
||||
c2=str.charCodeAt(a);
|
||||
if(c2>=0xDC00 && c2<0xE000){
|
||||
c=65536+(c-0xD800)*1024+c2-0xDC00;
|
||||
out.push((c >> 18)+240);
|
||||
out.push(((c >> 12) & 63)+128);
|
||||
out.push(((c >> 6) & 63)+128);
|
||||
out.push((c & 63)+128);
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if(c>=0xDC00 && c<0xE000){
|
||||
return null;
|
||||
}
|
||||
else{
|
||||
out.push((c >> 12)+224);
|
||||
out.push(((c >> 6) & 63)+128);
|
||||
out.push((c & 63)+128);
|
||||
}
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new Uint8Array(out);
|
||||
}
|
||||
function UTF8decodeA(arrarr){
|
||||
var result="";
|
||||
var intermediate;
|
||||
var minvalue;
|
||||
var missing=0;
|
||||
var a,b;
|
||||
var arr;
|
||||
var c;
|
||||
var lower,upper;
|
||||
for(a=0;a<arrarr.length;a++){
|
||||
arr=arrarr[a];
|
||||
for(b=0;b<arr.length;b++){
|
||||
c=arr[b];
|
||||
if(missing){
|
||||
if(c>127 && c<192){
|
||||
intermediate=intermediate*64+c-128;
|
||||
missing--;
|
||||
if(!missing){
|
||||
if(intermediate>=minvalue){
|
||||
if(intermediate>=65536){
|
||||
if(intermediate>0x10FFFF){
|
||||
return null;
|
||||
}
|
||||
upper=(intermediate-65536)>>10;
|
||||
lower=intermediate%1024;
|
||||
result+=String.fromCharCode(upper+0xD800,lower+0xDC00);
|
||||
}
|
||||
else{
|
||||
result+=String.fromCharCode(intermediate);
|
||||
}
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if(c<128){
|
||||
result+=String.fromCharCode(c);
|
||||
}
|
||||
else if(c>191 && c<248){
|
||||
if(c<224){
|
||||
intermediate=c-192;
|
||||
minvalue=128;
|
||||
missing=1;
|
||||
}
|
||||
else if(c<240){
|
||||
intermediate=c-224;
|
||||
minvalue=2048;
|
||||
missing=2;
|
||||
}
|
||||
else{
|
||||
intermediate=c-240;
|
||||
minvalue=65536;
|
||||
missing=3;
|
||||
}
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(missing){
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function deflate(str){
|
||||
var a,c;
|
||||
var readlen=50000;
|
||||
var resulta=[];
|
||||
var results="";
|
||||
var b,d;
|
||||
var zipper=new zip.Deflater(9);
|
||||
for(a=0;a<str.length;a+=readlen){
|
||||
d=UTF8encode(str.substr(a,readlen));
|
||||
if(d===null){ //This error may be due to a 4 byte charachter being split, retry with a string that is 1 longer to fix it.
|
||||
d=UTF8encode(str.substr(a,readlen+1));
|
||||
a+=1;
|
||||
if(d===null){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
b=zipper.append(d);
|
||||
if(b.length!==0){
|
||||
resulta.push(b);
|
||||
}
|
||||
}
|
||||
b=zipper.flush();
|
||||
if(b.length!==0){
|
||||
resulta.push(b);
|
||||
}
|
||||
for(a=0;a<resulta.length;a++){
|
||||
for(c=0;c<resulta[a].length;c++){
|
||||
results+=String.fromCharCode(resulta[a][c]);
|
||||
}
|
||||
}
|
||||
return "rawdeflate,"+btoa(results);
|
||||
}
|
||||
function inflate(dfl){
|
||||
var unzipper=new zip.Inflater();
|
||||
var resulta=[];
|
||||
var dfls;
|
||||
var a,c;
|
||||
var b,d;
|
||||
if(dfl.slice(0,11)!="rawdeflate,"){
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
dfls=atob(dfl.slice(11));
|
||||
}
|
||||
catch(e){
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
for(a=0;a<dfls.length;a+=50000){
|
||||
b=new Uint8Array(Math.min(50000,dfls.length-a));
|
||||
for(c=0;c<b.length;c++){
|
||||
b[c]=dfls.charCodeAt(c+a);
|
||||
}
|
||||
d=unzipper.append(b);
|
||||
if(d.length){
|
||||
resulta.push(d);
|
||||
}
|
||||
}
|
||||
return UTF8decodeA(resulta);
|
||||
}
|
||||
catch(e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
window.EasyDeflate = {
|
||||
'zip': zip,
|
||||
'inflate': inflate,
|
||||
'deflate': deflate
|
||||
};
|
||||
|
||||
})();
|
||||
2163
resources/lib/easy-deflate/inflate.js
Normal file
2163
resources/lib/easy-deflate/inflate.js
Normal file
File diff suppressed because it is too large
Load diff
64
tests/phpunit/includes/libs/EasyDeflateTest.php
Normal file
64
tests/phpunit/includes/libs/EasyDeflateTest.php
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (C) 2018 Kunal Mehta <legoktm@member.fsf.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @covers EasyDeflate
|
||||
*/
|
||||
class EasyDeflateTest extends PHPUnit\Framework\TestCase {
|
||||
|
||||
public function provideIsDeflated() {
|
||||
return [
|
||||
[ 'rawdeflate,S8vPT0osAgA=', true ],
|
||||
[ 'abcdefghijklmnopqrstuvwxyz', false ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIsDeflated
|
||||
*/
|
||||
public function testIsDeflated( $data, $expected ) {
|
||||
$actual = EasyDeflate::isDeflated( $data );
|
||||
$this->assertSame( $expected, $actual );
|
||||
}
|
||||
|
||||
public function provideInflate() {
|
||||
return [
|
||||
[ 'rawdeflate,S8vPT0osAgA=', true, 'foobar' ],
|
||||
// Fails base64_decode
|
||||
[ 'rawdeflate,🌻', false, 'easydeflate-invaliddeflate' ],
|
||||
// Fails gzinflate
|
||||
[ 'rawdeflate,S8vPT0dfdAgB=', false, 'easydeflate-invaliddeflate' ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInflate
|
||||
*/
|
||||
public function testInflate( $data, $ok, $value ) {
|
||||
$actual = EasyDeflate::inflate( $data );
|
||||
if ( $ok ) {
|
||||
$this->assertTrue( $actual->isOK() );
|
||||
$this->assertSame( $value, $actual->getValue() );
|
||||
} else {
|
||||
$this->assertFalse( $actual->isOK() );
|
||||
$this->assertTrue( $actual->hasMessage( $value ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue