- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
导读
本文以 CKEditor 4 仓库中的手动测试规范 tests/plugins/easyimage/manual/datauriimages.md 为核心,剖析 Easy Image 插件在处理以data:URI 形式粘贴或拖入的图片时的类型过滤与 upcast 判定逻辑。读完本文,你将理解为什么"带 logo 的 PNG 图片"会被自动转换为 Easy Image 控件(widget),而"由公式编辑器生成的 SVG 图片"却会原样保留为普通图片,并掌握在 IE 11 / Edge 等浏览器下的测试注意事项与源码级验证方法。
一、测试场景的背景:bug #3524 与 4.13.1
该手动测试文件头部携带的元信息说明了它的来历与适用范围:
@bender-tags: 4.13.1, bug, 3524这表明本次测试针对的是 CKEditor 4.13.1 版本中修复的一个 bug(编号 3524):当用户向编辑器中粘贴(或拖入)以 data URI 内联的图片时,部分图片格式(尤其是 SVG)会被错误地 upcast 成 Easy Image 控件,或在上传过程中丢失/报错。该测试规范用于人工回归验证修复是否生效。
测试同时引用了两个已知的浏览器缺陷:
- #3546:IE 11 与 Edge 浏览器中的复制粘贴缺陷;
- #3568:使用原生右键菜单进行复制时触发的缺陷。
因此测试规范明确要求:在 IE 11 和 Edge 上只能使用 Ctrl+C / Ctrl+V 复制粘贴,而在其余浏览器上不要使用原生右键菜单复制,否则会命中已知 bug 而干扰验证结果。
二、手动测试规范完整解读
2.1 测试环境配置
测试页面的插件加载配置如下(对应 datauriimages.md 与 datauriimages.html):
@bender-ckeditor-plugins: sourcearea, htmlwriter, wysiwygarea, floatingspace, toolbar, easyimage, undo @bender-include: ../_helpers/tools.js- 编辑器类型:经典编辑器(
wysiwygarea),配合浮动工具栏(floatingspace、toolbar)与源码视图(sourcearea); - 核心被测插件:
easyimage,它依赖imagebase、balloontoolbar、cloudservices等插件(见 plugins/easyimage/plugin.js 的requires声明); - 辅助工具:引入 tests/plugins/easyimage/_helpers/tools.js,其中定义了测试所用的 Cloud Services 上传网关地址(
CLOUD_SERVICES_UPLOAD_GATEWAY)与令牌地址(CLOUD_SERVICES_TOKEN_URL)。
测试页 HTML(datauriimages.html)预先放置了两张以data:URI 内联的示例图片,作为对比素材:
- 不被 upcast 的图片:一张由 WIRIS 公式编辑器生成的 SVG 公式图,其
src以data:image/svg+xml;charset=utf8,...开头,内嵌 MathML 与字体资源; - 会被 upcast 的图片:一张
data:image/png;base64,...的 PNG logo 图片。
2.2 操作步骤与预期结果
原文档规定的操作与判定标准如下:
步骤 1:打开浏览器开发者控制台。
步骤 2:向编辑器中复制粘贴或拖放图片。
- 预期结果:
- 图片被成功粘贴到编辑器中;
- 带 logo 的图片被 upcast 为 Easy Image 控件;
- 公式图片(SVG)不被 upcast 为 Easy Image 控件。
- 意外结果(即 bug 复现):
- 公式图片未能复制成功,且控制台出现错误;
- 公式图片被错误地 upcast 为 Easy Image 控件。
步骤 3:切换到源码视图检查。
- 预期结果:
- 公式图片的
src属性仍以data:image/svg+xml;开头,即保持原始 data URI 形式,未被上传流程改写。
- 公式图片的
三、源码级原理剖析:为什么 PNG 会被 upcast 而 SVG 不会
3.1 支持类型白名单
Easy Image 插件在源码顶部定义了支持的上传图片类型白名单(plugins/easyimage/plugin.js):
var WIDGET_NAME = 'easyimage', SUPPORTED_IMAGE_TYPES = [ 'jpeg', 'png', 'gif', 'bmp' ];这份白名单贯穿三个关键环节:
- 文件拖放上传:widget 定义的
supportedTypes属性将其编译为正则image/(jpeg|png|gif|bmp)(plugin.js),只有匹配的文件才会被上传特征(upload feature)捕获并转成控件; - data URI 粘贴识别:粘贴监听器中的
imgWithDataUri正则(见下节); - 自动化测试断言:对应 tests/plugins/easyimage/pasteintegration.js 中
IMG常量。
SVG 之所以被排除,是因为它不在SUPPORTED_IMAGE_TYPES白名单之内——这与 Easy Image 依赖 Cloud Services 后端做图片处理(裁剪、缩放、响应式srcset)的产品定位一致:后端只承诺处理位图格式(JPEG/PNG/GIF/BMP),矢量 SVG 及任意伪造的 MIME 类型都不在支持范围内。
3.2 粘贴监听器与 data URI 判定正则
核心逻辑位于 plugins/easyimage/plugin.js 的addPasteListener函数。它注册了一个paste事件监听器,专门处理以 data URI 内联的图片。此类情况发生在:
- IE 11 从剪贴板粘贴图片时;
- Firefox 从剪贴板粘贴单个图片文件时。
这两种情况下,浏览器会把图片转成<img src="data:...">元素进入剪贴板内容,而不是作为File对象经dataTransfer传入。
监听器开头的快速过滤正则(plugin.js)是关键:
var imgWithDataUri = new RegExp( '<img[^>]*\\ssrc=[\\\'\\"]?data:image/(' + SUPPORTED_IMAGE_TYPES.join( '|' ) + ');base64,', 'i' );它要求同时满足三个条件:
- HTML 片段中存在
<img>标签; - 该图片的
src以data:开头; data:image/之后的 MIME 子类型必须是白名单中的jpeg、png、gif或bmp,且必须以;base64,编码形式内联。
由于image/svg+xml;charset=utf8,...既不属于四个支持类型,又不使用;base64,编码,因此正则直接不匹配,监听器立刻return——SVG 图片不会被上传、不会被 upcast,原样保留为普通图片。这正是测试预期"公式图片不被 upcast"的源码依据。
该正则同时覆盖了"伪造类型"与"非图片 data URI"两类脏数据,源码注释也明确指出这是出于性能考虑:在解析整段 HTML 之前先用正则快速排除不含图片或不符合格式的内容(plugin.js)。
3.3 通过判定后的处理流程
当图片通过上述判定后,监听器会执行以下链路(plugin.js):
- 将粘贴内容
dataValue追加到临时 DOM 文档中(使用document.implementation.createHTMLDocument创建隔离文档,注释明确说明这是为防止 XSS 攻击); - 遍历所有
<img>,逐一校验:src以data:开头、不是 fake object(data('cke-realelement') === null)、且不位于只读区块内(对应历史缺陷 dev.ckeditor.com/ticket/13003 的处理); - 从
src中解析 MIME 子类型并据此推断文件格式(缺省回退为jpg); - 调用 imagebase 上传特征提供的
_spawnLoader创建文件加载器(plugins/imagebase/plugin.js),其默认执行loadAndUpload(先读取 data URI 内容再上传); - 调用
_insertWidget以 data URI 为src插入控件模板(<figure><img></figure>,plugins/imagebase/plugin.js),并把uploadId暂存为data-cke-upload-id属性; - 用控件元素替换原始
<img>,最终由 widget 的init方法接管uploadId(plugin.js),imagebase 的上传特征在 widgetready时根据uploadId找到 loader 并开始上传(plugins/imagebase/plugin.js)。
上传成功后,控件会依据 Cloud Services 响应设置src、srcset(由 plugin.js 的_parseSrcSet生成)与sizes="100vw"等属性(plugin.js)。这也是为什么测试要求在源码视图中检查公式图片——正常路径下被 upcast 的图片最终会被替换为后端 URL,而未被 upcast 的 SVG 公式图必须保持原始data:image/svg+xml;不变。
四、自动化测试佐证:pasteintegration.js
与手动测试遥相呼应,仓库还提供了自动化回归测试 tests/plugins/easyimage/pasteintegration.js,其IMG常量完整覆盖了支持/不支持的 MIME 类型矩阵:
| 输入片段 | MIME 类型 | 是否 upcast | 测试用例 |
|---|---|---|---|
data:image/jpeg;base64,foo | image/jpeg | ✅ 是 | test easyimage upcasts image/jpeg |
data:image/gif;base64,... | image/gif | ✅ 是 | test easyimage upcasts image/gif |
data:image/png;base64,... | image/png | ✅ 是 | test easyimage upcasts image/png |
data:image/bmp;base64,foo | image/bmp | ✅ 是 | test easyimage upcasts image/bmp |
data:image/svg+xml;charset=utf8,... | image/svg+xml | ❌ 否 | test easyimage not upcasts image/svg |
data:image/foobar;base64,foobarbaz | image/foobar | ❌ 否 | test easyimage not upcasts image/foobar |
data:text/html;charset=utf8,... | text/html | ❌ 否 | test easyimage not upcasts text/html |
测试通过sinon.spy监听_spawnLoader方法:期望 upcast 的用例断言其被调用一次且getData()中包含easyimage标记;不期望 upcast 的用例断言其未被调用。该自动化测试与手动测试规范互为印证,可直接作为 data URI 图片 upcast 行为的判定基准。
五、浏览器兼容性与测试注意事项
原文档末尾的两条Important提醒是实战中必须遵守的约束:
- 不要使用原生右键菜单复制:在浏览器原生上下文菜单中选择"复制"会触发已知 bug#3568,导致复制结果与正常 Ctrl+C 不一致,干扰 upcast 行为判断;
- IE 11 / Edge 上仅使用复制粘贴:由于#3546与#3568两个缺陷,在 IE 11 与 Edge 中应只通过键盘快捷键复制粘贴图片,避免拖放路径。
此外,Easy Image 插件本身带有环境支持门槛(plugins/easyimage/plugin.js):
isSupportedEnvironment: function() { return !CKEDITOR.env.ie || CKEDITOR.env.version >= 11; }即IE 10 及以下版本不受支持,测试页中也会通过bender.tools.ignoreUnsupportedEnvironment( 'easyimage' )在不支持的环境下自动跳过。
六、总结:一条 data URI 图片的完整判定链
综合手动测试规范、插件源码与自动化测试,CKEditor 4 Easy Image 对粘贴/拖入的 data URI 图片的判定链可归纳为:
- 环境门槛:IE 11+ 或其他现代浏览器;
- 快速正则过滤:HTML 中是否含有
src="data:image/(jpeg|png|gif|bmp);base64,..."格式的<img>(plugin.js); - 语义校验:非只读区块、非 fake object;
- 控件化(upcast):满足条件则经
_spawnLoader+_insertWidget转换为 Easy Image 控件并进入上传流程;不满足则原样保留普通图片。
对于集成公式编辑器(如 WIRIS/MathType 类工具)的 CKEditor 4 站点,这条判定链意味着:SVG 公式图片永远不会被 Easy Image 拦截或改写,可以放心地以 data URI 形式内联在文档中;而位图格式(PNG/JPEG/GIF/BMP)的粘贴内容则会自动走上 Cloud Services 上传与srcset响应式处理流程。若需要进一步调整支持格式或自定义控件行为,可分别修改SUPPORTED_IMAGE_TYPES、widget 定义中的supportedTypes以及easyimage_class、easyimage_styles、easyimage_toolbar等配置(plugin.js 中的CKEDITOR.config默认值均有完整文档注释)。
参考路径速查
- 手动测试规范:tests/plugins/easyimage/manual/datauriimages.md
- 手动测试页面(含 SVG 公式图与 PNG logo 示例):tests/plugins/easyimage/manual/datauriimages.html
- Easy Image 插件源码(粘贴监听器 / 类型白名单 / 配置项):plugins/easyimage/plugin.js
- imagebase 上传特征(loader 与控件插入):plugins/imagebase/plugin.js
- 自动化回归测试(MIME 类型矩阵):tests/plugins/easyimage/pasteintegration.js
- 测试辅助工具(Cloud Services 网关配置):tests/plugins/easyimage/_helpers/tools.js
- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
相关推荐
CKEditor 4 Easy Image 与 Word 粘贴集成:从误转图片 Bug 到 Data URI 图片 Upcast 机制解析
CKEditor 4 Easy Image 与 Word 粘贴集成:从误转图片 Bug 到 Data URI 图片 Upcast 机制解析 本文围绕 CKEdi
前端UI组件CKEditor 4 Edge 浏览器粘贴图片边界用例深度解析:从 Bender 手动测试到 Clipboard 插件源码
CKEditor 4 Edge 浏览器粘贴图片边界用例深度解析:从 Bender 手动测试到 Clipboard 插件源码 适用读者 :CKEditor 4 的
前端UI组件CKEditor 4 移动端粘贴对话框行为解析:从 Bender 手动测试到 forcePasteDialog 源码实现
CKEditor 4 移动端粘贴对话框行为解析:从 Bender 手动测试到 forcePasteDialog 源码实现 导读 本文围绕 CKEditor 4
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考