mirror of
https://gitee.com/ulthon/ulthon_information.git
synced 2026-07-11 07:42:48 +08:00
fix(phone-image): 修复XSS注入、正则兼容性、render锁稳定性和缓存清理
T1: XSS修复 - PHP模板注入改用json_encode,poster URL转义处理
T2: fontSize NaN修复 - parseInt统一处理,lookbehind正则替换为兼容方案
T3: render锁稳定性 - insertPageBreak/removePageBreak添加15次重试上限,
fontsReady添加catch处理,_pending递归添加错误捕获
T4: 缓存清理 - init()清空缓存,render()超过3倍blocks数自动清理
This commit is contained in:
@@ -92,6 +92,9 @@ var PhoneImageEngine = (function () {
|
|||||||
$.extend(config, userConfig);
|
$.extend(config, userConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 清空缓存,避免旧数据干扰
|
||||||
|
convertedBlockCache = {};
|
||||||
|
|
||||||
// 内容流事件委托(只绑定一次)
|
// 内容流事件委托(只绑定一次)
|
||||||
$(document).off('click', '.break-inserter-btn');
|
$(document).off('click', '.break-inserter-btn');
|
||||||
$(document).off('click', '.remove-break-btn');
|
$(document).off('click', '.remove-break-btn');
|
||||||
@@ -142,6 +145,12 @@ var PhoneImageEngine = (function () {
|
|||||||
var cleanHtml = preprocessContent(postData.content_html);
|
var cleanHtml = preprocessContent(postData.content_html);
|
||||||
var blocks = parseHtmlToBlocks(cleanHtml);
|
var blocks = parseHtmlToBlocks(cleanHtml);
|
||||||
|
|
||||||
|
// 缓存清理:如果缓存条目过多则清空,防止内存膨胀
|
||||||
|
var cacheKeys = Object.keys(convertedBlockCache);
|
||||||
|
if (cacheKeys.length > blocks.length * 3) {
|
||||||
|
convertedBlockCache = {};
|
||||||
|
}
|
||||||
|
|
||||||
// 先渲染内容流(DOM) - 用于实测高度
|
// 先渲染内容流(DOM) - 用于实测高度
|
||||||
renderContentFlow(blocks);
|
renderContentFlow(blocks);
|
||||||
|
|
||||||
@@ -181,7 +190,9 @@ var PhoneImageEngine = (function () {
|
|||||||
// 如果渲染期间有新的渲染请求排队,自动触发
|
// 如果渲染期间有新的渲染请求排队,自动触发
|
||||||
if (render._pending) {
|
if (render._pending) {
|
||||||
render._pending = false;
|
render._pending = false;
|
||||||
render();
|
render().catch(function () {
|
||||||
|
// 静默处理递归渲染失败
|
||||||
|
});
|
||||||
}
|
}
|
||||||
deferred.resolve(pages);
|
deferred.resolve(pages);
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
@@ -553,13 +564,25 @@ var PhoneImageEngine = (function () {
|
|||||||
return [block];
|
return [block];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按句子拆分:句号、问号、叹号、换行
|
// 按句子拆分:句号、问号、叹号、换行(兼容不支持 lookbehind 的浏览器)
|
||||||
var sentences = text.split(/(?<=[。!?\n])/);
|
var effectiveFontSize = parseInt(config.fontSize, 10) || 14;
|
||||||
|
var parts = text.split(/([。!?\n])/);
|
||||||
|
var sentences = [];
|
||||||
|
var current = '';
|
||||||
|
for (var si = 0; si < parts.length; si++) {
|
||||||
|
current += parts[si];
|
||||||
|
if (/[。!?\n]/.test(parts[si]) && current.length > 0) {
|
||||||
|
sentences.push(current);
|
||||||
|
current = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current) sentences.push(current);
|
||||||
|
|
||||||
if (sentences.length <= 1) {
|
if (sentences.length <= 1) {
|
||||||
// 无法按句子拆分,按固定字符数拆分
|
// 无法按句子拆分,按固定字符数拆分
|
||||||
sentences = [];
|
sentences = [];
|
||||||
var chunkSize = Math.floor(getContentWidth() / config.fontSize) *
|
var chunkSize = Math.floor(getContentWidth() / effectiveFontSize) *
|
||||||
Math.floor(pageHeight / (config.fontSize * 1.8));
|
Math.floor(pageHeight / (effectiveFontSize * 1.8));
|
||||||
if (chunkSize < 10) chunkSize = 10;
|
if (chunkSize < 10) chunkSize = 10;
|
||||||
for (var ci = 0; ci < text.length; ci += chunkSize) {
|
for (var ci = 0; ci < text.length; ci += chunkSize) {
|
||||||
sentences.push(text.substring(ci, ci + chunkSize));
|
sentences.push(text.substring(ci, ci + chunkSize));
|
||||||
@@ -570,7 +593,7 @@ var PhoneImageEngine = (function () {
|
|||||||
for (var j = 0; j < sentences.length; j++) {
|
for (var j = 0; j < sentences.length; j++) {
|
||||||
var s = sentences[j].trim();
|
var s = sentences[j].trim();
|
||||||
if (!s) continue;
|
if (!s) continue;
|
||||||
var h = estimateTextHeight(s, config.fontSize);
|
var h = estimateTextHeight(s, effectiveFontSize);
|
||||||
result.push({
|
result.push({
|
||||||
type: wrapperTag === 'blockquote' ? 'blockquote' : 'p',
|
type: wrapperTag === 'blockquote' ? 'blockquote' : 'p',
|
||||||
html: '<' + wrapperTag + '>' + s + '</' + wrapperTag + '>',
|
html: '<' + wrapperTag + '>' + s + '</' + wrapperTag + '>',
|
||||||
@@ -610,7 +633,7 @@ var PhoneImageEngine = (function () {
|
|||||||
html += '" style="width:' + sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">';
|
html += '" style="width:' + sizeConfig.width + 'px;height:' + sizeConfig.height + 'px;">';
|
||||||
|
|
||||||
if (hasCover) {
|
if (hasCover) {
|
||||||
html += '<img class="cover-image" src="' + postData.poster + '" alt="">';
|
html += '<img class="cover-image" src="' + escapeHtml(postData.poster) + '" alt="">';
|
||||||
html += '<div class="cover-title">' + escapeHtml(postData.title) + '</div>';
|
html += '<div class="cover-title">' + escapeHtml(postData.title) + '</div>';
|
||||||
if (postData.desc) {
|
if (postData.desc) {
|
||||||
html += '<div class="cover-subtitle">' + escapeHtml(postData.desc) + '</div>';
|
html += '<div class="cover-subtitle">' + escapeHtml(postData.desc) + '</div>';
|
||||||
@@ -825,7 +848,9 @@ var PhoneImageEngine = (function () {
|
|||||||
? document.fonts.ready
|
? document.fonts.ready
|
||||||
: $.Deferred().resolve().promise();
|
: $.Deferred().resolve().promise();
|
||||||
|
|
||||||
fontsReady.then(function () {
|
fontsReady.catch(function () {
|
||||||
|
// 字体加载失败不影响截图,继续执行
|
||||||
|
}).then(function () {
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(function () {
|
||||||
// html2canvas会跳过visibility:hidden的元素,临时切换为可见
|
// html2canvas会跳过visibility:hidden的元素,临时切换为可见
|
||||||
$staging.css({ visibility: 'visible' });
|
$staging.css({ visibility: 'visible' });
|
||||||
@@ -1163,9 +1188,16 @@ var PhoneImageEngine = (function () {
|
|||||||
function insertPageBreak(blockIndex) {
|
function insertPageBreak(blockIndex) {
|
||||||
// 如果正在渲染,延迟重试
|
// 如果正在渲染,延迟重试
|
||||||
if (render._locked) {
|
if (render._locked) {
|
||||||
|
insertPageBreak._retryCount = (insertPageBreak._retryCount || 0) + 1;
|
||||||
|
if (insertPageBreak._retryCount >= 15) {
|
||||||
|
insertPageBreak._retryCount = 0;
|
||||||
|
layer.msg('操作超时,请重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setTimeout(function () { insertPageBreak(blockIndex); }, 200);
|
setTimeout(function () { insertPageBreak(blockIndex); }, 200);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
insertPageBreak._retryCount = 0;
|
||||||
|
|
||||||
var cleanHtml = preprocessContent(postData.content_html);
|
var cleanHtml = preprocessContent(postData.content_html);
|
||||||
var $temp = $('<div>').html(cleanHtml);
|
var $temp = $('<div>').html(cleanHtml);
|
||||||
@@ -1191,9 +1223,16 @@ var PhoneImageEngine = (function () {
|
|||||||
function removePageBreak(blockIndex) {
|
function removePageBreak(blockIndex) {
|
||||||
// 如果正在渲染,延迟重试
|
// 如果正在渲染,延迟重试
|
||||||
if (render._locked) {
|
if (render._locked) {
|
||||||
|
removePageBreak._retryCount = (removePageBreak._retryCount || 0) + 1;
|
||||||
|
if (removePageBreak._retryCount >= 15) {
|
||||||
|
removePageBreak._retryCount = 0;
|
||||||
|
layer.msg('操作超时,请重试');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setTimeout(function () { removePageBreak(blockIndex); }, 200);
|
setTimeout(function () { removePageBreak(blockIndex); }, 200);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
removePageBreak._retryCount = 0;
|
||||||
|
|
||||||
// 统计这是第几个 page-break
|
// 统计这是第几个 page-break
|
||||||
var cleanHtml = preprocessContent(postData.content_html);
|
var cleanHtml = preprocessContent(postData.content_html);
|
||||||
|
|||||||
@@ -187,13 +187,13 @@
|
|||||||
|
|
||||||
var postData = {
|
var postData = {
|
||||||
postId: {$post.id},
|
postId: {$post.id},
|
||||||
title: '{$post.title|raw}',
|
title: <?php echo json_encode($post->title ?? ''); ?>,
|
||||||
desc: '{$post.desc|default=""}',
|
desc: <?php echo json_encode($post->desc ?? ''); ?>,
|
||||||
coverText: '{$post->getData("cover_text")|default=""}',
|
coverText: <?php echo json_encode($post->getData('cover_text') ?? ''); ?>,
|
||||||
contentHtml: $('#post-content-html').html(),
|
contentHtml: $('#post-content-html').html(),
|
||||||
poster: '{$post.poster|default=""}',
|
poster: <?php echo json_encode($post->poster ?? ''); ?>,
|
||||||
authorName: '{$post.author_name|default=""}',
|
authorName: <?php echo json_encode($post->author_name ?? ''); ?>,
|
||||||
createTime: '{$post.create_time_text|default=""}',
|
createTime: <?php echo json_encode($post->create_time_text ?? ''); ?>,
|
||||||
categoryName: ''
|
categoryName: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user