mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-10 17:48:50 +08:00
Fix support for tables pasted from Excel which contain a <style> tag in their HTML #1773
This commit is contained in:
parent
5c912578c4
commit
378bc9415f
5 changed files with 59 additions and 22 deletions
|
@ -576,7 +576,11 @@ export default class Application extends EventEmitter {
|
|||
out = html;
|
||||
}
|
||||
// win = BrowserWindow.fromWebContents(event.sender)
|
||||
event.sender.send('inline-styles-result', { html: out, key });
|
||||
if (key) {
|
||||
event.sender.send('inline-styles-result', { html: out, key });
|
||||
} else {
|
||||
event.returnValue = out;
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', (event, hasVisibleWindows) => {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import * as Immutable from 'immutable';
|
||||
import { Editor, Value, Operation, Range } from 'slate';
|
||||
import { Editor as SlateEditorComponent, EditorProps } from 'slate-react';
|
||||
import { clipboard as ElectronClipboard } from 'electron';
|
||||
import { InlineStyleTransformer } from 'mailspring-exports';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
|
@ -11,7 +13,6 @@ import ComposerEditorToolbar from './composer-editor-toolbar';
|
|||
import { schema, plugins, convertFromHTML, convertToHTML, convertToPlainText } from './conversion';
|
||||
import { lastUnquotedNode, removeQuotedText } from './base-block-plugins';
|
||||
import { changes as InlineAttachmentChanges } from './inline-attachment-plugins';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
const AEditor = (SlateEditorComponent as any) as React.ComponentType<
|
||||
EditorProps & { ref: any; propsForPlugins: any }
|
||||
|
@ -211,9 +212,17 @@ export class ComposerEditor extends React.Component<ComposerEditorProps> {
|
|||
}
|
||||
}
|
||||
|
||||
// handle text/html paste
|
||||
const html = event.clipboardData.getData('text/html');
|
||||
let html = event.clipboardData.getData('text/html');
|
||||
|
||||
if (html) {
|
||||
// Unfortuantely, pasting HTML requires an synchronous hop through our main process style
|
||||
// transfomer. This ensures that we inline styles and preserve as much as possible.
|
||||
// (eg: pasting tables from Excel).
|
||||
try {
|
||||
html = InlineStyleTransformer.runSync(html);
|
||||
} catch (err) {
|
||||
//no-op
|
||||
}
|
||||
const value = convertFromHTML(html);
|
||||
if (value && value.document) {
|
||||
editor.insertFragment(value.document);
|
||||
|
|
|
@ -27,6 +27,11 @@ import { Rule, ComposerEditorPlugin } from './types';
|
|||
import './patch-chrome-ime';
|
||||
|
||||
export const schema = {
|
||||
blocks: {
|
||||
[UNEDITABLE_TYPE]: {
|
||||
isVoid: true,
|
||||
},
|
||||
},
|
||||
inlines: {
|
||||
[VARIABLE_TYPE]: {
|
||||
isVoid: true,
|
||||
|
|
|
@ -7,18 +7,15 @@ import RegExpUtils from '../regexp-utils';
|
|||
let userAgentDefault = null;
|
||||
|
||||
class InlineStyleTransformer {
|
||||
_inlineStylePromises = {};
|
||||
_inlineStyleResolvers = {};
|
||||
_inlineStylePromises: { [key: string]: Promise<string> } = {};
|
||||
_inlineStyleResolvers: { [key: string]: (result: string) => void } = {};
|
||||
|
||||
constructor() {
|
||||
ipcRenderer.on('inline-styles-result', this._onInlineStylesResult);
|
||||
}
|
||||
|
||||
run = html => {
|
||||
if (!html || typeof html !== 'string' || html.length <= 0) {
|
||||
return Promise.resolve(html);
|
||||
}
|
||||
if (!RegExpUtils.looseStyleTag().test(html)) {
|
||||
run = (html: string) => {
|
||||
if (!this._requiresProcessing(html)) {
|
||||
return Promise.resolve(html);
|
||||
}
|
||||
|
||||
|
@ -27,24 +24,46 @@ class InlineStyleTransformer {
|
|||
.update(html)
|
||||
.digest('hex');
|
||||
|
||||
// http://stackoverflow.com/questions/8695031/why-is-there-often-a-inside-the-style-tag
|
||||
// https://regex101.com/r/bZ5tX4/1
|
||||
let styled = html.replace(
|
||||
/<style[^>]*>[\n\r \t]*<!--([^</]*)-->[\n\r \t]*<\/style/g,
|
||||
(full, content) => `<style>${content}</style`
|
||||
);
|
||||
|
||||
styled = this._injectUserAgentStyles(styled);
|
||||
|
||||
if (this._inlineStylePromises[key] == null) {
|
||||
html = this._prepareHTMLForInlineStyling(html);
|
||||
|
||||
this._inlineStylePromises[key] = new Promise(resolve => {
|
||||
this._inlineStyleResolvers[key] = resolve;
|
||||
ipcRenderer.send('inline-style-parse', { html: styled, key: key });
|
||||
ipcRenderer.send('inline-style-parse', { html, key });
|
||||
});
|
||||
}
|
||||
return this._inlineStylePromises[key];
|
||||
};
|
||||
|
||||
runSync = (html: string) => {
|
||||
if (!this._requiresProcessing(html)) return html;
|
||||
html = this._prepareHTMLForInlineStyling(html);
|
||||
return ipcRenderer.sendSync('inline-style-parse', { html, key: '' });
|
||||
};
|
||||
|
||||
_requiresProcessing(html: string) {
|
||||
if (!html || typeof html !== 'string' || html.length <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (!RegExpUtils.looseStyleTag().test(html)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_prepareHTMLForInlineStyling(html: string) {
|
||||
// http://stackoverflow.com/questions/8695031/why-is-there-often-a-inside-the-style-tag
|
||||
// https://regex101.com/r/bZ5tX4/1
|
||||
let result = html.replace(
|
||||
/<style[^>]*>[\n\r \t]*<!--([^</]*)-->[\n\r \t]*<\/style/g,
|
||||
(full, content) => `<style>${content}</style`
|
||||
);
|
||||
|
||||
result = this._injectUserAgentStyles(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// This will prepend the user agent stylesheet so we can apply it to the
|
||||
// styles properly.
|
||||
_injectUserAgentStyles(body) {
|
||||
|
|
2
mailsync
2
mailsync
|
@ -1 +1 @@
|
|||
Subproject commit 7f7642b3a642761ce0ea5c5102a969afc059a2ea
|
||||
Subproject commit e44852a479a3f9013e6fd51b3d36c27126c142e5
|
Loading…
Reference in a new issue