Skip to content

Commit c2da346

Browse files
Merge pull request #671 from wordpress-mobile/issue/658-unsupported-html-fixes
UnsupportedHTML: Multiple Updates
2 parents 9c68007 + 1046f1e commit c2da346

File tree

6 files changed

+83
-49
lines changed

6 files changed

+83
-49
lines changed

Aztec/Classes/Converters/HTMLNodeToNSAttributedString.swift

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,17 +216,17 @@ private extension HTMLNodeToNSAttributedString {
216216
return attributes
217217
}
218218

219-
let representation = HTMLRepresentation(for: .element(HTMLElementRepresentation(element)))
219+
let elementRepresentation = HTMLElementRepresentation(element)
220+
let representation = HTMLRepresentation(for: .element(elementRepresentation))
220221
var finalAttributes = attributes
221222

222223
if let elementFormatter = formatter(for: element) {
223224
finalAttributes = elementFormatter.apply(to: finalAttributes, andStore: representation)
224-
} else if element.name == StandardElementType.li.rawValue {
225+
} else if element.name == StandardElementType.li.rawValue {
225226
// ^ Since LI is handled by the OL and UL formatters, we can safely ignore it here.
226-
227227
finalAttributes = attributes
228228
} else {
229-
finalAttributes = self.attributes(storing: element, in: finalAttributes)
229+
finalAttributes = self.attributes(storing: elementRepresentation, in: finalAttributes)
230230
}
231231

