Arlolra has uploaded a new change for review. https://gerrit.wikimedia.org/r/251194
Change subject: WIP: Refactor WTS to be async ...................................................................... WIP: Refactor WTS to be async Change-Id: Id68d24007229dcfa0bf6fb16c6ed3ecdaad0c2e5 --- M lib/html2wt/SelectiveSerializer.js M lib/html2wt/SerializerState.js M lib/html2wt/WTSUtils.js M lib/html2wt/WikitextSerializer.js M lib/utils/DOMUtils.js 5 files changed, 283 insertions(+), 312 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/services/parsoid refs/changes/94/251194/1 diff --git a/lib/html2wt/SelectiveSerializer.js b/lib/html2wt/SelectiveSerializer.js index fb0fc8a..cfca657 100644 --- a/lib/html2wt/SelectiveSerializer.js +++ b/lib/html2wt/SelectiveSerializer.js @@ -41,10 +41,9 @@ * Selectively serialize an HTML DOM document synchronously. * WARNING: You probably want to use DU.serializeDOM instead. */ -SSP.serializeDOMSync = function(body) { +SSP.serializeDOM = Promise.method(function(body) { console.assert(DU.isBody(body), 'Expected a body node.'); - var out; var startTimers = new Map(); if ((!this.env.page.dom && !this.env.page.domdiff) || this.env.page.src === null) { @@ -53,12 +52,12 @@ } // If there's no old source, fall back to non-selective serialization. - out = this.wts.serializeDOMSync(body, false); - - if (this.timer) { - this.timer.timing('html2wt.full.serialize', '', - (Date.now() - startTimers.get('html2wt.full.serialize'))); - } + return this.wts.serializeDOM(body, false).tap(function() { + if (this.timer) { + this.timer.timing('html2wt.full.serialize', '', + (Date.now() - startTimers.get('html2wt.full.serialize'))); + } + }.bind(this)); } else { if (this.timer) { startTimers.set('html2wt.selser.serialize', Date.now()); @@ -82,9 +81,10 @@ } } + var p; if (diff.isEmpty) { // Nothing was modified, just re-use the original source - out = this.env.page.src; + p = Promise.resolve(this.env.page.src); } else { body = diff.dom; this.env.page.editedDoc = body.ownerDocument; @@ -96,16 +96,16 @@ } // Call the WikitextSerializer to do our bidding - out = this.wts.serializeDOMSync(body, true); + p = this.wts.serializeDOM(body, true); } - - if (this.timer) { - this.timer.timing('html2wt.selser.serialize', '', - (Date.now() - startTimers.get('html2wt.selser.serialize'))); - } + return p.tap(function() { + if (this.timer) { + this.timer.timing('html2wt.selser.serialize', '', + (Date.now() - startTimers.get('html2wt.selser.serialize'))); + } + }.bind(this)); } - return out; -}; +}); if (typeof module === 'object') { diff --git a/lib/html2wt/SerializerState.js b/lib/html2wt/SerializerState.js index 2f5a1d3..ce81040 100644 --- a/lib/html2wt/SerializerState.js +++ b/lib/html2wt/SerializerState.js @@ -177,42 +177,27 @@ * Serialize the children of a DOM node, sharing the global serializer state. * Typically called by a DOM-based handler to continue handling its children. */ -SSP.serializeChildren = function(node, wtEscaper) { - try { - // TODO gwicke: use nested WikitextSerializer instead? +SSP.serializeChildren = Promise.method(function(node, wtEscaper) { + // SSS FIXME: Unsure if this is the right thing always + if (wtEscaper) { this.wteHandlerStack.push(wtEscaper); } - // SSS FIXME: Unsure if this is the right thing always - if (wtEscaper) { - this.wteHandlerStack.push(wtEscaper); - } + var serializeChildChain = Promise.method(function(child) { + if (child === null) { return; } + return this.serializer._serializeNode(child).then(function(next) { + if (next === node) { return; } // Serialized all children + if (next === child) { next = next.nextSibling; } // Advance + return serializeChildChain(next); + }); + }).bind(this); - var child = node.firstChild; - var nextChild = null; - - while (child) { - nextChild = this.serializer._serializeNode(child); - if (nextChild === node) { - // serialized all children - break; - } else if (nextChild === child) { - // advance the child - child = child.nextSibling; - } else { - child = nextChild; - } - } + return serializeChildChain(node.firstChild).then(function() { + if (wtEscaper) { this.wteHandlerStack.pop(); } // If we serialized children explicitly, // we were obviously processing a modified node. this.currNodeUnmodified = false; - - if (wtEscaper) { - this.wteHandlerStack.pop(); - } - } catch (e) { - this.env.log("fatal", e); - } -}; + }.bind(this)); +}); /** */ diff --git a/lib/html2wt/WTSUtils.js b/lib/html2wt/WTSUtils.js index 8e0f23a..c4f6e4e 100644 --- a/lib/html2wt/WTSUtils.js +++ b/lib/html2wt/WTSUtils.js @@ -50,6 +50,21 @@ }, }; +WTSUtils.traceNodeName = function(node) { + switch (node.nodeType) { + case node.ELEMENT_NODE: + return DU.isMarkerMeta(node, "mw:DiffMarker") ? + "DIFF_MARK" : "NODE: " + node.nodeName; + case node.TEXT_NODE: + return "TEXT: " + JSON.stringify(node.nodeValue); + case node.COMMENT_NODE: + return "CMT : " + JSON.stringify(WTSUtils.commentWT(node.nodeValue)); + default: + return node.nodeName; + } +}; + + if (typeof module === "object") { module.exports.WTSUtils = WTSUtils; } diff --git a/lib/html2wt/WikitextSerializer.js b/lib/html2wt/WikitextSerializer.js index fdffa20..4747e3f 100644 --- a/lib/html2wt/WikitextSerializer.js +++ b/lib/html2wt/WikitextSerializer.js @@ -658,7 +658,7 @@ /** * Serialize the content of a text node */ -WSP._serializeTextNode = function(node) { +WSP._serializeTextNode = Promise.method(function(node) { // write out a potential separator? var res = node.nodeValue; var state = this.state; @@ -727,7 +727,7 @@ /* SSS FIXME: what are we doing with the stripped NLs?? */ } } -}; +}); /** * Emit non-separator wikitext that does not need to be escaped @@ -761,268 +761,238 @@ return out; }; -function traceNodeName(node) { - switch (node.nodeType) { - case node.ELEMENT_NODE: - return DU.isMarkerMeta(node, "mw:DiffMarker") ? "DIFF_MARK" : "NODE: " + node.nodeName; - case node.TEXT_NODE: - return "TEXT: " + JSON.stringify(node.nodeValue); - case node.COMMENT_NODE: - return "CMT : " + JSON.stringify(WTSUtils.commentWT(node.nodeValue)); - default: - return node.nodeName; +// DOM-based serialization +WSP._serializeDOMNode = Promise.method(function(node, domHandler) { + // To serialize a node from source, the node should satisfy these + // conditions: + // + // 1. It should not have a diff marker or be in a modified subtree + // WTS should not be in a subtree with a modification flag that + // applies to every node of a subtree (rather than an indication + // that some node in the subtree is modified). + // + // 2. It should continue to be valid in any surrounding edited context + // For some nodes, modification of surrounding context + // can change serialized output of this node + // (ex: <td>s and whether you emit | or || for them) + // + // 3. It should have valid, usable DSR + // + // 4. Either it has non-zero positive DSR width, or meets one of the + // following: + // + // 4a. It is content like <p><br/><p> or an automatically-inserted + // wikitext <references/> (HTML <ol>) (will have dsr-width 0) + // 4b. it is fostered content (will have dsr-width 0) + // 4c. it is misnested content (will have dsr-width 0) + // + // SSS FIXME: Additionally, we can guard against buggy DSR with + // some sanity checks. We can test that non-sep src content + // leading wikitext markup corresponds to the node type. + // + // Ex: If node.nodeName is 'UL', then src[0] should be '*' + // + // TO BE DONE + + var state = this.state; + var handled = false; + var wrapperUnmodified = false; + var dp = DU.getDataParsoid(node); + + dp.dsr = dp.dsr || []; + + if (state.selserMode + && !state.inModifiedContent + && DU.origSrcValidInEditedContext(state.env, node) + && dp && Util.isValidDSR(dp.dsr) + && (dp.dsr[1] > dp.dsr[0] + // FIXME: <p><br/></p> + // nodes that have dsr width 0 because currently, + // we emit newlines outside the p-nodes. So, this check + // tries to handle that scenario. + // Zero-width <ol> corresponds to automatically-inserted + // <references/> nodes. + || (dp.dsr[1] === dp.dsr[0] && /^(P|BR|OL)$/.test(node.nodeName)) + || dp.fostered || dp.misnested)) { + + if (!DU.hasDiffMarkers(node, this.env)) { + // If this HTML node will disappear in wikitext because of + // zero width, then the separator constraints will carry over + // to the node's children. + // + // Since we dont recurse into 'node' in selser mode, we update the + // separator constraintInfo to apply to 'node' and its first child. + // + // We could clear constraintInfo altogether which would be + // correct (but could normalize separators and introduce dirty + // diffs unnecessarily). + + state.currNodeUnmodified = true; + + if (DU.isZeroWidthWikitextElt(node) && + node.childNodes.length > 0 && + state.sep.constraints.constraintInfo.sepType === 'sibling') { + state.sep.constraints.constraintInfo.onSOL = state.onSOL; + state.sep.constraints.constraintInfo.sepType = 'parent-child'; + state.sep.constraints.constraintInfo.nodeA = node; + state.sep.constraints.constraintInfo.nodeB = node.firstChild; + } + + var out = state.getOrigSrc(dp.dsr[0], dp.dsr[1]); + + this.trace("ORIG-src with DSR", function() { + return '[' + dp.dsr[0] + ',' + dp.dsr[1] + '] = ' + JSON.stringify(out); + }); + + // When reusing source, we should only suppress serializing + // to a single line for the cases we've whitelisted in + // normal serialization. + var suppressSLC = DU.isFirstEncapsulationWrapperNode(node) || + ['DL', 'UL', 'OL'].indexOf(node.nodeName) > -1 || + (node.nodeName === 'TABLE' && + node.parentNode.nodeName === 'DD' && + DU.previousNonSepSibling(node) === null); + + // Use selser to serialize this text! The original + // wikitext is `out`. But first allow + // `ConstrainedText.fromSelSer` to figure out the right + // type of ConstrainedText chunk(s) to use to represent + // `out`, based on the node type. Since we might actually + // have to break this wikitext into multiple chunks, + // `fromSelSer` returns an array. + if (suppressSLC) { state.singleLineContext.disable(); } + ConstrainedText + .fromSelSer(out, node, dp, state.env) + .forEach(function(ct) { + state.emitChunk(ct, ct.node); + }); + if (suppressSLC) { state.singleLineContext.pop(); } + + // Skip over encapsulated content since it has already been + // serialized. + if (DU.isFirstEncapsulationWrapperNode(node)) { + return DU.skipOverEncapsulatedContent(node); + } else { + return node.nextSibling; + } + } + + if (DU.onlySubtreeChanged(node, this.env) && + WTSUtils.hasValidTagWidths(dp.dsr) && + // In general, we want to avoid nodes with auto-inserted + // start/end tags since dsr for them might not be entirely + // trustworthy. But, since wikitext does not have closing tags + // for tr/td/th in the first place, dsr for them can be trusted. + // + // SSS FIXME: I think this is only for b/i tags for which we do + // dsr fixups. It may be okay to use this for other tags. + ((!dp.autoInsertedStart && !dp.autoInsertedEnd) || + /^(TD|TH|TR)$/.test(node.nodeName))) { + wrapperUnmodified = true; + } } -} + + state.currNodeUnmodified = false; + + var inModifiedContent = state.selserMode && + DU.hasInsertedDiffMark(node, this.env); + + if (inModifiedContent) { state.inModifiedContent = true; } + var next = domHandler.handle(node, state, wrapperUnmodified); + if (inModifiedContent) { state.inModifiedContent = false; } + + return next; +}); /** * Internal worker. Recursively serialize a DOM subtree. */ -WSP._serializeNode = function(node) { - var prev, next, nextNode; +WSP._serializeNode = Promise.method(function(node) { + var prev, domHandler, p; var state = this.state; if (state.selserMode) { - this.trace(function() { return traceNodeName(node); }, + this.trace(function() { return WTSUtils.traceNodeName(node); }, "; prev-unmodified: ", state.prevNodeUnmodified, "; SOL: ", state.onSOL); } else { - this.trace(function() { return traceNodeName(node); }, + this.trace(function() { return WTSUtils.traceNodeName(node); }, "; SOL: ", state.onSOL); } - // serialize this node switch (node.nodeType) { - case node.ELEMENT_NODE: - - // Ignore DiffMarker metas, but clear unmodified node state - if (DU.isMarkerMeta(node, "mw:DiffMarker")) { - state.sep.lastSourceNode = node; - // Update modification flags - state.updateModificationFlags(node); - return node.nextSibling; - } - - var dp = DU.getDataParsoid(node); - dp.dsr = dp.dsr || []; - - // Update separator constraints - var domHandler = this._getDOMHandler(node); - prev = DU.previousNonSepSibling(node) || node.parentNode; - if (prev) { - this.updateSeparatorConstraints( - prev, this._getDOMHandler(prev), - node, domHandler); - } - - var handled = false; - var wrapperUnmodified = false; - - // To serialize a node from source, the node should satisfy these conditions: - // 1. It should not have a diff marker or be in a modified subtree - // WTS should not be in a subtree with a modification flag that applies - // to every node of a subtree (rather than an indication that some node - // in the subtree is modified). - // - // 2. It should continue to be valid in any surrounding edited context - // For some nodes, modification of surrounding context - // can change serialized output of this node - // (ex: <td>s and whether you emit | or || for them) - // - // 3. It should have valid, usable DSR - // - // 4. Either it has non-zero positive DSR width, or meets one of the following - // 4a. It is content like <p><br/><p> or an - // automatically-inserted wikitext <references/> (HTML <ol>) - // (will have dsr-width 0) - // 4b. it is fostered content (will have dsr-width 0) - // 4c. it is misnested content (will have dsr-width 0) - // - // SSS FIXME: Additionally, we can guard against buggy DSR with - // some sanity checks. We can test that non-sep src content - // leading wikitext markup corresponds to the node type. - // - // Ex: If node.nodeName is 'UL', then src[0] should be '*' - // - // TO BE DONE - - if (state.selserMode - && !state.inModifiedContent - && DU.origSrcValidInEditedContext(state.env, node) - && dp && Util.isValidDSR(dp.dsr) - && (dp.dsr[1] > dp.dsr[0] - // FIXME: <p><br/></p> - // nodes that have dsr width 0 because currently, - // we emit newlines outside the p-nodes. So, this check - // tries to handle that scenario. - // Zero-width <ol> corresponds to automatically-inserted - // <references/> nodes. - || (dp.dsr[1] === dp.dsr[0] && /^(P|BR|OL)$/.test(node.nodeName)) - || dp.fostered || dp.misnested)) { - if (!DU.hasDiffMarkers(node, this.env)) { - state.currNodeUnmodified = true; - handled = true; - - // If this HTML node will disappear in wikitext because of zero width, - // then the separator constraints will carry over to the node's children. - // - // Since we dont recurse into 'node' in selser mode, we update the - // separator constraintInfo to apply to 'node' and its first child. - // - // We could clear constraintInfo altogether which would be correct (but - // could normalize separators and introduce dirty diffs unnecessarily). - if (DU.isZeroWidthWikitextElt(node) && - node.childNodes.length > 0 && - state.sep.constraints.constraintInfo.sepType === 'sibling') { - state.sep.constraints.constraintInfo.onSOL = state.onSOL; - state.sep.constraints.constraintInfo.sepType = 'parent-child'; - state.sep.constraints.constraintInfo.nodeA = node; - state.sep.constraints.constraintInfo.nodeB = node.firstChild; - } - - var out = state.getOrigSrc(dp.dsr[0], dp.dsr[1]); - - // console.warn("USED ORIG"); - this.trace("ORIG-src with DSR", function() { - return '[' + dp.dsr[0] + ',' + dp.dsr[1] + '] = ' + JSON.stringify(out); - }); - - // When reusing source, we should only suppress serializing - // to a single line for the cases we've whitelisted in - // normal serialization. - var suppressSLC = DU.isFirstEncapsulationWrapperNode(node) || - ['DL', 'UL', 'OL'].indexOf(node.nodeName) > -1 || - (node.nodeName === 'TABLE' && - node.parentNode.nodeName === 'DD' && - DU.previousNonSepSibling(node) === null); - - // Use selser to serialize this text! The original - // wikitext is `out`. But first allow - // `ConstrainedText.fromSelSer` to figure out the right - // type of ConstrainedText chunk(s) to use to represent - // `out`, based on the node type. Since we might actually - // have to break this wikitext into multiple chunks, - // `fromSelSer` returns an array. - if (suppressSLC) { state.singleLineContext.disable(); } - ConstrainedText.fromSelSer(out, node, dp, state.env).forEach(function(ct) { - state.emitChunk(ct, ct.node); - }); - if (suppressSLC) { state.singleLineContext.pop(); } - - // Skip over encapsulated content since it has already been serialized - if (DU.isFirstEncapsulationWrapperNode(node)) { - nextNode = DU.skipOverEncapsulatedContent(node); - } - } else if (DU.onlySubtreeChanged(node, this.env) && - WTSUtils.hasValidTagWidths(dp.dsr) && - // In general, we want to avoid nodes with auto-inserted start/end tags - // since dsr for them might not be entirely trustworthy. But, since wikitext - // does not have closing tags for tr/td/th in the first place, dsr for them - // can be trusted. - // - // SSS FIXME: I think this is only for b/i tags for which we do dsr fixups. - // It may be okay to use this for other tags. - ((!dp.autoInsertedStart && !dp.autoInsertedEnd) || /^(TD|TH|TR)$/.test(node.nodeName))) { - wrapperUnmodified = true; - } - } - - if (!handled) { - state.currNodeUnmodified = false; - - // console.warn("USED NEW"); - if (domHandler && domHandler.handle) { - // DOM-based serialization - try { - if (state.selserMode && DU.hasInsertedDiffMark(node, this.env)) { - state.inModifiedContent = true; - nextNode = domHandler.handle(node, state, wrapperUnmodified); - state.inModifiedContent = false; - } else { - nextNode = domHandler.handle(node, state, wrapperUnmodified); - } - } catch (e) { - this.env.log("fatal", e); - } - // The handler is responsible for serializing its children - } else { - // Used to be token-based serialization - this.env.log("error", 'No dom handler found for', node.outerHTML); - } - } - + case node.ELEMENT_NODE: + // Ignore DiffMarker metas, but clear unmodified node state + if (DU.isMarkerMeta(node, "mw:DiffMarker")) { + state.sep.lastSourceNode = node; // Update modification flags state.updateModificationFlags(node); - - // Update end separator constraints - next = DU.nextNonSepSibling(node) || node.parentNode; - if (next) { - this.updateSeparatorConstraints( - node, domHandler, - next, this._getDOMHandler(next)); + return node.nextSibling; + } + domHandler = this._getDOMHandler(node); + console.assert(domHandler && domHandler.handle, + 'No dom handler found for', node.outerHTML); + p = this._serializeDOMNode(node, domHandler); + break; + case node.TEXT_NODE: + if (this.handleSeparatorText(node)) { + return node.nextSibling; + } + if (state.selserMode) { + prev = node.previousSibling; + if (!state.inModifiedContent && ( + (!prev && DU.isBody(node.parentNode)) || + (prev && !DU.isMarkerMeta(prev, "mw:DiffMarker"))) + ) { + state.currNodeUnmodified = true; + } else { + state.currNodeUnmodified = false; } - - break; - case node.TEXT_NODE: - if (!this.handleSeparatorText(node)) { - if (state.selserMode) { - // If unmodified, emit output and return - prev = node.previousSibling; - if (!state.inModifiedContent && ( - (!prev && DU.isBody(node.parentNode)) || - (prev && !DU.isMarkerMeta(prev, "mw:DiffMarker"))) - ) { - state.currNodeUnmodified = true; - } else { - state.currNodeUnmodified = false; - } - } - - // Text is not just whitespace - prev = DU.previousNonSepSibling(node) || node.parentNode; - if (prev) { - this.updateSeparatorConstraints( - prev, this._getDOMHandler(prev), - node, {}); - } - // regular serialization - this._serializeTextNode(node); - next = DU.nextNonSepSibling(node) || node.parentNode; - if (next) { - this.updateSeparatorConstraints( - node, {}, - next, this._getDOMHandler(next)); - } - - // Update modification flags - state.updateModificationFlags(node); - } - break; - case node.COMMENT_NODE: - // Merge this into separators - this.handleSeparatorText(node); - break; - default: - this.env.log("error", "Unhandled node type:", node.outerHTML); - break; + } + domHandler = {}; + p = this._serializeTextNode(node); + break; + case node.COMMENT_NODE: + // Merge this into separators + this.handleSeparatorText(node); + return node.nextSibling; + default: + console.assert("Unhandled node type:", node.outerHTML); } - // If handlers didn't provide a valid next node, - // default to next sibling - if (nextNode === undefined) { - nextNode = node.nextSibling; - } + prev = DU.previousNonSepSibling(node) || node.parentNode; + this.updateSeparatorConstraints( + prev, this._getDOMHandler(prev), + node, domHandler); - return nextNode; -}; + return p.then(function(nextNode) { + var next = DU.nextNonSepSibling(node) || node.parentNode; + this.updateSeparatorConstraints( + node, domHandler, + next, this._getDOMHandler(next)); -function stripUnnecessaryIndentPreNowikis(env, wt) { + // Update modification flags + state.updateModificationFlags(node); + + // If handlers didn't provide a valid next node, + // default to next sibling. + if (nextNode === undefined) { + nextNode = node.nextSibling; + } + + return nextNode; + }.bind(this)); +}); + +WSP._stripUnnecessaryIndentPreNowikis = function() { + var env = this.env; // FIXME: The solTransparentWikitextRegexp includes redirects, which really // only belong at the SOF and should be unique. See the "New redirect" test. var noWikiRegexp = new RegExp( '^' + env.conf.wiki.solTransparentWikitextNoWsRegexp.source + '(<nowiki>\\s+</nowiki>)([^\n]*(?:\n|$))', 'im' ); - var pieces = wt.split(noWikiRegexp); + var pieces = this.state.out.split(noWikiRegexp); var out = pieces[0]; for (var i = 1; i < pieces.length; i += 4) { out += pieces[i]; @@ -1061,15 +1031,15 @@ } out = out + nowiki + rest + pieces[i + 3]; } - return out; -} + this.state.out = out; +}; // This implements a heuristic to strip two common sources of <nowiki/>s. // When <i> and <b> tags are matched up properly, // - any single ' char before <i> or <b> does not need <nowiki/> protection. // - any single ' char before </i> or </b> does not need <nowiki/> protection. -function stripUnnecessaryQuoteNowikis(env, wt) { - return wt.split(/\n|$/).map(function(line) { +WSP._stripUnnecessaryQuoteNowikis = function() { + this.state.out = this.state.out.split(/\n|$/).map(function(line) { // Optimization: We are interested in <nowiki/>s before quote chars. // So, skip this if we don't have both. if (!(/<nowiki\s*\/>/.test(line) && /'/.test(line))) { @@ -1176,13 +1146,13 @@ return line; } }).join("\n"); -} +}; /** - * Serialize an HTML DOM document synchronously. + * Serialize an HTML DOM document. * WARNING: You probably want to use DU.serializeDOM instead. */ -WSP.serializeDOMSync = function(body, selserMode) { +WSP.serializeDOM = Promise.method(function(body, selserMode) { console.assert(DU.isBody(body), 'Expected a body node.'); this.logType = selserMode ? "trace/selser" : "trace/wts"; @@ -1206,30 +1176,31 @@ state.updateSep(body); state.resetCurrLine(body.firstChild); - state.serializeChildren(body); - // Emit child-parent seps. - state.emitChunk('', body); - // We've reached EOF, flush the remaining buffered text. - state.flushLine(); + return state.serializeChildren(body).then(function() { + // Emit child-parent seps. + state.emitChunk('', body); + // We've reached EOF, flush the remaining buffered text. + state.flushLine(); - if (state.hasIndentPreNowikis) { - // FIXME: Perhaps this can be done on a per-line basis - // rather than do one post-pass on the entire document. - // - // Strip excess/useless nowikis - state.out = stripUnnecessaryIndentPreNowikis(this.env, state.out); - } + if (state.hasIndentPreNowikis) { + // FIXME: Perhaps this can be done on a per-line basis + // rather than do one post-pass on the entire document. + // + // Strip excess/useless nowikis + this._stripUnnecessaryIndentPreNowikis(); + } - if (state.hasQuoteNowikis) { - // FIXME: Perhaps this can be done on a per-line basis - // rather than do one post-pass on the entire document. - // - // Strip excess/useless nowikis - state.out = stripUnnecessaryQuoteNowikis(this.env, state.out); - } + if (state.hasQuoteNowikis) { + // FIXME: Perhaps this can be done on a per-line basis + // rather than do one post-pass on the entire document. + // + // Strip excess/useless nowikis + this._stripUnnecessaryQuoteNowikis(); + } - return state.out; -}; + return state.out; + }.bind(this)); +}); if (typeof module === "object") { diff --git a/lib/utils/DOMUtils.js b/lib/utils/DOMUtils.js index 9769791..fd755e7 100644 --- a/lib/utils/DOMUtils.js +++ b/lib/utils/DOMUtils.js @@ -2715,7 +2715,7 @@ return p.then(function() { var Serializer = useSelser ? SelectiveSerializer : WikitextSerializer; var serializer = new Serializer({ env: env }); - return serializer.serializeDOMSync(body); + return serializer.serializeDOM(body); }).nodify(cb); }; -- To view, visit https://gerrit.wikimedia.org/r/251194 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Id68d24007229dcfa0bf6fb16c6ed3ecdaad0c2e5 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/services/parsoid Gerrit-Branch: master Gerrit-Owner: Arlolra <abrea...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits