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

Side by Side Diff: pkg/third_party/html5lib/lib/src/treebuilder.dart

Issue 178303009: [html5lib] api updates: localName (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 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 | Annotate | Revision Log
OLDNEW
1 /// Internals to the tree builders. 1 /// Internals to the tree builders.
2 library treebuilder; 2 library treebuilder;
3 3
4 import 'dart:collection'; 4 import 'dart:collection';
5 import 'package:html5lib/dom.dart'; 5 import 'package:html5lib/dom.dart';
6 import 'package:html5lib/parser.dart' show getElementNameTuple;
6 import 'package:source_maps/span.dart' show FileSpan; 7 import 'package:source_maps/span.dart' show FileSpan;
7 import 'constants.dart'; 8 import 'constants.dart';
8 import 'list_proxy.dart'; 9 import 'list_proxy.dart';
9 import 'token.dart'; 10 import 'token.dart';
10 import 'utils.dart'; 11 import 'utils.dart';
11 12
12 // The scope markers are inserted when entering object elements, 13 // The scope markers are inserted when entering object elements,
13 // marquees, table cells, and table captions, and are used to prevent formatting 14 // marquees, table cells, and table captions, and are used to prevent formatting
14 // from "leaking" into tables, object elements, and marquees. 15 // from "leaking" into tables, object elements, and marquees.
15 const Node Marker = null; 16 const Node Marker = null;
16 17
17 // TODO(jmesserly): this should extend ListBase<Node>, but my simple attempt 18 // TODO(jmesserly): this should extend ListBase<Element>, but my simple attempt
18 // didn't work. 19 // didn't work.
19 class ActiveFormattingElements extends ListProxy<Node> { 20 class ActiveFormattingElements extends ListProxy<Element> {
20 ActiveFormattingElements() : super(); 21 ActiveFormattingElements() : super();
21 22
22 // Override the "add" method. 23 // Override the "add" method.
23 // TODO(jmesserly): I'd rather not override this; can we do this in the 24 // TODO(jmesserly): I'd rather not override this; can we do this in the
24 // calling code instead? 25 // calling code instead?
25 void add(Node node) { 26 void add(Element node) {
26 int equalCount = 0; 27 int equalCount = 0;
27 if (node != Marker) { 28 if (node != Marker) {
28 for (Node element in reversed) { 29 for (var element in reversed) {
29 if (element == Marker) { 30 if (element == Marker) {
30 break; 31 break;
31 } 32 }
32 if (_nodesEqual(element, node)) { 33 if (_nodesEqual(element, node)) {
33 equalCount += 1; 34 equalCount += 1;
34 } 35 }
35 if (equalCount == 3) { 36 if (equalCount == 3) {
36 remove(element); 37 remove(element);
37 break; 38 break;
38 } 39 }
(...skipping 15 matching lines...) Expand all
54 } 55 }
55 56
56 if (a[keyA] != valB) { 57 if (a[keyA] != valB) {
57 return false; 58 return false;
58 } 59 }
59 } 60 }
60 return true; 61 return true;
61 } 62 }
62 63
63 64
64 bool _nodesEqual(Node node1, Node node2) { 65 bool _nodesEqual(Element node1, Element node2) {
65 return node1.nameTuple == node2.nameTuple && 66 return getElementNameTuple(node1) == getElementNameTuple(node2) &&
66 _mapEquals(node1.attributes, node2.attributes); 67 _mapEquals(node1.attributes, node2.attributes);
67 } 68 }
68 69
69 /// Basic treebuilder implementation. 70 /// Basic treebuilder implementation.
70 class TreeBuilder { 71 class TreeBuilder {
71 final String defaultNamespace; 72 final String defaultNamespace;
72 73
73 Document document; 74 Document document;
74 75
75 final openElements = <Node>[]; 76 final List<Element> openElements = <Element>[];
76 77
77 final activeFormattingElements = new ActiveFormattingElements(); 78 final activeFormattingElements = new ActiveFormattingElements();
78 79
79 Node headPointer; 80 Node headPointer;
80 81
81 Node formPointer; 82 Node formPointer;
82 83
83 /// Switch the function used to insert an element from the 84 /// Switch the function used to insert an element from the
84 /// normal one to the misnested table one and back again 85 /// normal one to the misnested table one and back again
85 bool insertFromTable; 86 bool insertFromTable;
(...skipping 12 matching lines...) Expand all
98 formPointer = null; 99 formPointer = null;
99 100
100 insertFromTable = false; 101 insertFromTable = false;
101 102
102 document = new Document(); 103 document = new Document();
103 } 104 }
104 105
105 bool elementInScope(target, {String variant}) { 106 bool elementInScope(target, {String variant}) {
106 //If we pass a node in we match that. if we pass a string 107 //If we pass a node in we match that. if we pass a string
107 //match any node with that name 108 //match any node with that name
108 bool exactNode = target is Node && target.nameTuple != null; 109 bool exactNode = target is Node;
109 110
110 List listElements1 = scopingElements; 111 List listElements1 = scopingElements;
111 List listElements2 = const []; 112 List listElements2 = const [];
112 bool invert = false; 113 bool invert = false;
113 if (variant != null) { 114 if (variant != null) {
114 switch (variant) { 115 switch (variant) {
115 case "button": 116 case "button":
116 listElements2 = const [const Pair(Namespaces.html, "button")]; 117 listElements2 = const [const Pair(Namespaces.html, "button")];
117 break; 118 break;
118 case "list": 119 case "list":
119 listElements2 = const [const Pair(Namespaces.html, "ol"), 120 listElements2 = const [const Pair(Namespaces.html, "ol"),
120 const Pair(Namespaces.html, "ul")]; 121 const Pair(Namespaces.html, "ul")];
121 break; 122 break;
122 case "table": 123 case "table":
123 listElements1 = const [const Pair(Namespaces.html, "html"), 124 listElements1 = const [const Pair(Namespaces.html, "html"),
124 const Pair(Namespaces.html, "table")]; 125 const Pair(Namespaces.html, "table")];
125 break; 126 break;
126 case "select": 127 case "select":
127 listElements1 = const [const Pair(Namespaces.html, "optgroup"), 128 listElements1 = const [const Pair(Namespaces.html, "optgroup"),
128 const Pair(Namespaces.html, "option")]; 129 const Pair(Namespaces.html, "option")];
129 invert = true; 130 invert = true;
130 break; 131 break;
131 default: 132 default:
132 throw new StateError('We should never reach this point'); 133 throw new StateError('We should never reach this point');
133 } 134 }
134 } 135 }
135 136
136 for (Node node in openElements.reversed) { 137 for (var node in openElements.reversed) {
137 if (node.tagName == target && !exactNode || 138 if (!exactNode && node.localName == target ||
138 node == target && exactNode) { 139 exactNode && node == target) {
139 return true; 140 return true;
140 } else if (invert != 141 } else if (invert !=
141 (listElements1.contains(node.nameTuple) || 142 (listElements1.contains(getElementNameTuple(node)) ||
142 listElements2.contains(node.nameTuple))) { 143 listElements2.contains(getElementNameTuple(node)))) {
143 return false; 144 return false;
144 } 145 }
145 } 146 }
146 147
147 throw new StateError('We should never reach this point'); 148 throw new StateError('We should never reach this point');
148 } 149 }
149 150
150 void reconstructActiveFormattingElements() { 151 void reconstructActiveFormattingElements() {
151 // Within this algorithm the order of steps described in the 152 // Within this algorithm the order of steps described in the
152 // specification is not quite the same as the order of steps in the 153 // specification is not quite the same as the order of steps in the
(...skipping 25 matching lines...) Expand all
178 179
179 while (true) { 180 while (true) {
180 // Step 7 181 // Step 7
181 i += 1; 182 i += 1;
182 183
183 // Step 8 184 // Step 8
184 entry = activeFormattingElements[i]; 185 entry = activeFormattingElements[i];
185 186
186 // TODO(jmesserly): optimize this. No need to create a token. 187 // TODO(jmesserly): optimize this. No need to create a token.
187 var cloneToken = new StartTagToken( 188 var cloneToken = new StartTagToken(
188 entry.tagName, 189 entry.localName,
189 namespace: entry.namespace, 190 namespace: entry.namespaceUri,
190 data: new LinkedHashMap.from(entry.attributes)) 191 data: new LinkedHashMap.from(entry.attributes))
191 ..span = entry.sourceSpan; 192 ..span = entry.sourceSpan;
192 193
193 // Step 9 194 // Step 9
194 var element = insertElement(cloneToken); 195 var element = insertElement(cloneToken);
195 196
196 // Step 10 197 // Step 10
197 activeFormattingElements[i] = element; 198 activeFormattingElements[i] = element;
198 199
199 // Step 11 200 // Step 11
200 if (element == activeFormattingElements.last) { 201 if (element == activeFormattingElements.last) {
201 break; 202 break;
202 } 203 }
203 } 204 }
204 } 205 }
205 206
206 void clearActiveFormattingElements() { 207 void clearActiveFormattingElements() {
207 var entry = activeFormattingElements.removeLast(); 208 var entry = activeFormattingElements.removeLast();
208 while (activeFormattingElements.length > 0 && entry != Marker) { 209 while (activeFormattingElements.length > 0 && entry != Marker) {
209 entry = activeFormattingElements.removeLast(); 210 entry = activeFormattingElements.removeLast();
210 } 211 }
211 } 212 }
212 213
213 /// Check if an element exists between the end of the active 214 /// Check if an element exists between the end of the active
214 /// formatting elements and the last marker. If it does, return it, else 215 /// formatting elements and the last marker. If it does, return it, else
215 /// return null. 216 /// return null.
216 Node elementInActiveFormattingElements(String name) { 217 Element elementInActiveFormattingElements(String name) {
217 for (Node item in activeFormattingElements.reversed) { 218 for (var item in activeFormattingElements.reversed) {
218 // Check for Marker first because if it's a Marker it doesn't have a 219 // Check for Marker first because if it's a Marker it doesn't have a
219 // name attribute. 220 // name attribute.
220 if (item == Marker) { 221 if (item == Marker) {
221 break; 222 break;
222 } else if (item.tagName == name) { 223 } else if (item.localName == name) {
223 return item; 224 return item;
224 } 225 }
225 } 226 }
226 return null; 227 return null;
227 } 228 }
228 229
229 void insertRoot(Token token) { 230 void insertRoot(Token token) {
230 var element = createElement(token); 231 var element = createElement(token);
231 openElements.add(element); 232 openElements.add(element);
232 document.nodes.add(element); 233 document.nodes.add(element);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 ..attributes = token.data 270 ..attributes = token.data
270 ..sourceSpan = token.span; 271 ..sourceSpan = token.span;
271 openElements.last.nodes.add(element); 272 openElements.last.nodes.add(element);
272 openElements.add(element); 273 openElements.add(element);
273 return element; 274 return element;
274 } 275 }
275 276
276 Element insertElementTable(token) { 277 Element insertElementTable(token) {
277 /// Create an element and insert it into the tree 278 /// Create an element and insert it into the tree
278 var element = createElement(token); 279 var element = createElement(token);
279 if (!tableInsertModeElements.contains(openElements.last.tagName)) { 280 if (!tableInsertModeElements.contains(openElements.last.localName)) {
280 return insertElementNormal(token); 281 return insertElementNormal(token);
281 } else { 282 } else {
282 // We should be in the InTable mode. This means we want to do 283 // We should be in the InTable mode. This means we want to do
283 // special magic element rearranging 284 // special magic element rearranging
284 var nodePos = getTableMisnestedNodePosition(); 285 var nodePos = getTableMisnestedNodePosition();
285 if (nodePos[1] == null) { 286 if (nodePos[1] == null) {
286 // TODO(jmesserly): I don't think this is reachable. If insertFromTable 287 // TODO(jmesserly): I don't think this is reachable. If insertFromTable
287 // is true, there will be a <table> element open, and it always has a 288 // is true, there will be a <table> element open, and it always has a
288 // parent pointer. 289 // parent pointer.
289 nodePos[0].nodes.add(element); 290 nodePos[0].nodes.add(element);
290 } else { 291 } else {
291 nodePos[0].insertBefore(element, nodePos[1]); 292 nodePos[0].insertBefore(element, nodePos[1]);
292 } 293 }
293 openElements.add(element); 294 openElements.add(element);
294 } 295 }
295 return element; 296 return element;
296 } 297 }
297 298
298 /// Insert text data. 299 /// Insert text data.
299 void insertText(String data, FileSpan span) { 300 void insertText(String data, FileSpan span) {
300 var parent = openElements.last; 301 var parent = openElements.last;
301 302
302 if (!insertFromTable || insertFromTable && 303 if (!insertFromTable || insertFromTable &&
303 !tableInsertModeElements.contains(openElements.last.tagName)) { 304 !tableInsertModeElements.contains(openElements.last.localName)) {
304 _insertText(parent, data, span); 305 _insertText(parent, data, span);
305 } else { 306 } else {
306 // We should be in the InTable mode. This means we want to do 307 // We should be in the InTable mode. This means we want to do
307 // special magic element rearranging 308 // special magic element rearranging
308 var nodePos = getTableMisnestedNodePosition(); 309 var nodePos = getTableMisnestedNodePosition();
309 _insertText(nodePos[0], data, span, nodePos[1]); 310 _insertText(nodePos[0], data, span, nodePos[1]);
310 } 311 }
311 } 312 }
312 313
313 /// Insert [data] as text in the current node, positioned before the 314 /// Insert [data] as text in the current node, positioned before the
(...skipping 26 matching lines...) Expand all
340 341
341 /// Get the foster parent element, and sibling to insert before 342 /// Get the foster parent element, and sibling to insert before
342 /// (or null) when inserting a misnested table node 343 /// (or null) when inserting a misnested table node
343 List<Node> getTableMisnestedNodePosition() { 344 List<Node> getTableMisnestedNodePosition() {
344 // The foster parent element is the one which comes before the most 345 // The foster parent element is the one which comes before the most
345 // recently opened table element 346 // recently opened table element
346 // XXX - this is really inelegant 347 // XXX - this is really inelegant
347 Node lastTable = null; 348 Node lastTable = null;
348 Node fosterParent = null; 349 Node fosterParent = null;
349 var insertBefore = null; 350 var insertBefore = null;
350 for (Node elm in openElements.reversed) { 351 for (var elm in openElements.reversed) {
351 if (elm.tagName == "table") { 352 if (elm.localName == "table") {
352 lastTable = elm; 353 lastTable = elm;
353 break; 354 break;
354 } 355 }
355 } 356 }
356 if (lastTable != null) { 357 if (lastTable != null) {
357 // XXX - we should really check that this parent is actually a 358 // XXX - we should really check that this parent is actually a
358 // node here 359 // node here
359 if (lastTable.parent != null) { 360 if (lastTable.parent != null) {
360 fosterParent = lastTable.parent; 361 fosterParent = lastTable.parent;
361 insertBefore = lastTable; 362 insertBefore = lastTable;
362 } else { 363 } else {
363 fosterParent = openElements[openElements.indexOf(lastTable) - 1]; 364 fosterParent = openElements[openElements.indexOf(lastTable) - 1];
364 } 365 }
365 } else { 366 } else {
366 fosterParent = openElements[0]; 367 fosterParent = openElements[0];
367 } 368 }
368 return [fosterParent, insertBefore]; 369 return [fosterParent, insertBefore];
369 } 370 }
370 371
371 void generateImpliedEndTags([String exclude]) { 372 void generateImpliedEndTags([String exclude]) {
372 var name = openElements.last.tagName; 373 var name = openElements.last.localName;
373 // XXX td, th and tr are not actually needed 374 // XXX td, th and tr are not actually needed
374 if (name != exclude && const ["dd", "dt", "li", "option", "optgroup", "p", 375 if (name != exclude && const ["dd", "dt", "li", "option", "optgroup", "p",
375 "rp", "rt"].contains(name)) { 376 "rp", "rt"].contains(name)) {
376 openElements.removeLast(); 377 openElements.removeLast();
377 // XXX This is not entirely what the specification says. We should 378 // XXX This is not entirely what the specification says. We should
378 // investigate it more closely. 379 // investigate it more closely.
379 generateImpliedEndTags(exclude); 380 generateImpliedEndTags(exclude);
380 } 381 }
381 } 382 }
382 383
383 /// Return the final tree. 384 /// Return the final tree.
384 Document getDocument() => document; 385 Document getDocument() => document;
385 386
386 /// Return the final fragment. 387 /// Return the final fragment.
387 DocumentFragment getFragment() { 388 DocumentFragment getFragment() {
388 //XXX assert innerHTML 389 //XXX assert innerHTML
389 var fragment = new DocumentFragment(); 390 var fragment = new DocumentFragment();
390 openElements[0].reparentChildren(fragment); 391 openElements[0].reparentChildren(fragment);
391 return fragment; 392 return fragment;
392 } 393 }
393 } 394 }
OLDNEW
« no previous file with comments | « pkg/third_party/html5lib/lib/src/tokenizer.dart ('k') | pkg/third_party/html5lib/test/parser_feature_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698