1 /* 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20 #include "config.h" 21 #include "JSHTMLCollection.h" 22 23 #include "HTMLCollection.h" 24 #include "HTMLOptionsCollection.h" 25 #include "HTMLAllCollection.h" 26 #include "JSDOMBinding.h" 27 #include "JSHTMLAllCollection.h" 28 #include "JSHTMLOptionsCollection.h" 29 #include "JSNode.h" 30 #include "JSNodeList.h" 31 #include "Node.h" 32 #include "StaticNodeList.h" 33 #include <wtf/Vector.h> 34 #include <wtf/text/AtomicString.h> 35 36 using namespace JSC; 37 38 namespace WebCore { 39 40 static JSValue getNamedItems(ExecState* exec, JSHTMLCollection* collection, const Identifier& propertyName) 41 { 42 Vector<RefPtr<Node> > namedItems; 43 collection->impl()->namedItems(identifierToAtomicString(propertyName), namedItems); 44 45 if (namedItems.isEmpty()) 46 return jsUndefined(); 47 if (namedItems.size() == 1) 48 return toJS(exec, collection->globalObject(), namedItems[0].get()); 49 50 // FIXME: HTML5 specifies that this should be a DynamicNodeList. 51 // FIXME: HTML5 specifies that non-HTMLOptionsCollection collections should return 52 // the first matching item instead of a NodeList. 53 return toJS(exec, collection->globalObject(), StaticNodeList::adopt(namedItems).get()); 54 } 55 56 // HTMLCollections are strange objects, they support both get and call, 57 // so that document.forms.item(0) and document.forms(0) both work. 58 static EncodedJSValue JSC_HOST_CALL callHTMLCollection(ExecState* exec) 59 { 60 if (exec->argumentCount() < 1) 61 return JSValue::encode(jsUndefined()); 62 63 // Do not use thisObj here. It can be the JSHTMLDocument, in the document.forms(i) case. 64 JSHTMLCollection* jsCollection = static_cast<JSHTMLCollection*>(exec->callee()); 65 HTMLCollection* collection = jsCollection->impl(); 66 67 // Also, do we need the TypeError test here ? 68 69 if (exec->argumentCount() == 1) { 70 // Support for document.all(<index>) etc. 71 bool ok; 72 UString string = exec->argument(0).toString(exec); 73 unsigned index = Identifier::toUInt32(string, ok); 74 if (ok) 75 return JSValue::encode(toJS(exec, jsCollection->globalObject(), collection->item(index))); 76 77 // Support for document.images('<name>') etc. 78 return JSValue::encode(getNamedItems(exec, jsCollection, Identifier(exec, string))); 79 } 80 81 // The second arg, if set, is the index of the item we want 82 bool ok; 83 UString string = exec->argument(0).toString(exec); 84 unsigned index = Identifier::toUInt32(exec->argument(1).toString(exec), ok); 85 if (ok) { 86 String pstr = ustringToString(string); 87 Node* node = collection->namedItem(pstr); 88 while (node) { 89 if (!index) 90 return JSValue::encode(toJS(exec, jsCollection->globalObject(), node)); 91 node = collection->nextNamedItem(pstr); 92 --index; 93 } 94 } 95 96 return JSValue::encode(jsUndefined()); 97 } 98 99 CallType JSHTMLCollection::getCallData(CallData& callData) 100 { 101 callData.native.function = callHTMLCollection; 102 return CallTypeHost; 103 } 104 105 bool JSHTMLCollection::canGetItemsForName(ExecState*, HTMLCollection* collection, const Identifier& propertyName) 106 { 107 Vector<RefPtr<Node> > namedItems; 108 collection->namedItems(identifierToAtomicString(propertyName), namedItems); 109 return !namedItems.isEmpty(); 110 } 111 112 JSValue JSHTMLCollection::nameGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName) 113 { 114 JSHTMLCollection* thisObj = static_cast<JSHTMLCollection*>(asObject(slotBase)); 115 return getNamedItems(exec, thisObj, propertyName); 116 } 117 118 JSValue JSHTMLCollection::item(ExecState* exec) 119 { 120 bool ok; 121 uint32_t index = Identifier::toUInt32(exec->argument(0).toString(exec), ok); 122 if (ok) 123 return toJS(exec, globalObject(), impl()->item(index)); 124 return getNamedItems(exec, this, Identifier(exec, exec->argument(0).toString(exec))); 125 } 126 127 JSValue JSHTMLCollection::namedItem(ExecState* exec) 128 { 129 return getNamedItems(exec, this, Identifier(exec, exec->argument(0).toString(exec))); 130 } 131 132 JSValue toJS(ExecState* exec, JSDOMGlobalObject* globalObject, HTMLCollection* collection) 133 { 134 if (!collection) 135 return jsNull(); 136 137 JSDOMWrapper* wrapper = getCachedWrapper(currentWorld(exec), collection); 138 139 if (wrapper) 140 return wrapper; 141 142 switch (collection->type()) { 143 case SelectOptions: 144 wrapper = CREATE_DOM_OBJECT_WRAPPER(exec, globalObject, HTMLOptionsCollection, collection); 145 break; 146 case DocAll: 147 wrapper = CREATE_DOM_OBJECT_WRAPPER(exec, globalObject, HTMLAllCollection, collection); 148 break; 149 default: 150 wrapper = CREATE_DOM_OBJECT_WRAPPER(exec, globalObject, HTMLCollection, collection); 151 break; 152 } 153 154 return wrapper; 155 } 156 157 } // namespace WebCore 158