Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /*
2 * Copyright (c) 2007 Henri Sivonen
3 * Copyright (c) 2008-2011 Mozilla Foundation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
24 package nu.validator.htmlparser.impl;
26 import nu.validator.htmlparser.annotation.Auto;
27 import nu.validator.htmlparser.annotation.IdType;
28 import nu.validator.htmlparser.annotation.Local;
29 import nu.validator.htmlparser.annotation.NsUri;
30 import nu.validator.htmlparser.annotation.Prefix;
31 import nu.validator.htmlparser.annotation.QName;
32 import nu.validator.htmlparser.common.Interner;
33 import nu.validator.htmlparser.common.XmlViolationPolicy;
35 import org.xml.sax.Attributes;
36 import org.xml.sax.SAXException;
38 /**
39 * Be careful with this class. QName is the name in from HTML tokenization.
40 * Otherwise, please refer to the interface doc.
41 *
42 * @version $Id: AttributesImpl.java 206 2008-03-20 14:09:29Z hsivonen $
43 * @author hsivonen
44 */
45 public final class HtmlAttributes implements Attributes {
47 // [NOCPP[
49 private static final AttributeName[] EMPTY_ATTRIBUTENAMES = new AttributeName[0];
51 private static final String[] EMPTY_STRINGS = new String[0];
53 // ]NOCPP]
55 public static final HtmlAttributes EMPTY_ATTRIBUTES = new HtmlAttributes(
56 AttributeName.HTML);
58 private int mode;
60 private int length;
62 private @Auto AttributeName[] names;
64 private @Auto String[] values; // XXX perhaps make this @NoLength?
66 // [NOCPP[
68 private String idValue;
70 private int xmlnsLength;
72 private AttributeName[] xmlnsNames;
74 private String[] xmlnsValues;
76 // ]NOCPP]
78 public HtmlAttributes(int mode) {
79 this.mode = mode;
80 this.length = 0;
81 /*
82 * The length of 5 covers covers 98.3% of elements
83 * according to Hixie
84 */
85 this.names = new AttributeName[5];
86 this.values = new String[5];
88 // [NOCPP[
90 this.idValue = null;
92 this.xmlnsLength = 0;
94 this.xmlnsNames = HtmlAttributes.EMPTY_ATTRIBUTENAMES;
96 this.xmlnsValues = HtmlAttributes.EMPTY_STRINGS;
98 // ]NOCPP]
99 }
100 /*
101 public HtmlAttributes(HtmlAttributes other) {
102 this.mode = other.mode;
103 this.length = other.length;
104 this.names = new AttributeName[other.length];
105 this.values = new String[other.length];
106 // [NOCPP[
107 this.idValue = other.idValue;
108 this.xmlnsLength = other.xmlnsLength;
109 this.xmlnsNames = new AttributeName[other.xmlnsLength];
110 this.xmlnsValues = new String[other.xmlnsLength];
111 // ]NOCPP]
112 }
113 */
115 void destructor() {
116 clear(0);
117 }
119 /**
120 * Only use with a static argument
121 *
122 * @param name
123 * @return
124 */
125 public int getIndex(AttributeName name) {
126 for (int i = 0; i < length; i++) {
127 if (names[i] == name) {
128 return i;
129 }
130 }
131 return -1;
132 }
134 /**
135 * Only use with static argument.
136 *
137 * @see org.xml.sax.Attributes#getValue(java.lang.String)
138 */
139 public String getValue(AttributeName name) {
140 int index = getIndex(name);
141 if (index == -1) {
142 return null;
143 } else {
144 return getValueNoBoundsCheck(index);
145 }
146 }
148 public int getLength() {
149 return length;
150 }
152 /**
153 * Variant of <code>getLocalName(int index)</code> without bounds check.
154 * @param index a valid attribute index
155 * @return the local name at index
156 */
157 public @Local String getLocalNameNoBoundsCheck(int index) {
158 // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
159 return names[index].getLocal(mode);
160 }
162 /**
163 * Variant of <code>getURI(int index)</code> without bounds check.
164 * @param index a valid attribute index
165 * @return the namespace URI at index
166 */
167 public @NsUri String getURINoBoundsCheck(int index) {
168 // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
169 return names[index].getUri(mode);
170 }
172 /**
173 * Variant of <code>getPrefix(int index)</code> without bounds check.
174 * @param index a valid attribute index
175 * @return the namespace prefix at index
176 */
177 public @Prefix String getPrefixNoBoundsCheck(int index) {
178 // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
179 return names[index].getPrefix(mode);
180 }
182 /**
183 * Variant of <code>getValue(int index)</code> without bounds check.
184 * @param index a valid attribute index
185 * @return the attribute value at index
186 */
187 public String getValueNoBoundsCheck(int index) {
188 // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
189 return values[index];
190 }
192 /**
193 * Variant of <code>getAttributeName(int index)</code> without bounds check.
194 * @param index a valid attribute index
195 * @return the attribute name at index
196 */
197 public AttributeName getAttributeNameNoBoundsCheck(int index) {
198 // CPPONLY: assert index < length && index >= 0: "Index out of bounds";
199 return names[index];
200 }
202 // [NOCPP[
204 /**
205 * Variant of <code>getQName(int index)</code> without bounds check.
206 * @param index a valid attribute index
207 * @return the QName at index
208 */
209 public @QName String getQNameNoBoundsCheck(int index) {
210 return names[index].getQName(mode);
211 }
213 /**
214 * Variant of <code>getType(int index)</code> without bounds check.
215 * @param index a valid attribute index
216 * @return the attribute type at index
217 */
218 public @IdType String getTypeNoBoundsCheck(int index) {
219 return (names[index] == AttributeName.ID) ? "ID" : "CDATA";
220 }
222 public int getIndex(String qName) {
223 for (int i = 0; i < length; i++) {
224 if (names[i].getQName(mode).equals(qName)) {
225 return i;
226 }
227 }
228 return -1;
229 }
231 public int getIndex(String uri, String localName) {
232 for (int i = 0; i < length; i++) {
233 if (names[i].getLocal(mode).equals(localName)
234 && names[i].getUri(mode).equals(uri)) {
235 return i;
236 }
237 }
238 return -1;
239 }
241 public @IdType String getType(String qName) {
242 int index = getIndex(qName);
243 if (index == -1) {
244 return null;
245 } else {
246 return getType(index);
247 }
248 }
250 public @IdType String getType(String uri, String localName) {
251 int index = getIndex(uri, localName);
252 if (index == -1) {
253 return null;
254 } else {
255 return getType(index);
256 }
257 }
259 public String getValue(String qName) {
260 int index = getIndex(qName);
261 if (index == -1) {
262 return null;
263 } else {
264 return getValue(index);
265 }
266 }
268 public String getValue(String uri, String localName) {
269 int index = getIndex(uri, localName);
270 if (index == -1) {
271 return null;
272 } else {
273 return getValue(index);
274 }
275 }
277 public @Local String getLocalName(int index) {
278 if (index < length && index >= 0) {
279 return names[index].getLocal(mode);
280 } else {
281 return null;
282 }
283 }
285 public @QName String getQName(int index) {
286 if (index < length && index >= 0) {
287 return names[index].getQName(mode);
288 } else {
289 return null;
290 }
291 }
293 public @IdType String getType(int index) {
294 if (index < length && index >= 0) {
295 return (names[index] == AttributeName.ID) ? "ID" : "CDATA";
296 } else {
297 return null;
298 }
299 }
301 public AttributeName getAttributeName(int index) {
302 if (index < length && index >= 0) {
303 return names[index];
304 } else {
305 return null;
306 }
307 }
309 public @NsUri String getURI(int index) {
310 if (index < length && index >= 0) {
311 return names[index].getUri(mode);
312 } else {
313 return null;
314 }
315 }
317 public @Prefix String getPrefix(int index) {
318 if (index < length && index >= 0) {
319 return names[index].getPrefix(mode);
320 } else {
321 return null;
322 }
323 }
325 public String getValue(int index) {
326 if (index < length && index >= 0) {
327 return values[index];
328 } else {
329 return null;
330 }
331 }
333 public String getId() {
334 return idValue;
335 }
337 public int getXmlnsLength() {
338 return xmlnsLength;
339 }
341 public @Local String getXmlnsLocalName(int index) {
342 if (index < xmlnsLength && index >= 0) {
343 return xmlnsNames[index].getLocal(mode);
344 } else {
345 return null;
346 }
347 }
349 public @NsUri String getXmlnsURI(int index) {
350 if (index < xmlnsLength && index >= 0) {
351 return xmlnsNames[index].getUri(mode);
352 } else {
353 return null;
354 }
355 }
357 public String getXmlnsValue(int index) {
358 if (index < xmlnsLength && index >= 0) {
359 return xmlnsValues[index];
360 } else {
361 return null;
362 }
363 }
365 public int getXmlnsIndex(AttributeName name) {
366 for (int i = 0; i < xmlnsLength; i++) {
367 if (xmlnsNames[i] == name) {
368 return i;
369 }
370 }
371 return -1;
372 }
374 public String getXmlnsValue(AttributeName name) {
375 int index = getXmlnsIndex(name);
376 if (index == -1) {
377 return null;
378 } else {
379 return getXmlnsValue(index);
380 }
381 }
383 public AttributeName getXmlnsAttributeName(int index) {
384 if (index < xmlnsLength && index >= 0) {
385 return xmlnsNames[index];
386 } else {
387 return null;
388 }
389 }
391 // ]NOCPP]
393 void addAttribute(AttributeName name, String value
394 // [NOCPP[
395 , XmlViolationPolicy xmlnsPolicy
396 // ]NOCPP]
397 ) throws SAXException {
398 // [NOCPP[
399 if (name == AttributeName.ID) {
400 idValue = value;
401 }
403 if (name.isXmlns()) {
404 if (xmlnsNames.length == xmlnsLength) {
405 int newLen = xmlnsLength == 0 ? 2 : xmlnsLength << 1;
406 AttributeName[] newNames = new AttributeName[newLen];
407 System.arraycopy(xmlnsNames, 0, newNames, 0, xmlnsNames.length);
408 xmlnsNames = newNames;
409 String[] newValues = new String[newLen];
410 System.arraycopy(xmlnsValues, 0, newValues, 0, xmlnsValues.length);
411 xmlnsValues = newValues;
412 }
413 xmlnsNames[xmlnsLength] = name;
414 xmlnsValues[xmlnsLength] = value;
415 xmlnsLength++;
416 switch (xmlnsPolicy) {
417 case FATAL:
418 // this is ugly
419 throw new SAXException("Saw an xmlns attribute.");
420 case ALTER_INFOSET:
421 return;
422 case ALLOW:
423 // fall through
424 }
425 }
427 // ]NOCPP]
429 if (names.length == length) {
430 int newLen = length << 1; // The first growth covers virtually
431 // 100% of elements according to
432 // Hixie
433 AttributeName[] newNames = new AttributeName[newLen];
434 System.arraycopy(names, 0, newNames, 0, names.length);
435 names = newNames;
436 String[] newValues = new String[newLen];
437 System.arraycopy(values, 0, newValues, 0, values.length);
438 values = newValues;
439 }
440 names[length] = name;
441 values[length] = value;
442 length++;
443 }
445 void clear(int m) {
446 for (int i = 0; i < length; i++) {
447 names[i].release();
448 names[i] = null;
449 Portability.releaseString(values[i]);
450 values[i] = null;
451 }
452 length = 0;
453 mode = m;
454 // [NOCPP[
455 idValue = null;
456 for (int i = 0; i < xmlnsLength; i++) {
457 xmlnsNames[i] = null;
458 xmlnsValues[i] = null;
459 }
460 xmlnsLength = 0;
461 // ]NOCPP]
462 }
464 /**
465 * This is used in C++ to release special <code>isindex</code>
466 * attribute values whose ownership is not transferred.
467 */
468 void releaseValue(int i) {
469 Portability.releaseString(values[i]);
470 }
472 /**
473 * This is only used for <code>AttributeName</code> ownership transfer
474 * in the isindex case to avoid freeing custom names twice in C++.
475 */
476 void clearWithoutReleasingContents() {
477 for (int i = 0; i < length; i++) {
478 names[i] = null;
479 values[i] = null;
480 }
481 length = 0;
482 }
484 boolean contains(AttributeName name) {
485 for (int i = 0; i < length; i++) {
486 if (name.equalsAnother(names[i])) {
487 return true;
488 }
489 }
490 // [NOCPP[
491 for (int i = 0; i < xmlnsLength; i++) {
492 if (name.equalsAnother(xmlnsNames[i])) {
493 return true;
494 }
495 }
496 // ]NOCPP]
497 return false;
498 }
500 public void adjustForMath() {
501 mode = AttributeName.MATHML;
502 }
504 public void adjustForSvg() {
505 mode = AttributeName.SVG;
506 }
508 public HtmlAttributes cloneAttributes(Interner interner)
509 throws SAXException {
510 assert (length == 0
511 // [NOCPP[
512 && xmlnsLength == 0
513 // ]NOCPP]
514 )
515 || mode == 0 || mode == 3;
516 HtmlAttributes clone = new HtmlAttributes(0);
517 for (int i = 0; i < length; i++) {
518 clone.addAttribute(names[i].cloneAttributeName(interner),
519 Portability.newStringFromString(values[i])
520 // [NOCPP[
521 , XmlViolationPolicy.ALLOW
522 // ]NOCPP]
523 );
524 }
525 // [NOCPP[
526 for (int i = 0; i < xmlnsLength; i++) {
527 clone.addAttribute(xmlnsNames[i], xmlnsValues[i],
528 XmlViolationPolicy.ALLOW);
529 }
530 // ]NOCPP]
531 return clone; // XXX!!!
532 }
534 public boolean equalsAnother(HtmlAttributes other) {
535 assert mode == 0 || mode == 3 : "Trying to compare attributes in foreign content.";
536 int otherLength = other.getLength();
537 if (length != otherLength) {
538 return false;
539 }
540 for (int i = 0; i < length; i++) {
541 // Work around the limitations of C++
542 boolean found = false;
543 // The comparing just the local names is OK, since these attribute
544 // holders are both supposed to belong to HTML formatting elements
545 @Local String ownLocal = names[i].getLocal(AttributeName.HTML);
546 for (int j = 0; j < otherLength; j++) {
547 if (ownLocal == other.names[j].getLocal(AttributeName.HTML)) {
548 found = true;
549 if (!Portability.stringEqualsString(values[i], other.values[j])) {
550 return false;
551 }
552 }
553 }
554 if (!found) {
555 return false;
556 }
557 }
558 return true;
559 }
561 // [NOCPP[
563 void processNonNcNames(TreeBuilder<?> treeBuilder, XmlViolationPolicy namePolicy) throws SAXException {
564 for (int i = 0; i < length; i++) {
565 AttributeName attName = names[i];
566 if (!attName.isNcName(mode)) {
567 String name = attName.getLocal(mode);
568 switch (namePolicy) {
569 case ALTER_INFOSET:
570 names[i] = AttributeName.create(NCName.escapeName(name));
571 // fall through
572 case ALLOW:
573 if (attName != AttributeName.XML_LANG) {
574 treeBuilder.warn("Attribute \u201C" + name + "\u201D is not serializable as XML 1.0.");
575 }
576 break;
577 case FATAL:
578 treeBuilder.fatal("Attribute \u201C" + name + "\u201D is not serializable as XML 1.0.");
579 break;
580 }
581 }
582 }
583 }
585 public void merge(HtmlAttributes attributes) throws SAXException {
586 int len = attributes.getLength();
587 for (int i = 0; i < len; i++) {
588 AttributeName name = attributes.getAttributeNameNoBoundsCheck(i);
589 if (!contains(name)) {
590 addAttribute(name, attributes.getValueNoBoundsCheck(i), XmlViolationPolicy.ALLOW);
591 }
592 }
593 }
596 // ]NOCPP]
598 }