Changeset 175947 in webkit
- Timestamp:
- Nov 11, 2014, 9:35:29 AM (12 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 17 edited
-
ChangeLog (modified) (1 diff)
-
accessibility/AccessibilityRenderObject.cpp (modified) (1 diff)
-
bindings/js/JSHTMLAllCollectionCustom.cpp (modified) (6 diffs)
-
bindings/js/JSHTMLFormControlsCollectionCustom.cpp (modified) (3 diffs)
-
bindings/js/JSHTMLFormElementCustom.cpp (modified) (1 diff)
-
html/HTMLAllCollection.cpp (modified) (2 diffs)
-
html/HTMLAllCollection.h (modified) (1 diff)
-
html/HTMLCollection.cpp (modified) (17 diffs)
-
html/HTMLCollection.h (modified) (5 diffs)
-
html/HTMLFormControlsCollection.cpp (modified) (3 diffs)
-
html/HTMLFormControlsCollection.h (modified) (2 diffs)
-
html/HTMLFormElement.cpp (modified) (2 diffs)
-
html/HTMLFormElement.h (modified) (1 diff)
-
html/HTMLNameCollection.h (modified) (1 diff)
-
html/HTMLSelectElement.cpp (modified) (1 diff)
-
html/HTMLSelectElement.h (modified) (3 diffs)
-
page/scrolling/AxisScrollSnapOffsets.cpp (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r175941 r175947 1 2014-11-09 Darin Adler <darin@apple.com> 2 3 Minor tweaks to HTMLCollection 4 https://bugs.webkit.org/show_bug.cgi?id=138556 5 6 Reviewed by Chris Dumez. 7 8 * accessibility/AccessibilityRenderObject.cpp: 9 (WebCore::AccessibilityRenderObject::addRadioButtonGroupMembers): 10 Update for namedElements function that now returns a Vector; also use 11 a new style for loop to iterate it. 12 13 * bindings/js/JSHTMLAllCollectionCustom.cpp: Removed some unneeded includes. 14 (WebCore::namedItems): Updated name to match WebKit coding style, and also 15 updated to use the return value from namedItems, which now returns a Vector. 16 (WebCore::callHTMLAllCollection): Updated for namedItems name change. 17 Also removed explicit Node* type from result of namedItemWithIndex, since that 18 function now returns a more specific type. 19 (WebCore::JSHTMLAllCollection::nameGetter): Update for namedItems name change. 20 (WebCore::JSHTMLAllCollection::item): Ditto. 21 (WebCore::JSHTMLAllCollection::namedItem): Ditto. 22 23 * bindings/js/JSHTMLFormControlsCollectionCustom.cpp: Removed some unneeded includes. 24 (WebCore::namedItems): Updated name to match WebKit coding style, and also 25 updated to use the return value from namedItems, which now returns a Vector. 26 (WebCore::JSHTMLFormControlsCollection::nameGetter): Update for namedItems name change. 27 (WebCore::JSHTMLFormControlsCollection::namedItem): Ditto. 28 29 * bindings/js/JSHTMLFormElementCustom.cpp: 30 (WebCore::JSHTMLFormElement::nameGetter): Updated to use the return value from 31 namedItems, which now returns a Vector. 32 33 * html/HTMLAllCollection.cpp: 34 (WebCore::HTMLAllCollection::HTMLAllCollection): Marked the constructor inline, 35 since it's only used in one place, the create function. 36 (WebCore::HTMLAllCollection::~HTMLAllCollection): Deleted. No need to have an 37 explicit destructor since there's nothing special to implement, and includers of 38 the header file have everything they ened to compile the compiler-generated one. 39 (WebCore::HTMLAllCollection::namedItemWithIndex): Changed return type to Element. 40 41 * html/HTMLAllCollection.h: Removed unneeded explicit declaration of destructor. 42 Chagned return type of namedItemWithIndex to Element. 43 44 * html/HTMLCollection.cpp: 45 (WebCore::HTMLCollection::rootTypeFromCollectionType): Marked this inline. Also 46 changed this to be a static member function so it can use the RootType enum, which 47 is now private to the class. 48 (WebCore::isMatchingHTMLElement): Marked this function static so it will get 49 internal linkage. 50 (WebCore::isMatchingElement): Ditto. 51 (WebCore::previousElement): Marked this function inline since it's called in only 52 one place. Changed argument type to a reference since it can never be null. 53 (WebCore::HTMLCollection::iterateForPreviousElement): Changed argument name and 54 also updated for above changes. 55 (WebCore::firstMatchingElement): Marked this function static so it will get 56 internal linkage. 57 (WebCore::nextMatchingElement): Ditto. Changed argument type to a reference 58 since it can never be null. 59 (WebCore::HTMLCollection::item): Changed return type to Element. 60 (WebCore::nameShouldBeVisibleInDocumentAll): Added an overload that takes an 61 Element. This streamlines the code below that calls it so it fits on one line. 62 (WebCore::firstMatchingChildElement): Marked this function static so it will get 63 internal linkage. 64 (WebCore::nextMatchingSiblingElement): Ditto. Changed argument type to a reference 65 since it can never be null. 66 (WebCore::HTMLCollection::usesCustomForwardOnlyTraversal): Moved here from the 67 header since, although it's marked inline, it's only used inside this file. 68 (WebCore::HTMLCollection::traverseForward): Restructured the code a little bit 69 to make the function smaller and possibly easier to read. This does add one 70 redundant null check, but it seems OK to do that. 71 (WebCore::HTMLCollection::collectionTraverseBackward): Tweaked foramtting a bit. 72 (WebCore::HTMLCollection::namedItem): Changed return type to Element. Tightened 73 the code that calls nameShouldBeVisibleInDocumentAll so it fits better on one line. 74 Changed code that handles m_shouldOnlyIncludeDirectChildren to use a nested if 75 instead of an && since it makes the code a little easier to read. 76 (WebCore::HTMLCollection::updateNamedElementCache): Tweaked code a little bit, 77 using shorter variable names, and using references instead of pointers. Also removed 78 the call to didPopulate, since setNamedItemCache now takes care of that. 79 (WebCore::HTMLCollection::namedItems): Changed to return a Vector instead of 80 appending to an existing one. Also use reserveInitialCapacity and uncheckedAppend 81 for better performance. Added a FIXME, because there seems to be something wrong 82 here about this being non-virtual. Made other small tweaks to streamline the code. 83 (WebCore::HTMLCollection::customElementAfter): Moved this here from the header. 84 There is no reason to need to inline this. 85 86 * html/HTMLCollection.h: Removed unneeded includes. Moved function bodies out 87 of the class definitions so the class definitions are easier to read. Made some 88 functions that were formerly public or protected be private instead. Added a call 89 to didPopulate to setNamedItemCache so the callers don't have to do it. 90 91 * html/HTMLFormControlsCollection.cpp: 92 (WebCore::HTMLFormControlsCollection::namedItem): Changed return value to Element. 93 Tweaked coding style a little bit. 94 (WebCore::HTMLFormControlsCollection::updateNamedElementCache): Rearranged to 95 simplify a bit. Don't build the foundInputElements set when the owner is not a 96 form element, since we don't use the set in that case. Use shorter variable names, 97 and modern for loops. Also removed the call to didPopulate, since setNamedItemCache 98 now takes care of that. 99 100 * html/HTMLFormControlsCollection.h: Removed some uneeded forward declarations. 101 Updated return type for namedItem, and also made the override private. 102 103 * html/HTMLFormElement.cpp: 104 (WebCore::HTMLFormElement::namedElements): Changed to return a Vector and updated 105 function name accordingly. 106 * html/HTMLFormElement.h: Ditto. 107 108 * html/HTMLNameCollection.h: Removed a stray blank line. 109 110 * html/HTMLSelectElement.cpp: 111 (WebCore::HTMLSelectElement::namedItem): Changed return value to Element. 112 (WebCore::HTMLSelectElement::item): Ditto. 113 * html/HTMLSelectElement.h: Ditto. 114 115 * page/scrolling/AxisScrollSnapOffsets.cpp: 116 (WebCore::appendChildSnapOffsets): Rewrote loop as a for loop rather than a while 117 loop. Removed unwanted use of children()->collectionBegin() to get the first element 118 child of an HTMLElement. This function uses a mix of DOM and rendering functions that 119 is probably incorrect, but I did not tackle fixing that at this time. 120 1 121 2014-11-11 Dan Bernstein <mitz@apple.com> 2 122 -
trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
r175380 r175947 950 950 // if there's a form, then this is easy 951 951 if (input.form()) { 952 Vector<Ref<Element>> formElements; 953 input.form()->getNamedElements(input.name(), formElements); 954 955 for (auto& associateElement : formElements) { 952 for (auto& associateElement : input.form()->namedElements(input.name())) { 956 953 if (AccessibilityObject* object = axObjectCache()->getOrCreate(&associateElement.get())) 957 954 linkedUIElements.append(object); -
trunk/Source/WebCore/bindings/js/JSHTMLAllCollectionCustom.cpp
r175583 r175947 28 28 29 29 #include "HTMLAllCollection.h" 30 #include "JSDOMBinding.h"31 30 #include "JSNode.h" 32 31 #include "JSNodeList.h" 33 #include "Node.h"34 32 #include "StaticNodeList.h" 35 33 #include <runtime/IdentifierInlines.h> 36 #include <runtime/JSCJSValue.h>37 #include <wtf/Vector.h>38 #include <wtf/text/AtomicString.h>39 34 40 35 using namespace JSC; … … 42 37 namespace WebCore { 43 38 44 static JSValue getNamedItems(ExecState* exec, JSHTMLAllCollection* collection, PropertyName propertyName)39 static JSValue namedItems(ExecState* exec, JSHTMLAllCollection* collection, PropertyName propertyName) 45 40 { 46 Vector<Ref<Element>> namedItems; 47 collection->impl().namedItems(propertyNameToAtomicString(propertyName), namedItems); 41 Vector<Ref<Element>> namedItems = collection->impl().namedItems(propertyNameToAtomicString(propertyName)); 48 42 49 43 if (namedItems.isEmpty()) … … 77 71 78 72 // Support for document.images('<name>') etc. 79 return JSValue::encode( getNamedItems(exec, jsCollection, Identifier(exec, string)));73 return JSValue::encode(namedItems(exec, jsCollection, Identifier(exec, string))); 80 74 } 81 75 … … 84 78 unsigned index = toUInt32FromStringImpl(exec->argument(1).toWTFString(exec).impl()); 85 79 if (index != PropertyName::NotAnIndex) { 86 if ( Node* node= collection.namedItemWithIndex(string, index))87 return JSValue::encode(toJS(exec, jsCollection->globalObject(), node));80 if (auto* item = collection.namedItemWithIndex(string, index)) 81 return JSValue::encode(toJS(exec, jsCollection->globalObject(), item)); 88 82 } 89 83 … … 105 99 { 106 100 JSHTMLAllCollection* thisObj = jsCast<JSHTMLAllCollection*>(slotBase); 107 return JSValue::encode( getNamedItems(exec, thisObj, propertyName));101 return JSValue::encode(namedItems(exec, thisObj, propertyName)); 108 102 } 109 103 … … 113 107 if (index != PropertyName::NotAnIndex) 114 108 return toJS(exec, globalObject(), impl().item(index)); 115 return getNamedItems(exec, this, Identifier(exec, exec->argument(0).toString(exec)->value(exec)));109 return namedItems(exec, this, Identifier(exec, exec->argument(0).toString(exec)->value(exec))); 116 110 } 117 111 118 112 JSValue JSHTMLAllCollection::namedItem(ExecState* exec) 119 113 { 120 JSValue value = getNamedItems(exec, this, Identifier(exec, exec->argument(0).toString(exec)->value(exec)));114 JSValue value = namedItems(exec, this, Identifier(exec, exec->argument(0).toString(exec)->value(exec))); 121 115 return value.isUndefined() ? jsNull() : value; 122 116 } -
trunk/Source/WebCore/bindings/js/JSHTMLFormControlsCollectionCustom.cpp
r175583 r175947 19 19 20 20 #include "config.h" 21 #include "JSHTMLFormControlsCollection.h" 22 21 23 #include "HTMLFormControlsCollection.h" 22 23 24 #include "HTMLAllCollection.h"25 #include "JSDOMBinding.h"26 #include "JSHTMLCollection.h"27 #include "JSHTMLFormControlsCollection.h"28 24 #include "JSNode.h" 29 #include "JSNodeList.h"30 25 #include "JSRadioNodeList.h" 31 #include "Node.h"32 26 #include "RadioNodeList.h" 33 27 #include <runtime/IdentifierInlines.h> 34 #include <wtf/Vector.h>35 #include <wtf/text/AtomicString.h>36 28 37 29 using namespace JSC; … … 39 31 namespace WebCore { 40 32 41 static JSValue getNamedItems(ExecState* exec, JSHTMLFormControlsCollection* collection, PropertyName propertyName)33 static JSValue namedItems(ExecState* exec, JSHTMLFormControlsCollection* collection, PropertyName propertyName) 42 34 { 43 Vector<Ref<Element>> namedItems;44 35 const AtomicString& name = propertyNameToAtomicString(propertyName); 45 collection->impl().namedItems(name, namedItems);36 Vector<Ref<Element>> namedItems = collection->impl().namedItems(name); 46 37 47 38 if (namedItems.isEmpty()) … … 62 53 { 63 54 JSHTMLFormControlsCollection* thisObj = jsCast<JSHTMLFormControlsCollection*>(slotBase); 64 return JSValue::encode( getNamedItems(exec, thisObj, propertyName));55 return JSValue::encode(namedItems(exec, thisObj, propertyName)); 65 56 } 66 57 67 58 JSValue JSHTMLFormControlsCollection::namedItem(ExecState* exec) 68 59 { 69 JSValue value = getNamedItems(exec, this, Identifier(exec, exec->argument(0).toString(exec)->value(exec)));60 JSValue value = namedItems(exec, this, Identifier(exec, exec->argument(0).toString(exec)->value(exec))); 70 61 return value.isUndefined() ? jsNull() : value; 71 62 } -
trunk/Source/WebCore/bindings/js/JSHTMLFormElementCustom.cpp
r165676 r175947 48 48 HTMLFormElement& form = jsForm->impl(); 49 49 50 Vector<Ref<Element>> namedItems; 51 form.getNamedElements(propertyNameToAtomicString(propertyName), namedItems); 50 Vector<Ref<Element>> namedItems = form.namedElements(propertyNameToAtomicString(propertyName)); 52 51 53 52 if (namedItems.isEmpty()) -
trunk/Source/WebCore/html/HTMLAllCollection.cpp
r164772 r175947 36 36 } 37 37 38 HTMLAllCollection::HTMLAllCollection(Document& document, CollectionType type)38 inline HTMLAllCollection::HTMLAllCollection(Document& document, CollectionType type) 39 39 : HTMLCollection(document, type) 40 40 { 41 41 } 42 42 43 HTMLAllCollection::~HTMLAllCollection() 44 { 45 } 46 47 Node* HTMLAllCollection::namedItemWithIndex(const AtomicString& name, unsigned index) const 43 Element* HTMLAllCollection::namedItemWithIndex(const AtomicString& name, unsigned index) const 48 44 { 49 45 updateNamedElementCache(); … … 61 57 } 62 58 63 return 0;59 return nullptr; 64 60 } 65 61 -
trunk/Source/WebCore/html/HTMLAllCollection.h
r175791 r175947 34 34 public: 35 35 static PassRef<HTMLAllCollection> create(Document&, CollectionType); 36 virtual ~HTMLAllCollection();37 36 38 Node* namedItemWithIndex(const AtomicString& name, unsigned index) const;37 Element* namedItemWithIndex(const AtomicString& name, unsigned index) const; 39 38 40 39 private: -
trunk/Source/WebCore/html/HTMLCollection.cpp
r175791 r175947 66 66 } 67 67 68 static HTMLCollection::RootType rootTypeFromCollectionType(CollectionType type) 68 inline auto HTMLCollection::rootTypeFromCollectionType(CollectionType type) -> RootType 69 69 { 70 70 switch (type) { … … 170 170 } 171 171 172 inline bool isMatchingHTMLElement(const HTMLCollection& collection, HTMLElement& element)172 static inline bool isMatchingHTMLElement(const HTMLCollection& collection, HTMLElement& element) 173 173 { 174 174 switch (collection.type()) { … … 219 219 } 220 220 221 inline bool isMatchingElement(const HTMLCollection& collection, Element& element)221 static inline bool isMatchingElement(const HTMLCollection& collection, Element& element) 222 222 { 223 223 // Collection types that deal with any type of Elements, not just HTMLElements. … … 234 234 } 235 235 236 static Element* previousElement(ContainerNode& base, Element* previous, bool onlyIncludeDirectChildren)237 { 238 return onlyIncludeDirectChildren ? ElementTraversal::previousSibling( previous) : ElementTraversal::previous(previous, &base);239 } 240 241 ALWAYS_INLINE Element* HTMLCollection::iterateForPreviousElement(Element* current) const236 static inline Element* previousElement(ContainerNode& base, Element& element, bool onlyIncludeDirectChildren) 237 { 238 return onlyIncludeDirectChildren ? ElementTraversal::previousSibling(&element) : ElementTraversal::previous(&element, &base); 239 } 240 241 ALWAYS_INLINE Element* HTMLCollection::iterateForPreviousElement(Element* element) const 242 242 { 243 243 bool onlyIncludeDirectChildren = m_shouldOnlyIncludeDirectChildren; 244 244 ContainerNode& rootNode = this->rootNode(); 245 for (; current; current = previousElement(rootNode, current, onlyIncludeDirectChildren)) {246 if (isMatchingElement(*this, * current))247 return current;245 for (; element; element = previousElement(rootNode, *element, onlyIncludeDirectChildren)) { 246 if (isMatchingElement(*this, *element)) 247 return element; 248 248 } 249 249 return nullptr; 250 250 } 251 251 252 inline Element* firstMatchingElement(const HTMLCollection& collection, ContainerNode& root)252 static inline Element* firstMatchingElement(const HTMLCollection& collection, ContainerNode& root) 253 253 { 254 254 Element* element = ElementTraversal::firstWithin(&root); … … 258 258 } 259 259 260 inline Element* nextMatchingElement(const HTMLCollection& collection, Element* current, ContainerNode& root) 261 { 260 static inline Element* nextMatchingElement(const HTMLCollection& collection, Element& element, ContainerNode& root) 261 { 262 Element* next = &element; 262 263 do { 263 current = ElementTraversal::next(current, &root);264 } while ( current && !isMatchingElement(collection, *current));265 return current;264 next = ElementTraversal::next(next, &root); 265 } while (next && !isMatchingElement(collection, *next)); 266 return next; 266 267 } 267 268 … … 271 272 } 272 273 273 Node* HTMLCollection::item(unsigned offset) const274 Element* HTMLCollection::item(unsigned offset) const 274 275 { 275 276 return m_indexCache.nodeAt(*this, offset); … … 289 290 } 290 291 291 inline Element* firstMatchingChildElement(const HTMLCollection& nodeList, ContainerNode& root) 292 static inline bool nameShouldBeVisibleInDocumentAll(Element& element) 293 { 294 return is<HTMLElement>(element) && nameShouldBeVisibleInDocumentAll(downcast<HTMLElement>(element)); 295 } 296 297 static inline Element* firstMatchingChildElement(const HTMLCollection& nodeList, ContainerNode& root) 292 298 { 293 299 Element* element = ElementTraversal::firstWithin(&root); … … 297 303 } 298 304 299 inline Element* nextMatchingSiblingElement(const HTMLCollection& nodeList, Element* current) 300 { 305 static inline Element* nextMatchingSiblingElement(const HTMLCollection& nodeList, Element& element) 306 { 307 Element* next = &element; 301 308 do { 302 current = ElementTraversal::nextSibling(current); 303 } while (current && !isMatchingElement(nodeList, *current)); 304 return current; 309 next = ElementTraversal::nextSibling(next); 310 } while (next && !isMatchingElement(nodeList, *next)); 311 return next; 312 } 313 314 inline bool HTMLCollection::usesCustomForwardOnlyTraversal() const 315 { 316 return m_usesCustomForwardOnlyTraversal; 305 317 } 306 318 … … 318 330 Element* element = ¤t; 319 331 if (usesCustomForwardOnlyTraversal()) { 320 for (traversedCount = 0; traversedCount < count; ++traversedCount) {332 for (traversedCount = 0; element && traversedCount < count; ++traversedCount) 321 333 element = customElementAfter(element); 322 if (!element) 323 return nullptr; 324 } 325 return element; 326 } 327 if (m_shouldOnlyIncludeDirectChildren) { 328 for (traversedCount = 0; traversedCount < count; ++traversedCount) { 329 element = nextMatchingSiblingElement(*this, element); 330 if (!element) 331 return nullptr; 332 } 333 return element; 334 } 335 for (traversedCount = 0; traversedCount < count; ++traversedCount) { 336 element = nextMatchingElement(*this, element, root); 337 if (!element) 338 return nullptr; 334 } else if (m_shouldOnlyIncludeDirectChildren) { 335 for (traversedCount = 0; element && traversedCount < count; ++traversedCount) 336 element = nextMatchingSiblingElement(*this, *element); 337 } else { 338 for (traversedCount = 0; element && traversedCount < count; ++traversedCount) 339 element = nextMatchingElement(*this, *element, root); 339 340 } 340 341 return element; … … 362 363 { 363 364 // FIXME: This should be optimized similarly to the forward case. 364 auto& root = rootNode();365 365 if (m_shouldOnlyIncludeDirectChildren) { 366 for (; count && current ; --count)366 for (; count && current; --count) 367 367 current = iterateForPreviousElement(ElementTraversal::previousSibling(current)); 368 368 return; 369 369 } 370 for (; count && current ; --count) 370 auto& root = rootNode(); 371 for (; count && current; --count) 371 372 current = iterateForPreviousElement(ElementTraversal::previous(current, &root)); 372 373 } … … 389 390 } 390 391 391 Node* HTMLCollection::namedItem(const AtomicString& name) const392 Element* HTMLCollection::namedItem(const AtomicString& name) const 392 393 { 393 394 // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp … … 402 403 ContainerNode& root = rootNode(); 403 404 if (!usesCustomForwardOnlyTraversal() && root.isInTreeScope()) { 405 Element* candidate = nullptr; 406 404 407 TreeScope& treeScope = root.treeScope(); 405 Element* candidate = nullptr;406 408 if (treeScope.hasElementWithId(*name.impl())) { 407 409 if (!treeScope.containsMultipleElementsWithId(name)) … … 410 412 if (!treeScope.containsMultipleElementsWithName(name)) { 411 413 candidate = treeScope.getElementByName(name); 412 if (candidate && type() == DocAll && (!is<HTMLElement>(*candidate) || !nameShouldBeVisibleInDocumentAll(downcast<HTMLElement>(*candidate))))414 if (candidate && type() == DocAll && !nameShouldBeVisibleInDocumentAll(*candidate)) 413 415 candidate = nullptr; 414 416 } … … 416 418 return nullptr; 417 419 418 if (candidate && isMatchingElement(*this, *candidate) 419 && (m_shouldOnlyIncludeDirectChildren ? candidate->parentNode() == &root : candidate->isDescendantOf(&root))) 420 return candidate; 420 if (candidate && isMatchingElement(*this, *candidate)) { 421 if (m_shouldOnlyIncludeDirectChildren ? candidate->parentNode() == &root : candidate->isDescendantOf(&root)) 422 return candidate; 423 } 421 424 } 422 425 … … 443 446 return; 444 447 445 446 448 auto cache = std::make_unique<CollectionNamedElementCache>(); 447 449 448 450 unsigned size = m_indexCache.nodeCount(*this); 449 for (unsigned i = 0; i < size; i++) { 450 Element* element = m_indexCache.nodeAt(*this, i); 451 ASSERT(element); 452 const AtomicString& idAttrVal = element->getIdAttribute(); 453 if (!idAttrVal.isEmpty()) 454 cache->appendIdCache(idAttrVal, element); 455 if (!is<HTMLElement>(*element)) 451 for (unsigned i = 0; i < size; ++i) { 452 Element& element = *m_indexCache.nodeAt(*this, i); 453 const AtomicString& id = element.getIdAttribute(); 454 if (!id.isEmpty()) 455 cache->appendToIdCache(id, element); 456 if (!is<HTMLElement>(element)) 456 457 continue; 457 const AtomicString& nameAttrVal = element->getNameAttribute(); 458 if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && (type() != DocAll || nameShouldBeVisibleInDocumentAll(downcast<HTMLElement>(*element)))) 459 cache->appendNameCache(nameAttrVal, element); 460 } 461 462 cache->didPopulate(); 463 setNameItemCache(WTF::move(cache)); 458 const AtomicString& name = element.getNameAttribute(); 459 if (!name.isEmpty() && id != name && (type() != DocAll || nameShouldBeVisibleInDocumentAll(downcast<HTMLElement>(element)))) 460 cache->appendToNameCache(name, element); 461 } 462 463 setNamedItemCache(WTF::move(cache)); 464 464 } 465 465 … … 470 470 } 471 471 472 void HTMLCollection::namedItems(const AtomicString& name, Vector<Ref<Element>>& result) const 473 { 474 ASSERT(result.isEmpty()); 472 Vector<Ref<Element>> HTMLCollection::namedItems(const AtomicString& name) const 473 { 474 // FIXME: This non-virtual function can't possibly be doing the correct thing for 475 // any derived class that overrides the virtual namedItem function. 476 477 Vector<Ref<Element>> elements; 478 475 479 if (name.isEmpty()) 476 return ;480 return elements; 477 481 478 482 updateNamedElementCache(); 479 483 ASSERT(m_namedElementCache); 480 484 481 const Vector<Element*>* idResults = m_namedElementCache->findElementsWithId(name); 482 const Vector<Element*>* nameResults = m_namedElementCache->findElementsWithName(name); 483 484 for (unsigned i = 0; idResults && i < idResults->size(); ++i) 485 result.append(*idResults->at(i)); 486 487 for (unsigned i = 0; nameResults && i < nameResults->size(); ++i) 488 result.append(*nameResults->at(i)); 485 auto* elementsWithId = m_namedElementCache->findElementsWithId(name); 486 auto* elementsWithName = m_namedElementCache->findElementsWithName(name); 487 488 elements.reserveInitialCapacity((elementsWithId ? elementsWithId->size() : 0) + (elementsWithName ? elementsWithName->size() : 0)); 489 490 if (elementsWithId) { 491 for (auto& element : *elementsWithId) 492 elements.uncheckedAppend(*element); 493 } 494 if (elementsWithName) { 495 for (auto& element : *elementsWithName) 496 elements.uncheckedAppend(*element); 497 } 498 499 return elements; 489 500 } 490 501 … … 494 505 } 495 506 507 Element* HTMLCollection::customElementAfter(Element*) const 508 { 509 ASSERT_NOT_REACHED(); 510 return nullptr; 511 } 512 496 513 } // namespace WebCore -
trunk/Source/WebCore/html/HTMLCollection.h
r175791 r175947 25 25 26 26 #include "CollectionIndexCache.h" 27 #include "CollectionType.h"28 #include "ContainerNode.h"29 #include "Document.h"30 27 #include "HTMLNames.h" 31 28 #include "LiveNodeList.h" 32 29 #include "ScriptWrappable.h" 33 #include <memory>34 #include <wtf/Forward.h>35 30 #include <wtf/HashMap.h> 36 #include <wtf/TypeCasts.h>37 #include <wtf/Vector.h>38 31 39 32 namespace WebCore { … … 43 36 class CollectionNamedElementCache { 44 37 public: 45 #ifndef ASSERT_DISABLED 46 CollectionNamedElementCache : m_didPopulateCalled(false) { } 47 #endif 48 49 const Vector<Element*>* findElementsWithId(const AtomicString& id) const { return find(m_idToElementsMap, id); } 50 const Vector<Element*>* findElementsWithName(const AtomicString& name) const { return find(m_nameToElementsMap, name); } 51 52 void appendIdCache(const AtomicString& id, Element* element) { return append(m_idToElementsMap, id, element); } 53 void appendNameCache(const AtomicString& name, Element* element) { return append(m_nameToElementsMap, name, element); } 54 55 void didPopulate() 56 { 57 #ifndef ASSERT_DISABLED 58 m_didPopulateCalled = true; 59 #endif 60 if (size_t cost = memoryCost()) 61 reportExtraMemoryCostForCollectionIndexCache(cost); 62 } 63 size_t memoryCost() const { return (m_idToElementsMap.size() + m_nameToElementsMap.size()) * sizeof(Element*); } 38 const Vector<Element*>* findElementsWithId(const AtomicString& id) const; 39 const Vector<Element*>* findElementsWithName(const AtomicString& name) const; 40 41 void appendToIdCache(const AtomicString& id, Element&); 42 void appendToNameCache(const AtomicString& name, Element&); 43 void didPopulate(); 44 45 size_t memoryCost() const; 64 46 65 47 private: 66 48 typedef HashMap<AtomicStringImpl*, Vector<Element*>> StringToElementsMap; 67 49 68 const Vector<Element*>* find(const StringToElementsMap& map, const AtomicString& key) const 69 { 70 #ifndef ASSERT_DISABLED 71 ASSERT(m_didPopulateCalled); 72 #endif 73 auto it = map.find(key.impl()); 74 return it != map.end() ? &it->value : nullptr; 75 } 76 77 static void append(StringToElementsMap& map, const AtomicString& key, Element* element) 78 { 79 map.add(key.impl(), Vector<Element*>()).iterator->value.append(element); 80 } 81 82 StringToElementsMap m_idToElementsMap; 83 StringToElementsMap m_nameToElementsMap; 84 #ifndef ASSERT_DISABLED 85 bool m_didPopulateCalled; 50 const Vector<Element*>* find(const StringToElementsMap&, const AtomicString& key) const; 51 static void append(StringToElementsMap&, const AtomicString& key, Element&); 52 53 StringToElementsMap m_idMap; 54 StringToElementsMap m_nameMap; 55 56 #if !ASSERT_DISABLED 57 bool m_didPopulate { false }; 86 58 #endif 87 59 }; … … 94 66 // DOM API 95 67 unsigned length() const; 96 Node* item(unsigned offset) const;97 virtual Node* namedItem(const AtomicString& name) const;68 Element* item(unsigned offset) const; 69 virtual Element* namedItem(const AtomicString& name) const; 98 70 PassRefPtr<NodeList> tags(const String&); 99 71 100 72 // Non-DOM API 101 73 bool hasNamedItem(const AtomicString& name) const; 102 void namedItems(const AtomicString& name, Vector<Ref<Element>>&) const; 103 size_t memoryCost() const { return m_indexCache.memoryCost() + (m_namedElementCache ? m_namedElementCache->memoryCost() : 0); } 104 105 enum RootType { 106 IsRootedAtNode, 107 IsRootedAtDocument 108 }; 109 bool isRootedAtDocument() const { return m_rootType == IsRootedAtDocument; } 110 NodeListInvalidationType invalidationType() const { return static_cast<NodeListInvalidationType>(m_invalidationType); } 111 CollectionType type() const { return static_cast<CollectionType>(m_collectionType); } 112 ContainerNode& ownerNode() const { return const_cast<ContainerNode&>(m_ownerNode.get()); } 113 void invalidateCache(const QualifiedName* attrName) const 114 { 115 if (!attrName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attrName)) 116 invalidateCache(document()); 117 else if (hasNamedElementCache() && (*attrName == HTMLNames::idAttr || *attrName == HTMLNames::nameAttr)) 118 invalidateNamedElementCache(document()); 119 } 74 Vector<Ref<Element>> namedItems(const AtomicString& name) const; 75 size_t memoryCost() const; 76 77 bool isRootedAtDocument() const; 78 NodeListInvalidationType invalidationType() const; 79 CollectionType type() const; 80 ContainerNode& ownerNode() const; 81 void invalidateCache(const QualifiedName* attributeName) const; 120 82 virtual void invalidateCache(Document&) const; 121 83 122 // For CollectionIndexCache 84 // For CollectionIndexCache; do not use elsewhere. 123 85 Element* collectionBegin() const; 124 86 Element* collectionLast() const; 125 Element* collectionEnd() const { return nullptr; }87 Element* collectionEnd() const; 126 88 void collectionTraverseForward(Element*&, unsigned count, unsigned& traversedCount) const; 127 89 void collectionTraverseBackward(Element*&, unsigned count) const; 128 bool collectionCanTraverseBackward() const { return !m_usesCustomForwardOnlyTraversal; }129 void willValidateIndexCache() const { document().registerCollection(const_cast<HTMLCollection&>(*this)); }130 131 bool hasNamedElementCache() const { return !!m_namedElementCache; }90 bool collectionCanTraverseBackward() const; 91 void willValidateIndexCache() const; 92 93 bool hasNamedElementCache() const; 132 94 133 95 protected: … … 137 99 virtual void updateNamedElementCache() const; 138 100 139 Document& document() const { return m_ownerNode->document(); } 101 void setNamedItemCache(std::unique_ptr<CollectionNamedElementCache>) const; 102 const CollectionNamedElementCache& namedItemCaches() const; 103 104 private: 105 Document& document() const; 140 106 ContainerNode& rootNode() const; 141 bool usesCustomForwardOnlyTraversal() const { return m_usesCustomForwardOnlyTraversal; } 142 143 RootType rootType() const { return static_cast<RootType>(m_rootType); } 144 145 void setNameItemCache(std::unique_ptr<CollectionNamedElementCache> cache) const 146 { 147 ASSERT(!m_namedElementCache); 148 m_namedElementCache = WTF::move(cache); 149 document().collectionCachedIdNameMap(*this); 150 } 151 152 const CollectionNamedElementCache& namedItemCaches() const 153 { 154 ASSERT(!!m_namedElementCache); 155 return *m_namedElementCache; 156 } 157 158 private: 159 Element* iterateForPreviousElement(Element* current) const; 107 bool usesCustomForwardOnlyTraversal() const; 108 109 Element* iterateForPreviousElement(Element*) const; 160 110 Element* firstElement(ContainerNode& root) const; 161 Element* traverseForward(Element& current, unsigned count, unsigned& traversedCount, ContainerNode& root) const;162 163 virtual Element* customElementAfter(Element*) const { ASSERT_NOT_REACHED(); return nullptr; }164 111 Element* traverseForward(Element&, unsigned count, unsigned& traversedCount, ContainerNode& root) const; 112 113 virtual Element* customElementAfter(Element*) const; 114 165 115 void invalidateNamedElementCache(Document&) const; 116 117 enum RootType { IsRootedAtNode, IsRootedAtDocument }; 118 static RootType rootTypeFromCollectionType(CollectionType); 166 119 167 120 Ref<ContainerNode> m_ownerNode; … … 177 130 }; 178 131 132 inline const Vector<Element*>* CollectionNamedElementCache::findElementsWithId(const AtomicString& id) const 133 { 134 return find(m_idMap, id); 135 } 136 137 inline const Vector<Element*>* CollectionNamedElementCache::findElementsWithName(const AtomicString& name) const 138 { 139 return find(m_nameMap, name); 140 } 141 142 inline void CollectionNamedElementCache::appendToIdCache(const AtomicString& id, Element& element) 143 { 144 return append(m_idMap, id, element); 145 } 146 147 inline void CollectionNamedElementCache::appendToNameCache(const AtomicString& name, Element& element) 148 { 149 return append(m_nameMap, name, element); 150 } 151 152 inline size_t CollectionNamedElementCache::memoryCost() const 153 { 154 return (m_idMap.size() + m_nameMap.size()) * sizeof(Element*); 155 } 156 157 inline void CollectionNamedElementCache::didPopulate() 158 { 159 #if !ASSERT_DISABLED 160 m_didPopulate = true; 161 #endif 162 if (size_t cost = memoryCost()) 163 reportExtraMemoryCostForCollectionIndexCache(cost); 164 } 165 166 inline const Vector<Element*>* CollectionNamedElementCache::find(const StringToElementsMap& map, const AtomicString& key) const 167 { 168 ASSERT(m_didPopulate); 169 auto it = map.find(key.impl()); 170 return it != map.end() ? &it->value : nullptr; 171 } 172 173 inline void CollectionNamedElementCache::append(StringToElementsMap& map, const AtomicString& key, Element& element) 174 { 175 map.add(key.impl(), Vector<Element*>()).iterator->value.append(&element); 176 } 177 178 inline size_t HTMLCollection::memoryCost() const 179 { 180 return m_indexCache.memoryCost() + (m_namedElementCache ? m_namedElementCache->memoryCost() : 0); 181 } 182 183 inline bool HTMLCollection::isRootedAtDocument() const 184 { 185 return m_rootType == IsRootedAtDocument; 186 } 187 188 inline NodeListInvalidationType HTMLCollection::invalidationType() const 189 { 190 return static_cast<NodeListInvalidationType>(m_invalidationType); 191 } 192 193 inline CollectionType HTMLCollection::type() const 194 { 195 return static_cast<CollectionType>(m_collectionType); 196 } 197 198 inline ContainerNode& HTMLCollection::ownerNode() const 199 { 200 return const_cast<ContainerNode&>(m_ownerNode.get()); 201 } 202 203 inline Document& HTMLCollection::document() const 204 { 205 return m_ownerNode->document(); 206 } 207 208 inline void HTMLCollection::invalidateCache(const QualifiedName* attributeName) const 209 { 210 if (!attributeName || shouldInvalidateTypeOnAttributeChange(invalidationType(), *attributeName)) 211 invalidateCache(document()); 212 else if (hasNamedElementCache() && (*attributeName == HTMLNames::idAttr || *attributeName == HTMLNames::nameAttr)) 213 invalidateNamedElementCache(document()); 214 } 215 216 inline Element* HTMLCollection::collectionEnd() const 217 { 218 return nullptr; 219 } 220 221 inline bool HTMLCollection::collectionCanTraverseBackward() const 222 { 223 return !m_usesCustomForwardOnlyTraversal; 224 } 225 226 inline void HTMLCollection::willValidateIndexCache() const 227 { 228 document().registerCollection(const_cast<HTMLCollection&>(*this)); 229 } 230 231 inline bool HTMLCollection::hasNamedElementCache() const 232 { 233 return !!m_namedElementCache; 234 } 235 236 inline void HTMLCollection::setNamedItemCache(std::unique_ptr<CollectionNamedElementCache> cache) const 237 { 238 ASSERT(cache); 239 ASSERT(!m_namedElementCache); 240 cache->didPopulate(); 241 m_namedElementCache = WTF::move(cache); 242 document().collectionCachedIdNameMap(*this); 243 } 244 245 inline const CollectionNamedElementCache& HTMLCollection::namedItemCaches() const 246 { 247 ASSERT(!!m_namedElementCache); 248 return *m_namedElementCache; 249 } 250 179 251 } // namespace WebCore 180 252 -
trunk/Source/WebCore/html/HTMLFormControlsCollection.cpp
r173980 r175947 122 122 } 123 123 124 Node* HTMLFormControlsCollection::namedItem(const AtomicString& name) const124 HTMLElement* HTMLFormControlsCollection::namedItem(const AtomicString& name) const 125 125 { 126 126 // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp … … 129 129 // object with a matching name attribute, but only on those elements 130 130 // that are allowed a name attribute. 131 const Vector<HTMLImageElement*>* imagesElements = is<HTMLFieldSetElement>(ownerNode()) ? nullptr : &formImageElements();132 if (HTMLElement* item = firstNamedItem(formControlElements(), image sElements, idAttr, name))131 auto* imageElements = is<HTMLFieldSetElement>(ownerNode()) ? nullptr : &formImageElements(); 132 if (HTMLElement* item = firstNamedItem(formControlElements(), imageElements, idAttr, name)) 133 133 return item; 134 135 return firstNamedItem(formControlElements(), imagesElements, nameAttr, name); 134 return firstNamedItem(formControlElements(), imageElements, nameAttr, name); 136 135 } 137 136 … … 142 141 143 142 auto cache = std::make_unique<CollectionNamedElementCache>(); 143 144 bool ownerIsFormElement = is<HTMLFormElement>(ownerNode()); 144 145 HashSet<AtomicStringImpl*> foundInputElements; 145 const Vector<FormAssociatedElement*>& elementsArray = formControlElements();146 146 147 for ( unsigned i = 0; i < elementsArray.size(); ++i) {148 FormAssociatedElement& associatedElement = *element sArray[i];147 for (auto& elementPtr : formControlElements()) { 148 FormAssociatedElement& associatedElement = *elementPtr; 149 149 if (associatedElement.isEnumeratable()) { 150 150 HTMLElement& element = associatedElement.asHTMLElement(); 151 const AtomicString& id AttrVal= element.getIdAttribute();152 const AtomicString& nameAttrVal = element.getNameAttribute();153 if (!idAttrVal.isEmpty()) {154 cache->appendIdCache(idAttrVal, &element);155 foundInputElements.add(idAttrVal.impl());151 const AtomicString& id = element.getIdAttribute(); 152 if (!id.isEmpty()) { 153 cache->appendToIdCache(id, element); 154 if (ownerIsFormElement) 155 foundInputElements.add(id.impl()); 156 156 } 157 if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal) { 158 cache->appendNameCache(nameAttrVal, &element); 159 foundInputElements.add(nameAttrVal.impl()); 157 const AtomicString& name = element.getNameAttribute(); 158 if (!name.isEmpty() && id != name) { 159 cache->appendToNameCache(name, element); 160 if (ownerIsFormElement) 161 foundInputElements.add(name.impl()); 160 162 } 161 163 } 162 164 } 163 164 if (is<HTMLFormElement>(ownerNode())) { 165 const Vector<HTMLImageElement*>& imageElementsArray = formImageElements(); 166 for (unsigned i = 0; i < imageElementsArray.size(); ++i) { 167 HTMLImageElement& element = *imageElementsArray[i]; 168 const AtomicString& idAttrVal = element.getIdAttribute(); 169 const AtomicString& nameAttrVal = element.getNameAttribute(); 170 if (!idAttrVal.isEmpty() && !foundInputElements.contains(idAttrVal.impl())) 171 cache->appendIdCache(idAttrVal, &element); 172 if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && !foundInputElements.contains(nameAttrVal.impl())) 173 cache->appendNameCache(nameAttrVal, &element); 165 if (ownerIsFormElement) { 166 for (auto* elementPtr : formImageElements()) { 167 HTMLImageElement& element = *elementPtr; 168 const AtomicString& id = element.getIdAttribute(); 169 if (!id.isEmpty() && !foundInputElements.contains(id.impl())) 170 cache->appendToIdCache(id, element); 171 const AtomicString& name = element.getNameAttribute(); 172 if (!name.isEmpty() && id != name && !foundInputElements.contains(name.impl())) 173 cache->appendToNameCache(name, element); 174 174 } 175 175 } 176 176 177 cache->didPopulate(); 178 setNameItemCache(WTF::move(cache)); 177 setNamedItemCache(WTF::move(cache)); 179 178 } 180 179 -
trunk/Source/WebCore/html/HTMLFormControlsCollection.h
r175791 r175947 25 25 26 26 #include "HTMLCollection.h" 27 #include "HTMLElement.h" 27 28 28 29 namespace WebCore { 29 30 30 31 class FormAssociatedElement; 31 class HTMLElement;32 32 class HTMLImageElement; 33 class QualifiedName;34 33 35 34 // This class is just a big hack to find form elements even in malformed HTML elements. … … 39 38 public: 40 39 static PassRef<HTMLFormControlsCollection> create(ContainerNode&, CollectionType); 41 42 40 virtual ~HTMLFormControlsCollection(); 43 44 virtual Node* namedItem(const AtomicString& name) const override;45 41 46 42 private: 47 43 explicit HTMLFormControlsCollection(ContainerNode&); 48 44 45 virtual HTMLElement* namedItem(const AtomicString& name) const override; 49 46 virtual void invalidateCache(Document&) const override; 50 47 virtual void updateNamedElementCache() const override; -
trunk/Source/WebCore/html/HTMLFormElement.cpp
r175719 r175947 793 793 } 794 794 795 // FIXME: Use Ref Ptr<HTMLElement> for namedItems. elements()->namedItems never return non-HTMLElement nodes.796 void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<Ref<Element>>& namedItems)795 // FIXME: Use Ref<HTMLElement> for the function result since there are no non-HTML elements returned here. 796 Vector<Ref<Element>> HTMLFormElement::namedElements(const AtomicString& name) 797 797 { 798 798 // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem 799 elements()->namedItems(name, namedItems);799 Vector<Ref<Element>> namedItems = elements()->namedItems(name); 800 800 801 801 HTMLElement* elementFromPast = elementFromPastNamesMap(name); … … 804 804 else if (elementFromPast && namedItems.isEmpty()) 805 805 namedItems.append(*elementFromPast); 806 807 return namedItems; 806 808 } 807 809 -
trunk/Source/WebCore/html/HTMLFormElement.h
r175719 r175947 53 53 PassRefPtr<HTMLCollection> elements(); 54 54 bool hasNamedElement(const AtomicString&); 55 void getNamedElements(const AtomicString&, Vector<Ref<Element>>&);55 Vector<Ref<Element>> namedElements(const AtomicString&); 56 56 57 57 unsigned length() const; -
trunk/Source/WebCore/html/HTMLNameCollection.h
r175791 r175947 25 25 26 26 #include "HTMLCollection.h" 27 28 27 #include <wtf/text/AtomicString.h> 29 28 -
trunk/Source/WebCore/html/HTMLSelectElement.cpp
r175791 r175947 428 428 } 429 429 430 Node* HTMLSelectElement::namedItem(const AtomicString& name)431 { 432 return options()->namedItem(name);433 } 434 435 Node* HTMLSelectElement::item(unsigned index)436 { 437 return options()->item(index);430 HTMLOptionElement* HTMLSelectElement::namedItem(const AtomicString& name) 431 { 432 return downcast<HTMLOptionElement>(options()->namedItem(name)); 433 } 434 435 HTMLOptionElement* HTMLSelectElement::item(unsigned index) 436 { 437 return downcast<HTMLOptionElement>(options()->item(index)); 438 438 } 439 439 -
trunk/Source/WebCore/html/HTMLSelectElement.h
r174178 r175947 29 29 #include "Event.h" 30 30 #include "HTMLFormControlElementWithState.h" 31 #include "HTMLOptionElement.h" 31 32 #include "TypeAhead.h" 32 33 #include <wtf/Vector.h> … … 34 35 namespace WebCore { 35 36 36 class HTMLOptionElement;37 37 class HTMLOptionsCollection; 38 38 … … 88 88 void setLength(unsigned, ExceptionCode&); 89 89 90 Node* namedItem(const AtomicString& name);91 Node* item(unsigned index);90 HTMLOptionElement* namedItem(const AtomicString& name); 91 HTMLOptionElement* item(unsigned index); 92 92 93 93 void scrollToSelection(); -
trunk/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp
r173659 r175947 27 27 #include "AxisScrollSnapOffsets.h" 28 28 29 #include "ElementChildIterator.h" 29 30 #include "HTMLCollection.h" 30 31 #include "HTMLElement.h" … … 39 40 static void appendChildSnapOffsets(HTMLElement& parent, bool shouldAddHorizontalChildOffsets, Vector<LayoutUnit>& horizontalSnapOffsetSubsequence, bool shouldAddVerticalChildOffsets, Vector<LayoutUnit>& verticalSnapOffsetSubsequence) 40 41 { 41 Element* child = parent.children()->collectionBegin();42 42 // FIXME: Instead of traversing all children, register children with snap coordinates before appending to snapOffsetSubsequence. 43 while (child) {44 if (RenderBox* box = child ->renderBox()) {43 for (auto& child : childrenOfType<Element>(parent)) { 44 if (RenderBox* box = child.renderBox()) { 45 45 LayoutUnit viewWidth = box->width(); 46 46 LayoutUnit viewHeight = box->height(); 47 47 #if PLATFORM(IOS) 48 // FIXME: Investigate why using localToContainerPoint gives the wrong offsets for iOS mainframe. Also, these offsets won't take transforms into account (make sure to test this!) 49 float left = child->offsetLeft(); 50 float top = child->offsetTop(); 48 // FIXME: Dangerous to call offsetLeft and offsetTop because they call updateLayoutIgnorePendingStylesheets, which can invalidate the RenderBox pointer we are holding. 49 // FIXME: Investigate why using localToContainerPoint gives the wrong offsets for iOS main frame. Also, these offsets won't take transforms into account (make sure to test this!). 50 float left = child.offsetLeft(); 51 float top = child.offsetTop(); 51 52 #else 52 53 // FIXME: Check that localToContainerPoint works with CSS rotations. … … 65 66 } 66 67 } 67 child = child->nextElementSibling();68 68 } 69 69 }
Note:
See TracChangeset
for help on using the changeset viewer.