2017-02-17 04:10:15 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace MediaWiki\Tidy;
|
|
|
|
|
|
2024-02-08 23:12:50 +00:00
|
|
|
use InvalidArgumentException;
|
2024-01-05 08:42:51 +00:00
|
|
|
use Wikimedia\RemexHtml\Serializer\SerializerNode;
|
|
|
|
|
use Wikimedia\RemexHtml\TreeBuilder\Element;
|
|
|
|
|
|
2017-02-17 04:10:15 +00:00
|
|
|
/**
|
|
|
|
|
* @internal
|
|
|
|
|
*/
|
|
|
|
|
class RemexMungerData {
|
|
|
|
|
/**
|
|
|
|
|
* The Element for the mw:p-wrap which is a child of the current node. If
|
|
|
|
|
* this is set, inline insertions into this node will be diverted so that
|
|
|
|
|
* they insert into the p-wrap.
|
|
|
|
|
*
|
2024-01-05 08:42:51 +00:00
|
|
|
* @var Element|null
|
2017-02-17 04:10:15 +00:00
|
|
|
*/
|
|
|
|
|
public $childPElement;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This tracks the mw:p-wrap node in the Serializer stack which is an
|
|
|
|
|
* ancestor of this node. If there is no mw:p-wrap ancestor, it is null.
|
|
|
|
|
*
|
2024-01-05 08:42:51 +00:00
|
|
|
* @var SerializerNode|null
|
2017-02-17 04:10:15 +00:00
|
|
|
*/
|
|
|
|
|
public $ancestorPNode;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The wrap base node is the body or blockquote node which is the parent
|
|
|
|
|
* of active p-wrappers. This is set if there is an ancestor p-wrapper,
|
|
|
|
|
* or if a p-wrapper was closed due to a block element being encountered
|
|
|
|
|
* inside it.
|
|
|
|
|
*
|
2024-01-05 08:42:51 +00:00
|
|
|
* @var SerializerNode|null
|
2017-02-17 04:10:15 +00:00
|
|
|
*/
|
|
|
|
|
public $wrapBaseNode;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stack splitting (essentially our idea of AFE reconstruction) can clone
|
|
|
|
|
* formatting elements which are split over multiple paragraphs.
|
|
|
|
|
* TreeBuilder is not aware of the cloning, and continues to insert into
|
|
|
|
|
* the original element. This is set to the newer clone if this node was
|
|
|
|
|
* cloned, i.e. if there is an active diversion of the insertion location.
|
|
|
|
|
*
|
2024-01-05 08:42:51 +00:00
|
|
|
* @var Element|null
|
2017-02-17 04:10:15 +00:00
|
|
|
*/
|
|
|
|
|
public $currentCloneElement;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Is the node a p-wrapper, with name mw:p-wrap?
|
|
|
|
|
*
|
|
|
|
|
* @var bool
|
|
|
|
|
*/
|
|
|
|
|
public $isPWrapper = false;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Is the node splittable, i.e. a formatting element or a node with a
|
|
|
|
|
* formatting element ancestor which is under an active or deactivated
|
|
|
|
|
* p-wrapper.
|
|
|
|
|
*
|
|
|
|
|
* @var bool
|
|
|
|
|
*/
|
|
|
|
|
public $isSplittable = false;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is true if the node is a body or blockquote, which activates
|
|
|
|
|
* p-wrapping of child nodes.
|
2024-09-14 08:12:18 +00:00
|
|
|
*
|
|
|
|
|
* @var bool
|
2017-02-17 04:10:15 +00:00
|
|
|
*/
|
|
|
|
|
public $needsPWrapping = false;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The number of child nodes, not counting whitespace-only text nodes or
|
|
|
|
|
* comments.
|
2024-09-14 08:12:18 +00:00
|
|
|
*
|
|
|
|
|
* @var int
|
2017-02-17 04:10:15 +00:00
|
|
|
*/
|
|
|
|
|
public $nonblankNodeCount = 0;
|
|
|
|
|
|
|
|
|
|
public function __set( $name, $value ) {
|
2021-09-04 01:42:33 +00:00
|
|
|
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
|
2024-02-08 23:12:50 +00:00
|
|
|
throw new InvalidArgumentException( "Cannot set property \"$name\"" );
|
2017-02-17 04:10:15 +00:00
|
|
|
}
|
Fix RemexCompatMunger infinite recursion
When TreeBuilder requests reparenting of all child nodes of a given
element, we do this by removing the existing child nodes, and then
inserting the proposed new parent under the old parent. However, when a
p-wrap diversion is in place, the insertion of the new parent is
diverted into the p-wrap, and the p-wrap then becomes a child of the new
parent, causing a reference loop, and ultimately infinite recursion in
Serializer.
Instead, divert the entire reparent request to the p-wrap, so that the
new parent is a child of the p-wrap. This makes sense since the new
parent is always a formatting element. The only caller of
reparentChildren(), apart from proxies, is AAA step 17, which reparents
children under the formatting element cloned from the AFE list.
Left in some debug code for next time.
Bug: T178632
Change-Id: Id77d21d99748e94c064ef24c43ee0033de627b8e
2017-11-17 11:15:59 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get a text representation of the current state of the serializer, for
|
|
|
|
|
* debugging.
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public function dump() {
|
2019-04-15 16:07:31 +00:00
|
|
|
$parts = [];
|
|
|
|
|
|
Fix RemexCompatMunger infinite recursion
When TreeBuilder requests reparenting of all child nodes of a given
element, we do this by removing the existing child nodes, and then
inserting the proposed new parent under the old parent. However, when a
p-wrap diversion is in place, the insertion of the new parent is
diverted into the p-wrap, and the p-wrap then becomes a child of the new
parent, causing a reference loop, and ultimately infinite recursion in
Serializer.
Instead, divert the entire reparent request to the p-wrap, so that the
new parent is a child of the p-wrap. This makes sense since the new
parent is always a formatting element. The only caller of
reparentChildren(), apart from proxies, is AAA step 17, which reparents
children under the formatting element cloned from the AFE list.
Left in some debug code for next time.
Bug: T178632
Change-Id: Id77d21d99748e94c064ef24c43ee0033de627b8e
2017-11-17 11:15:59 +00:00
|
|
|
if ( $this->childPElement ) {
|
|
|
|
|
$parts[] = 'childPElement=' . $this->childPElement->getDebugTag();
|
|
|
|
|
}
|
|
|
|
|
if ( $this->ancestorPNode ) {
|
|
|
|
|
$parts[] = "ancestorPNode=<{$this->ancestorPNode->name}>";
|
|
|
|
|
}
|
|
|
|
|
if ( $this->wrapBaseNode ) {
|
|
|
|
|
$parts[] = "wrapBaseNode=<{$this->wrapBaseNode->name}>";
|
|
|
|
|
}
|
|
|
|
|
if ( $this->currentCloneElement ) {
|
|
|
|
|
$parts[] = "currentCloneElement=" . $this->currentCloneElement->getDebugTag();
|
|
|
|
|
}
|
|
|
|
|
if ( $this->isPWrapper ) {
|
|
|
|
|
$parts[] = 'isPWrapper';
|
|
|
|
|
}
|
|
|
|
|
if ( $this->isSplittable ) {
|
|
|
|
|
$parts[] = 'isSplittable';
|
|
|
|
|
}
|
|
|
|
|
if ( $this->needsPWrapping ) {
|
|
|
|
|
$parts[] = 'needsPWrapping';
|
|
|
|
|
}
|
|
|
|
|
if ( $this->nonblankNodeCount ) {
|
|
|
|
|
$parts[] = "nonblankNodeCount={$this->nonblankNodeCount}";
|
|
|
|
|
}
|
|
|
|
|
$s = "RemexMungerData {\n";
|
|
|
|
|
foreach ( $parts as $part ) {
|
|
|
|
|
$s .= " $part\n";
|
|
|
|
|
}
|
|
|
|
|
$s .= "}\n";
|
|
|
|
|
return $s;
|
|
|
|
|
}
|
2017-02-17 04:10:15 +00:00
|
|
|
}
|