jenkins-bot has submitted this change and it was merged.
Change subject: Fix ModelRegistry bugs
......................................................................
Fix ModelRegistry bugs
* In matchTypeRegExps(), skip string types
** Didn't break because we currently have mixes of strings and regexes
* Combine types from rel, typeof and property rather than picking one
** Added test case in ve.dm.example that resembles actual Parsoid output
* If the element has extension-specific types, not only restrict type
matching to extension-specific types, but also require that *all* types
present on the element be matched
Change-Id: Iacf3851a0ca9081d2c813b42435484a47cec6230
---
M modules/ve/dm/ve.dm.ModelRegistry.js
M modules/ve/test/dm/ve.dm.Converter.test.js
M modules/ve/test/dm/ve.dm.ModelRegistry.test.js
M modules/ve/test/dm/ve.dm.example.js
4 files changed, 91 insertions(+), 14 deletions(-)
Approvals:
Esanders: Looks good to me, approved
jenkins-bot: Verified
diff --git a/modules/ve/dm/ve.dm.ModelRegistry.js
b/modules/ve/dm/ve.dm.ModelRegistry.js
index 9ab0e0b..1647fbd 100644
--- a/modules/ve/dm/ve.dm.ModelRegistry.js
+++ b/modules/ve/dm/ve.dm.ModelRegistry.js
@@ -174,9 +174,8 @@
*/
ve.dm.ModelRegistry.prototype.matchElement = function ( element ) {
var i, name, model, matches, winner, types, elementExtSpecificTypes,
matchTypes,
+ hasExtSpecificTypes,
tag = element.nodeName.toLowerCase(),
- typeAttr = element.getAttribute( 'typeof' ) ||
element.getAttribute( 'rel' ) ||
- element.getAttribute( 'property' ),
reg = this;
function byRegistrationOrderDesc( a, b ) {
@@ -189,6 +188,7 @@
types = reg.registry[models[i]].static.matchRdfaTypes;
for ( j = 0; j < types.length; j++ ) {
if (
+ types[j] instanceof RegExp &&
type.match( types[j] ) &&
(
reg.registry[models[i]].static.matchTagNames === null ||
@@ -202,18 +202,51 @@
return matches;
}
- function matchWithFunc( types, tag ) {
+ function matchesAllTypes( types, name ) {
+ var i, j, haveMatch, matchTypes =
reg.registry[name].static.matchRdfaTypes;
+ for ( i = 0; i < types.length; i++ ) {
+ haveMatch = false;
+ for ( j = 0; j < matchTypes.length; j++ ) {
+ if ( matchTypes[j] instanceof RegExp ) {
+ if ( types[i].match( matchTypes[j] ) ) {
+ haveMatch = true;
+ break;
+ }
+ } else {
+ if ( types[i] === matchTypes[j] ) {
+ haveMatch = true;
+ break;
+ }
+ }
+ }
+ if ( !haveMatch ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function matchWithFunc( types, tag, mustMatchAll ) {
var i, queue = [], queue2 = [];
for ( i = 0; i < types.length; i++ ) {
// Queue string matches and regexp matches separately
queue = queue.concat( ve.getProp(
reg.modelsByTypeAndTag, 1, types[i], tag ) || [] );
queue2 = queue2.concat( matchTypeRegExps( types[i],
tag, true ) );
}
+ if ( mustMatchAll ) {
+ // Filter out matches that don't match all types
+ queue = ve.filterArray( queue, function ( name ) {
return matchesAllTypes( types, name ); } );
+ queue2 = ve.filterArray( queue2, function ( name ) {
return matchesAllTypes( types, name ); } );
+ }
// Try string matches first, then regexp matches
queue.sort( byRegistrationOrderDesc );
queue2.sort( byRegistrationOrderDesc );
queue = queue.concat( queue2 );
for ( i = 0; i < queue.length; i++ ) {
+ if ( mustMatchAll && !matchesAllTypes( types, queue[i]
) ) {
+ // Skip matches that don't match all types if
that's required
+ continue;
+ }
if ( reg.registry[queue[i]].static.matchFunction(
element ) ) {
return queue[i];
}
@@ -221,16 +254,25 @@
return null;
}
- function matchWithoutFunc( types, tag ) {
+ function matchWithoutFunc( types, tag, mustMatchAll ) {
var i, queue = [], queue2 = [], winningName = null;
for ( i = 0; i < types.length; i++ ) {
// Queue string and regexp matches separately
queue = queue.concat( ve.getProp(
reg.modelsByTypeAndTag, 0, types[i], tag ) || [] );
queue2 = queue2.concat( matchTypeRegExps( types[i],
tag, false ) );
}
+ if ( mustMatchAll ) {
+ // Filter out matches that don't match all types
+ queue = ve.filterArray( queue, function ( name ) {
return matchesAllTypes( types, name ); } );
+ queue2 = ve.filterArray( queue2, function ( name ) {
return matchesAllTypes( types, name ); } );
+ }
// Only try regexp matches if there are no string matches
queue = queue.length > 0 ? queue : queue2;
for ( i = 0; i < queue.length; i++ ) {
+ if ( mustMatchAll && !matchesAllTypes( types, queue[i]
) ) {
+ // Skip matches that don't match all types if
that's required
+ continue;
+ }
if (
winningName === null ||
reg.registrationOrder[winningName] <
reg.registrationOrder[queue[i]]
@@ -241,14 +283,24 @@
return winningName;
}
- types = typeAttr ? typeAttr.split( ' ' ) : [];
+ types = [];
+ if ( element.getAttribute( 'rel' ) ) {
+ types = types.concat( element.getAttribute( 'rel' ).split( ' '
) );
+ }
+ if ( element.getAttribute( 'typeof' ) ) {
+ types = types.concat( element.getAttribute( 'typeof' ).split( '
' ) );
+ }
+ if ( element.getAttribute( 'property' ) ) {
+ types = types.concat( element.getAttribute( 'property' ).split(
' ' ) );
+ }
elementExtSpecificTypes = ve.filterArray( types, ve.bind(
this.isExtensionSpecificType, this ) );
+ hasExtSpecificTypes = elementExtSpecificTypes.length !== 0;
// If the element has extension-specific types, only use those for
matching and ignore its
// other types. If it has no extension-specific types, use all of its
types.
- matchTypes = elementExtSpecificTypes.length === 0 ? types :
elementExtSpecificTypes;
+ matchTypes = hasExtSpecificTypes ? elementExtSpecificTypes : types;
if ( types.length ) {
// func+tag+type match
- winner = matchWithFunc( matchTypes, tag );
+ winner = matchWithFunc( matchTypes, tag, hasExtSpecificTypes );
if ( winner !== null ) {
return winner;
}
@@ -256,14 +308,14 @@
// func+type match
// Only look at rules with no tag specified; if a rule does
specify a tag, we've
// either already processed it above, or the tag doesn't match
- winner = matchWithFunc( matchTypes, '' );
+ winner = matchWithFunc( matchTypes, '', hasExtSpecificTypes );
if ( winner !== null ) {
return winner;
}
}
// Do not check for type-less matches if the element has
extension-specific types
- if ( elementExtSpecificTypes.length === 0 ) {
+ if ( !hasExtSpecificTypes ) {
// func+tag match
matches = ve.getProp( this.modelsByTag, 1, tag ) || [];
// No need to sort because individual arrays in modelsByTag are
already sorted
@@ -294,7 +346,7 @@
}
// tag+type
- winner = matchWithoutFunc( matchTypes, tag );
+ winner = matchWithoutFunc( matchTypes, tag, hasExtSpecificTypes );
if ( winner !== null ) {
return winner;
}
@@ -302,7 +354,7 @@
// type only
// Only look at rules with no tag specified; if a rule does specify a
tag, we've
// either already processed it above, or the tag doesn't match
- winner = matchWithoutFunc( matchTypes, '' );
+ winner = matchWithoutFunc( matchTypes, '', hasExtSpecificTypes );
if ( winner !== null ) {
return winner;
}
diff --git a/modules/ve/test/dm/ve.dm.Converter.test.js
b/modules/ve/test/dm/ve.dm.Converter.test.js
index 66f968e..92d16fd 100644
--- a/modules/ve/test/dm/ve.dm.Converter.test.js
+++ b/modules/ve/test/dm/ve.dm.Converter.test.js
@@ -38,7 +38,7 @@
}
} );
-QUnit.test( 'getDataFromDom', 50, function ( assert ) {
+QUnit.test( 'getDataFromDom', 51, function ( assert ) {
var msg,
store = new ve.dm.IndexValueStore(),
cases = ve.copyObject( ve.dm.example.domToDataCases );
@@ -60,7 +60,7 @@
}
} );
-QUnit.test( 'getDomFromData', 54, function ( assert ) {
+QUnit.test( 'getDomFromData', 55, function ( assert ) {
var msg,
store = new ve.dm.IndexValueStore(),
cases = ve.copyObject( ve.dm.example.domToDataCases );
diff --git a/modules/ve/test/dm/ve.dm.ModelRegistry.test.js
b/modules/ve/test/dm/ve.dm.ModelRegistry.test.js
index 949fe2a..f1cd6d8 100644
--- a/modules/ve/test/dm/ve.dm.ModelRegistry.test.js
+++ b/modules/ve/test/dm/ve.dm.ModelRegistry.test.js
@@ -101,7 +101,7 @@
/* Tests */
-QUnit.test( 'matchElement', 20, function ( assert ) {
+QUnit.test( 'matchElement', 23, function ( assert ) {
var registry = new ve.dm.ModelRegistry(), element;
element = document.createElement( 'a' );
assert.deepEqual( registry.matchElement( element ), null,
'matchElement() returns null if registry empty' );
@@ -163,4 +163,11 @@
assert.deepEqual( registry.matchElement( element ), 'stub-regexp',
'RegExp type match for extension-specific type' );
element.setAttribute( 'rel', 'mw:abbr' );
assert.deepEqual( registry.matchElement( element ), 'stub-abbr',
'String match overrides RegExp match for extension-specific type' );
+ element.setAttribute( 'rel', 'mw:abbr mw:foo' );
+ assert.deepEqual( registry.matchElement( element ), 'stub-regexp',
'Additional extension-specific type (mw:foo) breaks string match, throws back
to regexp match' );
+ element.setAttribute( 'rel', 'mw:abbr foo' );
+ assert.deepEqual( registry.matchElement( element ), null, 'Additional
extension-specific type (foo) breaks match' );
+ element.setAttribute( 'rel', 'mw:abbr' );
+ element.setAttribute( 'typeof', 'foo' );
+ assert.deepEqual( registry.matchElement( element ), null, 'Types split
over two attributes still breaks match' );
} );
diff --git a/modules/ve/test/dm/ve.dm.example.js
b/modules/ve/test/dm/ve.dm.example.js
index 3395486..74ed18b 100644
--- a/modules/ve/test/dm/ve.dm.example.js
+++ b/modules/ve/test/dm/ve.dm.example.js
@@ -1815,6 +1815,24 @@
'<meta typeof="mw:Placeholder" data-parsoid="foobar"
/></body>',
'data': ve.dm.example.withMeta
},
+ 'RDFa types spread across two attributes': {
+ 'html': '<body><link rel="mw:WikiLink/Category"
href="./Category:Foo" about="#mwt1" typeof="mw:Object/Template"></body>',
+ 'data': [
+ {
+ 'type': 'alienMeta',
+ 'attributes': {
+ 'style': 'link',
+ 'key': 'mw:WikiLink/Category',
+ 'value': './Category:Foo',
+ 'html/0/rel': 'mw:WikiLink/Category',
+ 'html/0/href': './Category:Foo',
+ 'html/0/about': '#mwt1',
+ 'html/0/typeof': 'mw:Object/Template'
+ }
+ },
+ { 'type': '/alienMeta' },
+ ]
+ },
'change markers': {
'html': null,
'data': [
--
To view, visit https://gerrit.wikimedia.org/r/58259
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Iacf3851a0ca9081d2c813b42435484a47cec6230
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Catrope <[email protected]>
Gerrit-Reviewer: Esanders <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits