REGRESSION (r223440): Copying & pasting a list from Microsoft Word to TinyMCE fails
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Feb 2018 05:07:56 +0000 (05:07 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Feb 2018 05:07:56 +0000 (05:07 +0000)
commit439a5944a0b508036cfca3a1801ea5e0c89fcc10
treeeba98c7ef4c6855c293353fdbea2c47d7b5f177b
parent0bb5160b157877f7a26918a35cda688ce12eea0a
REGRESSION (r223440): Copying & pasting a list from Microsoft Word to TinyMCE fails
https://bugs.webkit.org/show_bug.cgi?id=182564

Reviewed by Wenson Hsieh.

Source/WebCore:

Turns out that Microsoft Word generates p and span elements with special styles instead of standard
ul and ol elements when copying a list items, and TinyMCE has a specialized code path to process
this proprietary format of Microsoft Word. The regression was caused by WebKit's sanitization code
stripping away these non-standard CSS rules and inline styles.

To preseve pre-r223440 behavior in TinyMCE, we preserve the following in a HTML markup:

1. The "html" element at the beginning with xmlns content attributes
2. @list rules in a style element starting with "/* List Definitions */" comment
3. inline style content attribute with "mso-list" property
4. comments conditional sections with "[if !supportLists]" and "[endif]"

(1) is needed for TinyMCE to trigger the specialized code path for Microsoft Word. (2) contains
the information about the structure of list items. (3) is needed to associate each p element with
a rule in (2). (4) is needed to strip away the content generated as list markers (e.g. dots).

We enable this "MSO list quirks" when the content comes from a non-WebKit client or a WebKit client
that doesn't enable custom pasteboard data (detected by the content origin being null), and the HTML
markup starts with a specific sequence of characters generated by Microsoft Word.

Test: http/tests/security/clipboard/copy-paste-html-across-origin-strips-mso-list.html
      PasteHTML.PreservesMSOList
      PasteHTML.StripsMSOListWhenMissingMSOHTMLElement
      PasteWebArchive.PreservesMSOList
      PasteWebArchive.StripsMSOListWhenMissingMSOHTMLElement

* editing/MarkupAccumulator.cpp:
(WebCore::MarkupAccumulator::appendTextSubstring): Added.
* editing/MarkupAccumulator.h:
* editing/WebContentReader.cpp:
(WebCore::FrameWebContentReader::msoListQuirksForMarkup const): Added. Enables the MSO list quirks
if the content origin is null. The content origin specifies the pasteboard content's origin if it's
copied in WebKit with custom pasteboard data types enabled. In all other applications, it would be
set to null.
* editing/WebContentReader.h:
* editing/cocoa/WebContentReaderCocoa.mm:
(WebCore::markupForFragmentInDocument): Moved to markup.cpp as sanitizedMarkupForFragmentInDocument.
(WebCore::sanitizeMarkupWithArchive):
(WebCore::WebContentReader::readWebArchive): Always disables MSO list quirks since this code path is
only used by WebKit's native code to paste content.
(WebCore::WebContentMarkupReader::readWebArchive): Calls msoListQuirksForMarkup since this is the code
path used by DataTransfer.
(WebCore::WebContentReader::readHTML): Always disables MSO list quirks since this code path is only
used by WebKit's native code to paste content.
(WebCore::WebContentMarkupReader::readHTML): Calls msoListQuirksForMarkup since this is the code path
used by DataTransfer.
* editing/markup.cpp:
(WebCore::sanitizeMarkup): Use sanitizedMarkupForFragmentInDocument to share code.
(WebCore::MSOListMode): Added. Set to Preserve if the sanitized markup is the one generated by
Microsoft Word, and MSO list quirks should actually kick in. This is unlike MSOListQuirks, which is
set to Enable whenever the content COULD be the one generated by Microsoft Word.
(WebCore::StyledMarkupAccumulator): Added a special MSO list preservation mode enabled by MSOListMode.
(WebCore::StyledMarkupAccumulator::StyledMarkupAccumulator):
(WebCore::StyledMarkupAccumulator::appendElement): Preseve (3). Unfortunately, TinyMCE only recognizes
mso-list and related properties only if they appear on their own. But we also need to preserve
the inline style generated using the computed style since we would lose the inline styles of the text
otherwise (e.g. red text and bold font). To workaround this, we generate two style content attributes,
one containing computed styles and another one containing mso-list. Luckily, the HTML parsing algorithm
dictates that the first attribute always wins when more than one attributes of the same name appears,
so we place the computed style's style attribute first so that the pasted content in non-TinyMCE
environment will continue to work.
(WebCore::StyledMarkupAccumulator::traverseNodesForSerialization):
(WebCore::StyledMarkupAccumulator::appendNodeToPreserveMSOList): Added. Generates special markup for
the conditional statements and the special style element with @list rules.
(WebCore::createMarkupInternal):
(WebCore::createMarkup):
(WebCore::sanitizedMarkupForFragmentInDocument): Moved from WebContentReaderCocoa.mm. If MSOListQuirks
is set to Enable, and the markup starts with a specific sequence of characters, generate the markup
with the newly added quirks code in StyledMarkupAccumulator, and wrap it in a special "html" element
TinyMCE recognizes.
* editing/markup.h:
(WebCore::MSOListQuirks): Added. Set to CheckIfNeeded if the content COULD require MSO list quirks.

Tools:

Added tests for pasting HTML with list items generated by Microsoft Word as well as HTML which looks like
the one generated by Microsoft Word but missing a proper "html" element at the beginning.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/PasteHTML.mm: Added test cases.
* TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm: Added test cases.
(msoListMarkupWithoutProperHTMLElement): Added.
* TestWebKitAPI/Tests/WebKitCocoa/mso-list.html: Added.

LayoutTests:

Added a test to make sure special Microsoft Word quirks would not get triggered
when pasting content copied within WebKit.

* http/tests/security/clipboard/copy-paste-html-across-origin-strips-mso-list-expected.txt: Added.
* http/tests/security/clipboard/copy-paste-html-across-origin-strips-mso-list.html: Added.
* http/tests/security/clipboard/resources/copy-mso-list.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@228352 268f45cc-cd09-0410-ab3c-d52691b4dbfc
18 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/security/clipboard/copy-paste-html-across-origin-strips-mso-list-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/clipboard/copy-paste-html-across-origin-strips-mso-list.html [new file with mode: 0644]
LayoutTests/http/tests/security/clipboard/resources/copy-mso-list.html [new file with mode: 0644]
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/editing/MarkupAccumulator.cpp
Source/WebCore/editing/MarkupAccumulator.h
Source/WebCore/editing/WebContentReader.cpp
Source/WebCore/editing/WebContentReader.h
Source/WebCore/editing/cocoa/WebContentReaderCocoa.mm
Source/WebCore/editing/markup.cpp
Source/WebCore/editing/markup.h
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteHTML.mm
Tools/TestWebKitAPI/Tests/WebKitCocoa/PasteWebArchive.mm
Tools/TestWebKitAPI/Tests/WebKitCocoa/mso-list.html [new file with mode: 0644]