1/*
2 * Copyright (C) 2018 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "RenderTreeBuilderMathML.h"
28
29#if ENABLE(MATHML)
30
31#include "RenderMathMLFenced.h"
32#include "RenderMathMLFencedOperator.h"
33#include "RenderTreeBuilderBlock.h"
34
35namespace WebCore {
36
37RenderTreeBuilder::MathML::MathML(RenderTreeBuilder& builder)
38 : m_builder(builder)
39{
40}
41
42RenderPtr<RenderMathMLFencedOperator> RenderTreeBuilder::MathML::createMathMLOperator(RenderMathMLFenced& parent, const String& operatorString,
43 MathMLOperatorDictionary::Form form, MathMLOperatorDictionary::Flag flag)
44{
45 RenderPtr<RenderMathMLFencedOperator> newOperator = createRenderer<RenderMathMLFencedOperator>(parent.document(), RenderStyle::createAnonymousStyleWithDisplay(parent.style(), DisplayType::Block), operatorString, form, flag);
46 newOperator->initializeStyle();
47 return newOperator;
48}
49
50void RenderTreeBuilder::MathML::makeFences(RenderMathMLFenced& parent)
51{
52 auto openFence = createMathMLOperator(parent, parent.openingBrace(), MathMLOperatorDictionary::Prefix, MathMLOperatorDictionary::Fence);
53 m_builder.blockBuilder().attach(parent, WTFMove(openFence), parent.firstChild());
54
55 auto closeFence = createMathMLOperator(parent, parent.closingBrace(), MathMLOperatorDictionary::Postfix, MathMLOperatorDictionary::Fence);
56 parent.setCloseFenceRenderer(*closeFence);
57 m_builder.blockBuilder().attach(parent, WTFMove(closeFence), nullptr);
58}
59
60void RenderTreeBuilder::MathML::attach(RenderMathMLFenced& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
61{
62 // make the fences if the render object is empty
63 if (!parent.firstChild()) {
64 parent.updateFromElement();
65 makeFences(parent);
66 }
67 // FIXME: Adding or removing a child should possibly cause all later separators to shift places
68 // if they're different, as later child positions change by +1 or -1.
69 // This should also handle surrogate pairs. See https://bugs.webkit.org/show_bug.cgi?id=125938.
70 RenderPtr<RenderMathMLFencedOperator> separatorRenderer;
71 auto* separators = parent.separators();
72 if (separators) {
73 unsigned count = 0;
74 for (Node* position = child->node(); position; position = position->previousSibling()) {
75 if (position->isElementNode())
76 count++;
77 }
78 if (!beforeChild) {
79 // We're adding at the end (before the closing fence), so a new separator would go before the new child, not after it.
80 --count;
81 }
82 // |count| is now the number of element children that will be before our new separator, i.e. it's the 1-based index of the separator.
83
84 if (count > 0) {
85 UChar separator;
86
87 // Use the last separator if we've run out of specified separators.
88 if (count > separators->length())
89 separator = (*separators)[separators->length() - 1];
90 else
91 separator = (*separators)[count - 1];
92
93 StringBuilder stringBuilder;
94 stringBuilder.append(separator);
95 separatorRenderer = createMathMLOperator(parent, stringBuilder.toString(), MathMLOperatorDictionary::Infix, MathMLOperatorDictionary::Separator);
96 }
97 }
98
99 if (beforeChild) {
100 // Adding |x| before an existing |y| e.g. in element (y) - first insert our new child |x|, then its separator, to get (x, y).
101 m_builder.blockBuilder().attach(parent, WTFMove(child), beforeChild);
102 if (separatorRenderer)
103 m_builder.blockBuilder().attach(parent, WTFMove(separatorRenderer), beforeChild);
104 } else {
105 // Adding |y| at the end of an existing element e.g. (x) - insert the separator first before the closing fence, then |y|, to get (x, y).
106 if (separatorRenderer)
107 m_builder.blockBuilder().attach(parent, WTFMove(separatorRenderer), parent.closeFenceRenderer());
108 m_builder.blockBuilder().attach(parent, WTFMove(child), parent.closeFenceRenderer());
109 }
110}
111
112}
113
114#endif // ENABLE(MATHML)
115