232232
for attribute in element.attributes {
@@ -270,12 +270,17 @@ private extension HTMLNodeToNSAttributedString {
270270
///
271271
/// - Returns: A collection of NSAttributedString Attributes, including the specified HTMLElementRepresentation.
272272
///
273-
private func attributes(storing element: ElementNode, in attributes: [String: Any]) -> [String: Any] {
274-
let unsupportedHTML = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML ?? UnsupportedHTML()
275-
unsupportedHTML.append(element: element)
276-
273+
private func attributes(storing representation: HTMLElementRepresentation, in attributes: [String: Any]) -> [String: Any] {
274+
let unsupportedHTML = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML
275+
var representations = unsupportedHTML?.representations ?? []
276+
representations.append(representation)
277+
278+
// Note:
279+
// We'll *ALWAYS* store a copy of the UnsupportedHTML instance. Reason is: reusing the old instance
280+
// would mean affecting a range that may fall beyond what we expected!
281+
//
277282
var updated = attributes
278-
updated[UnsupportedHTMLAttributeName] = unsupportedHTML
283+
updated[UnsupportedHTMLAttributeName] = UnsupportedHTML(representations: representations)
279284

280285
return updated
281286
}

Aztec/Classes/Converters/NSAttributedStringToNodes.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -685,11 +685,13 @@ private extension NSAttributedStringToNodes {
685685
/// Extracts all of the Unsupported HTML Snippets contained within a collection of Attributes.
686686
///
687687
private func processUnsupportedHTML(in attributes: [String: Any]) -> [ElementNode] {
688-
guard let unsupported = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML else {
688+
guard let unsupportedHTML = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML else {
689689
return []
690690
}
691691

692-
return unsupported.elements
692+
return unsupportedHTML.representations.map { representation in
693+
return representation.toElementNode()
694+
}
693695
}
694696
}
695697

Aztec/Classes/NSAttributedString/Styles/UnsupportedHTML.swift

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,24 @@ let UnsupportedHTMLAttributeName = "UnsupportedHTMLAttributeName"
1010
//
1111
class UnsupportedHTML: NSObject {
1212

13-
/// HTML Snippets not (natively) supported by the Editor (which will be re-serialized!!)
13+
/// ElementRepresentation for Unsupported HTML
1414
///
15-
private(set) var snippets = [String]()
15+
let representations: [HTMLElementRepresentation]
1616

17-
/// HTML Snippets not supported, converted back to their ElementNode representations
17+
/// Default Initializer
1818
///
19-
var elements: [ElementNode] {
20-
let converter = InHTMLConverter()
21-
22-
return snippets.flatMap { snippet in
23-
// Strip the Root Node(s): Always return the first child element
24-
let root = converter.convert(snippet)
25-
return root.children.first as? ElementNode
26-
}
19+
init(representations: [HTMLElementRepresentation]) {
20+
self.representations = representations
2721
}
2822

2923
/// Required Initializers
3024
///
31-
public required convenience init?(coder aDecoder: NSCoder) {
32-
self.init()
33-
self.snippets = aDecoder.decodeObject(forKey: Keys.elements) as? [String] ?? []
34-
}
25+
public required init?(coder aDecoder: NSCoder) {
26+
guard let representations = aDecoder.decodeObject(forKey: Keys.representations) as? [HTMLElementRepresentation] else {
27+
return nil
28+
}
3529

36-
/// Appends the specified Element Representation
37-
///
38-
func append(element: ElementNode) {
39-
let snippet = OutHTMLConverter().convert(element)
40-
snippets.append(snippet)
30+
self.representations = representations
4131
}
4232
}
4333

@@ -47,10 +37,10 @@ class UnsupportedHTML: NSObject {
4737
extension UnsupportedHTML: NSCoding {
4838

4939
struct Keys {
50-
static let elements = "elements"
40+
static let representations = "representations"
5141
}
5242

5343
open func encode(with aCoder: NSCoder) {
54-
aCoder.encode(snippets, forKey: Keys.elements)
44+
aCoder.encode(representations, forKey: Keys.representations)
5545
}
5646
}

AztecTests/Converters/HTMLNodeToNSAttributedStringTests.swift

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,26 @@ class HTMLNodeToNSAttributedStringTests: XCTestCase {
3333
return
3434
}
3535

36+
let representations = unsupportedHTML.representations
3637
XCTAssert(range.length == textNode.length())
37-
XCTAssert(unsupportedHTML.elements.count == 2)
38+
XCTAssert(representations.count == 2)
3839

39-
let restoredSpanElement2 = unsupportedHTML.elements.last
40+
let restoredSpanElement2 = representations.last
4041
XCTAssertEqual(restoredSpanElement2?.name, "span")
4142

4243
let restoredSpanAttribute2 = restoredSpanElement2?.attributes.first
4344
XCTAssertEqual(restoredSpanAttribute2?.name, "class")
4445
XCTAssertEqual(restoredSpanAttribute2?.value.toString(), "aztec")
4546

46-
let restoredSpanElement1 = unsupportedHTML.elements.first
47+
let restoredSpanElement1 = representations.first
4748
XCTAssertEqual(restoredSpanElement1?.name, "span")
4849

4950
let restoredSpanAttribute1 = restoredSpanElement1?.attributes.first
5051
XCTAssertEqual(restoredSpanAttribute1?.name, "class")
5152
XCTAssertEqual(restoredSpanAttribute1?.value.toString(), "first")
5253
}
5354

55+
5456
/// Verifies that the DivFormatter effectively appends the DIV Element Representation, to the properties collection.
5557
///
5658
func testHtmlDivFormatterEffectivelyAppendsNewDivProperty() {
@@ -95,6 +97,41 @@ class HTMLNodeToNSAttributedStringTests: XCTestCase {
9597
XCTAssert(restoredDiv3.name == divNode3.name)
9698
XCTAssert(restoredDiv3.attributes == [divAttr3])
9799
}
100+
101+
102+
/// Verifies that BR elements contained within div tags do not cause any side effect.
103+
/// Ref. #658
104+
///
105+
func testLineBreakTagWithinHTMLDivGetsProperlyEncodedAndDecoded() {
106+
let inHtml = "<div><br>Aztec, don't forget me!</div>"
107+
108+
let inNode = InHTMLConverter().convert(inHtml)
109+
let attrString = attributedString(from: inNode)
110+
111+
let outNode = NSAttributedStringToNodes().convert(attrString)
112+
let outHtml = OutHTMLConverter().convert(outNode)
113+
114+
XCTAssertEqual(outHtml, inHtml)
115+
}
116+
117+
118+
/// Verifies that BR elements contained within span tags do not cause Data Loss.
119+
/// Ref. #658
120+
///
121+
func testLineBreakTagWithinUnsupportedHTMLDoesNotCauseDataLoss() {
122+
let inHtml = "<span><br>Aztec, don't forget me!</span>"
123+
let expectedHtml = "<br><span>Aztec, don't forget me!</span>"
124+
// TODO: The actual expected html should wrap the BR within a span tag. To be addressed in another PR!
125+
// let expectedHtml = "<span><br></span><span>Aztec, don't forget me!</span>"
126+
127+
let inNode = InHTMLConverter().convert(inHtml)
128+
let attrString = attributedString(from: inNode)
129+
130+
let outNode = NSAttributedStringToNodes().convert(attrString)
131+
let outHtml = OutHTMLConverter().convert(outNode)
132+
133+
XCTAssertEqual(outHtml, expectedHtml)
134+
}
98135
}
99136

100137

@@ -103,7 +140,7 @@ class HTMLNodeToNSAttributedStringTests: XCTestCase {
103140
extension HTMLNodeToNSAttributedStringTests {
104141

105142
func attributedString(from node: Node) -> NSAttributedString {
106-
let descriptor = UIFont.boldSystemFont(ofSize: 14).fontDescriptor
143+
let descriptor = UIFont.systemFont(ofSize: 14).fontDescriptor
107144
let converter = HTMLNodeToNSAttributedString(usingDefaultFontDescriptor: descriptor)
108145

109146
return converter.convert(node)

AztecTests/Converters/NSAttributedStringToNodesTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,11 +615,11 @@ class NSAttributedStringToNodesTests: XCTestCase {
615615
let testingString = NSMutableAttributedString(string: text)
616616

617617
let spanElement = ElementNode(type: .span)
618+
let representation = HTMLElementRepresentation(spanElement)
618619

619-
let unsupported = UnsupportedHTML()
620-
unsupported.append(element: spanElement)
621-
622-
testingString.addAttribute(UnsupportedHTMLAttributeName, value: unsupported, range: testingString.rangeOfEntireString)
620+
// Store
621+
let unsupportedHTML = UnsupportedHTML(representations: [representation])
622+
testingString.addAttribute(UnsupportedHTMLAttributeName, value: unsupportedHTML, range: testingString.rangeOfEntireString)
623623

624624
// Convert + Verify
625625
let node = NSAttributedStringToNodes().convert(testingString)

AztecTests/TextKit/UnsupportedHTMLTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,18 @@ class UnsupportedHTMLTests: XCTestCase {
99
/// Verifies that a UnsupportedHTML Instance can get properly serialized back and forth
1010
///
1111
func testSnippetsGetProperlyEncodedAndDecoded() {
12-
let unsupported = UnsupportedHTML()
13-
unsupported.append(element: sampleElement)
14-
unsupported.append(element: sampleElement)
12+
let unsupported = UnsupportedHTML(representations: [sampleRepresentation, sampleRepresentation])
1513

1614
let data = NSKeyedArchiver.archivedData(withRootObject: unsupported)
1715
guard let restored = NSKeyedUnarchiver.unarchiveObject(with: data) as? UnsupportedHTML else {
1816
XCTFail()
1917
return
2018
}
2119

22-
let elements = restored.elements
23-
XCTAssert(elements.count == 2)
20+
XCTAssert(restored.representations.count == 2)
2421

25-
for element in elements {
26-
XCTAssert(element.children == sampleChildren)
27-
XCTAssert(element.attributes == sampleAttributes)
22+
for representation in restored.representations {
23+
XCTAssert(representation == sampleRepresentation)
2824
}
2925
}
3026
}
@@ -55,4 +51,8 @@ private extension UnsupportedHTMLTests {
5551
var sampleElement: ElementNode {
5652
return ElementNode(name: "Test", attributes: self.sampleAttributes, children: self.sampleChildren)
5753
}
54+
55+
var sampleRepresentation: HTMLElementRepresentation {
56+
return HTMLElementRepresentation(self.sampleElement)
57+
}
5858
}

0 commit comments

Comments
 (0)