Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(896)

Side by Side Diff: treebuilders/base.dart

Issue 10916294: switch html5lib to new pkg layout (Closed) Base URL: https://github.com/dart-lang/html5lib.git@master
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tokenizer.dart ('k') | treebuilders/simpletree.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /** Internals to the tree builders. */
2 #library('base');
3
4 #import('../lib/constants.dart');
5 #import('../lib/list_proxy.dart');
6 #import('../lib/token.dart');
7 #import('../lib/utils.dart');
8 #import('simpletree.dart');
9
10 // The scope markers are inserted when entering object elements,
11 // marquees, table cells, and table captions, and are used to prevent formatting
12 // from "leaking" into tables, object elements, and marquees.
13 final Node Marker = null;
14
15 class ActiveFormattingElements extends ListProxy<Node> {
16 ActiveFormattingElements() : super();
17
18 void addLast(Node node) => add(node);
19 void add(Node node) {
20 int equalCount = 0;
21 if (node != Marker) {
22 for (Node element in reversed(this)) {
23 if (element == Marker) {
24 break;
25 }
26 if (_nodesEqual(element, node)) {
27 equalCount += 1;
28 }
29 if (equalCount == 3) {
30 removeFromList(this, element);
31 break;
32 }
33 }
34 }
35 super.add(node);
36 }
37 }
38
39 // TODO(jmesserly): this should exist in corelib...
40 bool _mapEquals(Map a, Map b) {
41 if (a.length != b.length) return false;
42 if (a.length == 0) return true;
43
44 for (var keyA in a.getKeys()) {
45 var valB = b[keyA];
46 if (valB == null && !b.containsKey(keyA)) {
47 return false;
48 }
49
50 if (a[keyA] != valB) {
51 return false;
52 }
53 }
54 return true;
55 }
56
57
58 bool _nodesEqual(Node node1, Node node2) {
59 return node1.nameTuple == node2.nameTuple &&
60 _mapEquals(node1.attributes, node2.attributes);
61 }
62
63 /** Basic treebuilder implementation. */
64 class TreeBuilder {
65 final String defaultNamespace;
66
67 Document document;
68
69 final List<Node> openElements;
70
71 final ActiveFormattingElements activeFormattingElements;
72
73 Node headPointer;
74
75 Node formPointer;
76
77 /**
78 * Switch the function used to insert an element from the
79 * normal one to the misnested table one and back again
80 */
81 bool insertFromTable;
82
83 TreeBuilder(bool namespaceHTMLElements)
84 : defaultNamespace = namespaceHTMLElements ? Namespaces.html : null,
85 openElements = <Node>[],
86 activeFormattingElements = new ActiveFormattingElements() {
87 reset();
88 }
89
90 void reset() {
91 openElements.clear();
92 activeFormattingElements.clear();
93
94 //XXX - rename these to headElement, formElement
95 headPointer = null;
96 formPointer = null;
97
98 insertFromTable = false;
99
100 document = new Document();
101 }
102
103 bool elementInScope(target, [String variant]) {
104 //If we pass a node in we match that. if we pass a string
105 //match any node with that name
106 bool exactNode = target is Node && target.nameTuple != null;
107
108 List listElements1 = scopingElements;
109 List listElements2 = const [];
110 bool invert = false;
111 if (variant != null) {
112 switch (variant) {
113 case "button":
114 listElements2 = const [const Pair(Namespaces.html, "button")];
115 break;
116 case "list":
117 listElements2 = const [const Pair(Namespaces.html, "ol"),
118 const Pair(Namespaces.html, "ul")];
119 break;
120 case "table":
121 listElements1 = const [const Pair(Namespaces.html, "html"),
122 const Pair(Namespaces.html, "table")];
123 break;
124 case "select":
125 listElements1 = const [const Pair(Namespaces.html, "optgroup"),
126 const Pair(Namespaces.html, "option")];
127 invert = true;
128 break;
129 default: assert(false);
130 }
131 }
132
133 for (Node node in reversed(openElements)) {
134 if (node.tagName == target && !exactNode ||
135 node == target && exactNode) {
136 return true;
137 } else if (invert !=
138 (listElements1.indexOf(node.nameTuple) >= 0 ||
139 listElements2.indexOf(node.nameTuple) >= 0)) {
140 return false;
141 }
142 }
143
144 assert(false); // We should never reach this point
145 }
146
147 void reconstructActiveFormattingElements() {
148 // Within this algorithm the order of steps described in the
149 // specification is not quite the same as the order of steps in the
150 // code. It should still do the same though.
151
152 // Step 1: stop the algorithm when there's nothing to do.
153 if (activeFormattingElements.length == 0) {
154 return;
155 }
156
157 // Step 2 and step 3: we start with the last element. So i is -1.
158 int i = activeFormattingElements.length - 1;
159 var entry = activeFormattingElements[i];
160 if (entry == Marker || openElements.indexOf(entry) >= 0) {
161 return;
162 }
163
164 // Step 6
165 while (entry != Marker && openElements.indexOf(entry) == -1) {
166 if (i == 0) {
167 //This will be reset to 0 below
168 i = -1;
169 break;
170 }
171 i -= 1;
172 // Step 5: let entry be one earlier in the list.
173 entry = activeFormattingElements[i];
174 }
175
176 while (true) {
177 // Step 7
178 i += 1;
179
180 // Step 8
181 entry = activeFormattingElements[i];
182 var clone = entry.clone(); // Mainly to get a new copy of the attributes
183
184 // Step 9
185 var element = insertElement(new StartTagToken(clone.tagName,
186 namespace: clone.namespace, data: clone.attributes));
187
188 // Step 10
189 activeFormattingElements[i] = element;
190
191 // Step 11
192 if (element == activeFormattingElements.last()) {
193 break;
194 }
195 }
196 }
197
198 void clearActiveFormattingElements() {
199 var entry = activeFormattingElements.removeLast();
200 while (activeFormattingElements.length > 0 && entry != Marker) {
201 entry = activeFormattingElements.removeLast();
202 }
203 }
204
205 /**
206 * Check if an element exists between the end of the active
207 * formatting elements and the last marker. If it does, return it, else
208 * return null
209 */
210 Node elementInActiveFormattingElements(String name) {
211 for (Node item in reversed(activeFormattingElements)) {
212 // Check for Marker first because if it's a Marker it doesn't have a
213 // name attribute.
214 if (item == Marker) {
215 break;
216 } else if (item.tagName == name) {
217 return item;
218 }
219 }
220 return null;
221 }
222
223 void insertRoot(Token token) {
224 var element = createElement(token);
225 openElements.add(element);
226 document.$dom_appendChild(element);
227 }
228
229 void insertDoctype(DoctypeToken token) {
230 var doctype = new DocumentType(token.name, token.publicId, token.systemId);
231 document.$dom_appendChild(doctype);
232 }
233
234 void insertComment(Token token, [Node parent]) {
235 if (parent == null) {
236 parent = openElements.last();
237 }
238 parent.$dom_appendChild(new Comment(token.data));
239 }
240
241 /** Create an element but don't insert it anywhere */
242 Element createElement(StartTagToken token) {
243 var name = token.name;
244 var namespace = token.namespace;
245 if (namespace == null) namespace = defaultNamespace;
246 var element = new Element(name, namespace);
247 element.attributes = token.data;
248 return element;
249 }
250
251 Element insertElement(StartTagToken token) {
252 if (insertFromTable) return insertElementTable(token);
253 return insertElementNormal(token);
254 }
255
256 Element insertElementNormal(StartTagToken token) {
257 var name = token.name;
258 var namespace = token.namespace;
259 if (namespace == null) namespace = defaultNamespace;
260 Element element = new Element(name, namespace);
261 element.attributes = token.data;
262 openElements.last().$dom_appendChild(element);
263 openElements.add(element);
264 return element;
265 }
266
267 Element insertElementTable(token) {
268 /** Create an element and insert it into the tree */
269 var element = createElement(token);
270 if (tableInsertModeElements.indexOf(openElements.last().tagName) == -1) {
271 return insertElementNormal(token);
272 } else {
273 // We should be in the InTable mode. This means we want to do
274 // special magic element rearranging
275 var nodePos = getTableMisnestedNodePosition();
276 if (nodePos[1] == null) {
277 nodePos[0].appendChild(element);
278 } else {
279 nodePos[0].insertBefore(element, nodePos[1]);
280 }
281 openElements.add(element);
282 }
283 return element;
284 }
285
286 /** Insert text data. */
287 void insertText(String data, [Node parent]) {
288 if (parent == null) parent = openElements.last();
289
290 if (!insertFromTable || insertFromTable &&
291 tableInsertModeElements.indexOf(openElements.last().tagName) == -1) {
292 parent.insertText(data);
293 } else {
294 // We should be in the InTable mode. This means we want to do
295 // special magic element rearranging
296 var nodePos = getTableMisnestedNodePosition();
297 nodePos[0].insertText(data, nodePos[1]);
298 }
299 }
300
301 /**
302 * Get the foster parent element, and sibling to insert before
303 * (or null) when inserting a misnested table node
304 */
305 List getTableMisnestedNodePosition() {
306 // The foster parent element is the one which comes before the most
307 // recently opened table element
308 // XXX - this is really inelegant
309 var lastTable = null;
310 var fosterParent = null;
311 var insertBefore = null;
312 for (Node elm in reversed(openElements)) {
313 if (elm.tagName == "table") {
314 lastTable = elm;
315 break;
316 }
317 }
318 if (lastTable != null) {
319 // XXX - we should really check that this parent is actually a
320 // node here
321 if (lastTable.parent != null) {
322 fosterParent = lastTable.parent;
323 insertBefore = lastTable;
324 } else {
325 fosterParent = openElements[openElements.indexOf(lastTable) - 1];
326 }
327 } else {
328 fosterParent = openElements[0];
329 }
330 return [fosterParent, insertBefore];
331 }
332
333 void generateImpliedEndTags([String exclude]) {
334 var name = openElements.last().tagName;
335 // XXX td, th and tr are not actually needed
336 if (name != exclude && const ["dd", "dt", "li", "option", "optgroup", "p",
337 "rp", "rt"].indexOf(name) >= 0) {
338 openElements.removeLast();
339 // XXX This is not entirely what the specification says. We should
340 // investigate it more closely.
341 generateImpliedEndTags(exclude);
342 }
343 }
344
345 /** Return the final tree. */
346 Document getDocument() => document;
347
348 /** Return the final fragment. */
349 DocumentFragment getFragment() {
350 //XXX assert innerHTML
351 var fragment = new DocumentFragment();
352 openElements[0].reparentChildren(fragment);
353 return fragment;
354 }
355
356 /**
357 * Serialize the subtree of node in the format required by unit tests
358 * node - the node from which to start serializing
359 */
360 String testSerializer(node) {
361 throw const NotImplementedException();
362 }
363 }
OLDNEW
« no previous file with comments | « tokenizer.dart ('k') | treebuilders/simpletree.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698