Branch: refs/heads/main Home: https://github.com/WebKit/WebKit Commit: 664af9c1e8d722f89b18f8783a1737d799a2fafc https://github.com/WebKit/WebKit/commit/664af9c1e8d722f89b18f8783a1737d799a2fafc Author: Wenson Hsieh <wenson_hs...@apple.com> Date: 2023-08-08 (Tue, 08 Aug 2023)
Changed paths: M Source/WebCore/PAL/pal/spi/ios/UIKitSPI.h M Source/WebCore/editing/cocoa/AttributedString.h M Source/WebCore/editing/cocoa/AttributedString.mm M Source/WebCore/editing/cocoa/HTMLConverter.mm M Source/WebCore/platform/cocoa/MIMETypeRegistryCocoa.mm M Source/WebKit/Shared/Cocoa/WebCoreArgumentCodersCocoa.serialization.in M Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewGetContents.mm Log Message: ----------- REGRESSION (261984@main): Copied tables from some web pages don't properly paste into Numbers https://bugs.webkit.org/show_bug.cgi?id=259928 rdar://112030767 Reviewed by Megan Gardner. When copying a table in Quip and pasting into the Numbers app, tables are programmatically written to the pasteboard as HTML; upon paste, UIFoundation uses `nsattributedstringagent` to convert this pasted markup into an `NSAttributedString`, by loading the markup in a `WKWebView` and asking it to `-_getContentsAsAttributedStringWithCompletionHandler:`. This exercises the `HTMLConverter` codepath in WebCore, which implements the correct logic for representing tables and lists in the markup as `-textBlocks` and `-textLists`, respectively, on `NSParagraphStyle`; for tables, we list multiple `NSTextTableBlock`s that all point to the same `NSTextTable` object. For instance, two adjacent cells in the same table would be represented as: ``` "NSParagraphStyle" => { textBlocks => [ NSTextTableBlock 0x6000006e7100, table=<NSTextTable 0x6000003f27d0>, {0, 0} ], … } "NSParagraphStyle" => { textBlocks => [ NSTextTableBlock 0x6000006e6d80, table=<NSTextTable 0x6000003f27d0>, {0, 1} ], … } ``` Similarly, lists are represented by having multiple paragraph styles with the same `NSTextList`s. Two adjacent items in the same list, for instance, will have paragraph styles whose `-textLists` array contains the exact same list (i.e. equal pointers): ``` "NSParagraphStyle" => { textLists => [ NSTextList 0x600002676400 format <{disc}> ], … } "NSParagraphStyle" => { textLists => [ NSTextList 0x600002676400 format <{disc}> ], … } ``` Prior to the changes in 261984@main, we used a single `NSKeyedArchiver`/`NSKeyedUnarchiver` to encode/decode an `NSAttributedString` when sending this data from the web process to the UI process for writing to the pasteboard. This meant that Foundation's internal object caching mechanism in the keyed archiver would ensure that multiple `NSParagraphStyle` instances that all reference the same `NSTextList` or `NSTextTable` upon encoding would still reference the same list or table upon decoding, thereby preserving the table/list structure when writing to the pasteboard. However, after 261984@main, we now use a different keyed (un)archiver when encoding/decoding each individual attribute; as a result, different `NSParagraphStyle` instances that reference the same `NSTextList` or `NSTextTable` no longer correspond to the same object in the archiver's backing map. This means that in the two above examples, we'd end up with two 2x1 tables (each with only 1 cell populated), and two single-item lists, respectively. To fix this, we add some logic to plumb unique identifiers corresponding to tables or lists for each `NSTextBlock` or `NSTextList` in `-[NSParagraphStyle textBlocks]` and `-[NSParagraph textLists]`, which are propagated along with the rest of the attributes upon encoding. When decoding, we use these unique identifiers to ensure that all paragraph styles that previously referenced the same table or list will continue to do so after decoding. Tests: WKWebView.AttributedStringFromTable WKWebView.AttributedStringFromList * Source/WebCore/PAL/pal/spi/ios/UIKitSPI.h: Move various UIKit SPI and IPI declarations out of the implementation file in `HTMLConverter.mm` and into `UIKitSPI.h` instead, so that these declarations can be used in both `AttributedString.mm` and `HTMLConverter.mm`. * Source/WebCore/editing/cocoa/AttributedString.h: Instead of encoding a single `RetainPtr<NSParagraphStyle>`, send a struct containing an `RetainPtr<NSParagraphStyle>`, a list of `TextTableID` representing each text block, and a list of `TextListID`. Note that `tableIDs` is a list of optional identifiers, since a `textBlock` may not necessarily correspond to a table cell, in which case we use represent it via `nullopt`. `textLists` doesn't have this same constraint since each entry corresponds directly to an `NSTextList`. * Source/WebCore/editing/cocoa/AttributedString.mm: (WebCore::reconstructStyle): Add a helper method to take `HashMap`s of tables and lists by ID, and (only if necessary) create a copy of the decoded `NSParagraphStyle` whose referenced tables and lists match those that were previously referenced when decoding earlier parts of the attributed string. (WebCore::toNSObject): (WebCore::toNSDictionary): Maintain maps of tables and lists by unique ID over the whole lifetime of decoding the string. (WebCore::AttributedString::documentAttributesAsNSDictionary const): (WebCore::AttributedString::nsAttributedString const): (WebCore::extractListIDs): (WebCore::extractTableIDs): (WebCore::extractValue): (WebCore::extractDictionary): (WebCore::AttributedString::fromNSAttributedStringAndDocumentAttributes): Implement the opposite half of the above logic, by collecting identical `NSTextList`s and `NSTextTable`s that are referenced by `NSParagraphStyle`s during encoding. This allows us to build a list of unique IDs that correspond to each table or list, which we send over IPC and use during decoding to preserve references to the same objects when deserializing paragraph styles. * Source/WebCore/editing/cocoa/HTMLConverter.mm: * Source/WebCore/platform/cocoa/MIMETypeRegistryCocoa.mm: (WebCore::extensionsForMIMETypeMap): Drive-by fix: suppress a new deprecation warning. * Source/WebKit/Shared/Cocoa/WebCoreArgumentCodersCocoa.serialization.in: * Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewGetContents.mm: (-[WKWebView _contentsAsAttributedString]): Add a couple of API tests to exercise attributed string conversion for tables and lists. Importantly, these tests verify that table/list structure is preserved in the decoded string. Canonical link: https://commits.webkit.org/266700@main _______________________________________________ webkit-changes mailing list webkit-changes@lists.webkit.org https://lists.webkit.org/mailman/listinfo/webkit-changes