1 /*
2 * Copyright (C) 2006, 2007, 2008, 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "V8Binding.h"
33
34 #include "DOMStringList.h"
35 #include "Element.h"
36 #include "MathExtras.h"
37 #include "PlatformString.h"
38 #include "QualifiedName.h"
39 #include "StdLibExtras.h"
40 #include "Threading.h"
41 #include "V8Element.h"
42 #include "V8Proxy.h"
43 #include <wtf/text/AtomicString.h>
44 #include <wtf/text/CString.h>
45 #include <wtf/text/StringBuffer.h>
46 #include <wtf/text/StringHash.h>
47
48 namespace WebCore {
49
50 // WebCoreStringResource is a helper class for v8ExternalString. It is used
51 // to manage the life-cycle of the underlying buffer of the external string.
52 class WebCoreStringResource : public v8::String::ExternalStringResource {
53 public:
WebCoreStringResource(const String & string)54 explicit WebCoreStringResource(const String& string)
55 : m_plainString(string)
56 {
57 #ifndef NDEBUG
58 m_threadId = WTF::currentThread();
59 #endif
60 ASSERT(!string.isNull());
61 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length());
62 }
63
WebCoreStringResource(const AtomicString & string)64 explicit WebCoreStringResource(const AtomicString& string)
65 : m_plainString(string.string())
66 , m_atomicString(string)
67 {
68 #ifndef NDEBUG
69 m_threadId = WTF::currentThread();
70 #endif
71 ASSERT(!string.isNull());
72 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * string.length());
73 }
74
~WebCoreStringResource()75 virtual ~WebCoreStringResource()
76 {
77 #ifndef NDEBUG
78 ASSERT(m_threadId == WTF::currentThread());
79 #endif
80 int reducedExternalMemory = -2 * m_plainString.length();
81 if (m_plainString.impl() != m_atomicString.impl() && !m_atomicString.isNull())
82 reducedExternalMemory *= 2;
83 v8::V8::AdjustAmountOfExternalAllocatedMemory(reducedExternalMemory);
84 }
85
data() const86 virtual const uint16_t* data() const
87 {
88 return reinterpret_cast<const uint16_t*>(m_plainString.impl()->characters());
89 }
90
length() const91 virtual size_t length() const { return m_plainString.impl()->length(); }
92
webcoreString()93 String webcoreString() { return m_plainString; }
94
atomicString()95 AtomicString atomicString()
96 {
97 #ifndef NDEBUG
98 ASSERT(m_threadId == WTF::currentThread());
99 #endif
100 if (m_atomicString.isNull()) {
101 m_atomicString = AtomicString(m_plainString);
102 ASSERT(!m_atomicString.isNull());
103 if (m_plainString.impl() != m_atomicString.impl())
104 v8::V8::AdjustAmountOfExternalAllocatedMemory(2 * m_atomicString.length());
105 }
106 return m_atomicString;
107 }
108
toStringResource(v8::Handle<v8::String> v8String)109 static WebCoreStringResource* toStringResource(v8::Handle<v8::String> v8String)
110 {
111 return static_cast<WebCoreStringResource*>(v8String->GetExternalStringResource());
112 }
113
114 private:
115 // A shallow copy of the string. Keeps the string buffer alive until the V8 engine garbage collects it.
116 String m_plainString;
117 // If this string is atomic or has been made atomic earlier the
118 // atomic string is held here. In the case where the string starts
119 // off non-atomic and becomes atomic later it is necessary to keep
120 // the original string alive because v8 may keep derived pointers
121 // into that string.
122 AtomicString m_atomicString;
123
124 #ifndef NDEBUG
125 WTF::ThreadIdentifier m_threadId;
126 #endif
127 };
128
v8ValueToWebCoreString(v8::Handle<v8::Value> value)129 String v8ValueToWebCoreString(v8::Handle<v8::Value> value)
130 {
131 if (value->IsString())
132 return v8StringToWebCoreString(v8::Handle<v8::String>::Cast(value));
133 return v8NonStringValueToWebCoreString(value);
134 }
135
v8ValueToAtomicWebCoreString(v8::Handle<v8::Value> value)136 AtomicString v8ValueToAtomicWebCoreString(v8::Handle<v8::Value> value)
137 {
138 if (value->IsString())
139 return v8StringToAtomicWebCoreString(v8::Handle<v8::String>::Cast(value));
140 return v8NonStringValueToAtomicWebCoreString(value);
141 }
142
toInt32(v8::Handle<v8::Value> value,bool & ok)143 int toInt32(v8::Handle<v8::Value> value, bool& ok)
144 {
145 ok = true;
146
147 // Fast case. The value is already a 32-bit integer.
148 if (value->IsInt32())
149 return value->Int32Value();
150
151 // Can the value be converted to a number?
152 v8::Local<v8::Number> numberObject = value->ToNumber();
153 if (numberObject.IsEmpty()) {
154 ok = false;
155 return 0;
156 }
157
158 // Does the value convert to nan or to an infinity?
159 double numberValue = numberObject->Value();
160 if (isnan(numberValue) || isinf(numberValue)) {
161 ok = false;
162 return 0;
163 }
164
165 // Can the value be converted to a 32-bit integer?
166 v8::Local<v8::Int32> intValue = value->ToInt32();
167 if (intValue.IsEmpty()) {
168 ok = false;
169 return 0;
170 }
171
172 // Return the result of the int32 conversion.
173 return intValue->Value();
174 }
175
toUInt32(v8::Handle<v8::Value> value,bool & ok)176 uint32_t toUInt32(v8::Handle<v8::Value> value, bool& ok)
177 {
178 ok = true;
179
180 // FIXME: there is currently no Value::IsUint32(). This code does
181 // some contortions to avoid silently converting out-of-range
182 // values to uint32_t.
183
184 // Fast case. The value is already a 32-bit positive integer.
185 if (value->IsInt32()) {
186 int32_t result = value->Int32Value();
187 if (result >= 0)
188 return result;
189 }
190
191 // Can the value be converted to a number?
192 v8::Local<v8::Number> numberObject = value->ToNumber();
193 if (numberObject.IsEmpty()) {
194 ok = false;
195 return 0;
196 }
197
198 // Does the value convert to nan or to an infinity?
199 double numberValue = numberObject->Value();
200 if (isnan(numberValue) || isinf(numberValue)) {
201 ok = false;
202 return 0;
203 }
204
205 // Can the value be converted to a 32-bit unsigned integer?
206 v8::Local<v8::Uint32> uintValue = value->ToUint32();
207 if (uintValue.IsEmpty()) {
208 ok = false;
209 return 0;
210 }
211
212 // FIXME: v8::Uint32::Value is not defined!
213 // http://code.google.com/p/v8/issues/detail?id=624
214 v8::Local<v8::Int32> intValue = value->ToInt32();
215 if (intValue.IsEmpty()) {
216 ok = false;
217 return 0;
218 }
219
220 return static_cast<uint32_t>(intValue->Value());
221 }
222
toWebCoreString(const v8::Arguments & args,int index)223 String toWebCoreString(const v8::Arguments& args, int index) {
224 return v8ValueToWebCoreString(args[index]);
225 }
226
227
toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)228 String toWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)
229 {
230 if (value->IsNull())
231 return String();
232 return v8ValueToWebCoreString(value);
233 }
234
toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)235 AtomicString toAtomicWebCoreStringWithNullCheck(v8::Handle<v8::Value> value)
236 {
237 if (value->IsNull())
238 return AtomicString();
239 return v8ValueToAtomicWebCoreString(value);
240 }
241
toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value)242 String toWebCoreStringWithNullOrUndefinedCheck(v8::Handle<v8::Value> value)
243 {
244 if (value->IsNull() || value->IsUndefined())
245 return String();
246 return toWebCoreString(value);
247 }
248
isUndefinedOrNull(v8::Handle<v8::Value> value)249 bool isUndefinedOrNull(v8::Handle<v8::Value> value)
250 {
251 return value->IsNull() || value->IsUndefined();
252 }
253
v8Boolean(bool value)254 v8::Handle<v8::Boolean> v8Boolean(bool value)
255 {
256 return value ? v8::True() : v8::False();
257 }
258
v8UndetectableString(const String & str)259 v8::Handle<v8::String> v8UndetectableString(const String& str)
260 {
261 return v8::String::NewUndetectable(fromWebCoreString(str), str.length());
262 }
263
v8StringOrNull(const String & str)264 v8::Handle<v8::Value> v8StringOrNull(const String& str)
265 {
266 return str.isNull() ? v8::Handle<v8::Value>(v8::Null()) : v8::Handle<v8::Value>(v8String(str));
267 }
268
v8StringOrUndefined(const String & str)269 v8::Handle<v8::Value> v8StringOrUndefined(const String& str)
270 {
271 return str.isNull() ? v8::Handle<v8::Value>(v8::Undefined()) : v8::Handle<v8::Value>(v8String(str));
272 }
273
v8StringOrFalse(const String & str)274 v8::Handle<v8::Value> v8StringOrFalse(const String& str)
275 {
276 return str.isNull() ? v8::Handle<v8::Value>(v8::False()) : v8::Handle<v8::Value>(v8String(str));
277 }
278
toWebCoreDate(v8::Handle<v8::Value> object)279 double toWebCoreDate(v8::Handle<v8::Value> object)
280 {
281 return (object->IsDate() || object->IsNumber()) ? object->NumberValue() : std::numeric_limits<double>::quiet_NaN();
282 }
283
v8DateOrNull(double value)284 v8::Handle<v8::Value> v8DateOrNull(double value)
285 {
286 if (isfinite(value))
287 return v8::Date::New(value);
288 return v8::Null();
289 }
290
291 template <class S> struct StringTraits
292 {
293 static S fromStringResource(WebCoreStringResource* resource);
294
295 static S fromV8String(v8::Handle<v8::String> v8String, int length);
296 };
297
298 template<>
299 struct StringTraits<String>
300 {
fromStringResourceWebCore::StringTraits301 static String fromStringResource(WebCoreStringResource* resource)
302 {
303 return resource->webcoreString();
304 }
305
fromV8StringWebCore::StringTraits306 static String fromV8String(v8::Handle<v8::String> v8String, int length)
307 {
308 ASSERT(v8String->Length() == length);
309 // NOTE: as of now, String(const UChar*, int) performs String::createUninitialized
310 // anyway, so no need to optimize like we do for AtomicString below.
311 UChar* buffer;
312 String result = String::createUninitialized(length, buffer);
313 v8String->Write(reinterpret_cast<uint16_t*>(buffer), 0, length);
314 return result;
315 }
316 };
317
318 template<>
319 struct StringTraits<AtomicString>
320 {
fromStringResourceWebCore::StringTraits321 static AtomicString fromStringResource(WebCoreStringResource* resource)
322 {
323 return resource->atomicString();
324 }
325
fromV8StringWebCore::StringTraits326 static AtomicString fromV8String(v8::Handle<v8::String> v8String, int length)
327 {
328 ASSERT(v8String->Length() == length);
329 static const int inlineBufferSize = 16;
330 if (length <= inlineBufferSize) {
331 UChar inlineBuffer[inlineBufferSize];
332 v8String->Write(reinterpret_cast<uint16_t*>(inlineBuffer), 0, length);
333 return AtomicString(inlineBuffer, length);
334 }
335 UChar* buffer;
336 String tmp = String::createUninitialized(length, buffer);
337 v8String->Write(reinterpret_cast<uint16_t*>(buffer), 0, length);
338 return AtomicString(tmp);
339 }
340 };
341
342 template <typename StringType>
v8StringToWebCoreString(v8::Handle<v8::String> v8String,ExternalMode external)343 StringType v8StringToWebCoreString(v8::Handle<v8::String> v8String, ExternalMode external)
344 {
345 WebCoreStringResource* stringResource = WebCoreStringResource::toStringResource(v8String);
346 if (stringResource)
347 return StringTraits<StringType>::fromStringResource(stringResource);
348
349 int length = v8String->Length();
350 if (!length) {
351 // Avoid trying to morph empty strings, as they do not have enough room to contain the external reference.
352 return StringImpl::empty();
353 }
354
355 StringType result(StringTraits<StringType>::fromV8String(v8String, length));
356
357 if (external == Externalize && v8String->CanMakeExternal()) {
358 stringResource = new WebCoreStringResource(result);
359 if (!v8String->MakeExternal(stringResource)) {
360 // In case of a failure delete the external resource as it was not used.
361 delete stringResource;
362 }
363 }
364 return result;
365 }
366
367 // Explicitly instantiate the above template with the expected parameterizations,
368 // to ensure the compiler generates the code; otherwise link errors can result in GCC 4.4.
369 template String v8StringToWebCoreString<String>(v8::Handle<v8::String>, ExternalMode);
370 template AtomicString v8StringToWebCoreString<AtomicString>(v8::Handle<v8::String>, ExternalMode);
371
372 // Fast but non thread-safe version.
int32ToWebCoreStringFast(int value)373 String int32ToWebCoreStringFast(int value)
374 {
375 // Caching of small strings below is not thread safe: newly constructed AtomicString
376 // are not safely published.
377 ASSERT(WTF::isMainThread());
378
379 // Most numbers used are <= 100. Even if they aren't used there's very little cost in using the space.
380 const int kLowNumbers = 100;
381 DEFINE_STATIC_LOCAL(Vector<AtomicString>, lowNumbers, (kLowNumbers + 1));
382 String webCoreString;
383 if (0 <= value && value <= kLowNumbers) {
384 webCoreString = lowNumbers[value];
385 if (!webCoreString) {
386 AtomicString valueString = AtomicString(String::number(value));
387 lowNumbers[value] = valueString;
388 webCoreString = valueString;
389 }
390 } else
391 webCoreString = String::number(value);
392 return webCoreString;
393 }
394
int32ToWebCoreString(int value)395 String int32ToWebCoreString(int value)
396 {
397 // If we are on the main thread (this should always true for non-workers), call the faster one.
398 if (WTF::isMainThread())
399 return int32ToWebCoreStringFast(value);
400 return String::number(value);
401 }
402
v8NonStringValueToWebCoreString(v8::Handle<v8::Value> object)403 String v8NonStringValueToWebCoreString(v8::Handle<v8::Value> object)
404 {
405 ASSERT(!object->IsString());
406 if (object->IsInt32())
407 return int32ToWebCoreString(object->Int32Value());
408
409 v8::TryCatch block;
410 v8::Handle<v8::String> v8String = object->ToString();
411 // Handle the case where an exception is thrown as part of invoking toString on the object.
412 if (block.HasCaught()) {
413 throwError(block.Exception());
414 return StringImpl::empty();
415 }
416 // This path is unexpected. However there is hypothesis that it
417 // might be combination of v8 and v8 bindings bugs. For now
418 // just bailout as we'll crash if attempt to convert empty handle into a string.
419 if (v8String.IsEmpty()) {
420 ASSERT_NOT_REACHED();
421 return StringImpl::empty();
422 }
423 return v8StringToWebCoreString<String>(v8String, DoNotExternalize);
424 }
425
v8NonStringValueToAtomicWebCoreString(v8::Handle<v8::Value> object)426 AtomicString v8NonStringValueToAtomicWebCoreString(v8::Handle<v8::Value> object)
427 {
428 ASSERT(!object->IsString());
429 return AtomicString(v8NonStringValueToWebCoreString(object));
430 }
431
432 static bool stringImplCacheEnabled = false;
433
enableStringImplCache()434 void enableStringImplCache()
435 {
436 stringImplCacheEnabled = true;
437 }
438
makeExternalString(const String & string)439 static v8::Local<v8::String> makeExternalString(const String& string)
440 {
441 WebCoreStringResource* stringResource = new WebCoreStringResource(string);
442 v8::Local<v8::String> newString = v8::String::NewExternal(stringResource);
443 if (newString.IsEmpty())
444 delete stringResource;
445
446 return newString;
447 }
448
449 typedef HashMap<StringImpl*, v8::String*> StringCache;
450
getStringCache()451 static StringCache& getStringCache()
452 {
453 ASSERT(WTF::isMainThread());
454 DEFINE_STATIC_LOCAL(StringCache, mainThreadStringCache, ());
455 return mainThreadStringCache;
456 }
457
cachedStringCallback(v8::Persistent<v8::Value> wrapper,void * parameter)458 static void cachedStringCallback(v8::Persistent<v8::Value> wrapper, void* parameter)
459 {
460 ASSERT(WTF::isMainThread());
461 StringImpl* stringImpl = static_cast<StringImpl*>(parameter);
462 ASSERT(getStringCache().contains(stringImpl));
463 getStringCache().remove(stringImpl);
464 wrapper.Dispose();
465 stringImpl->deref();
466 }
467
468 RefPtr<StringImpl> lastStringImpl = 0;
469 v8::Persistent<v8::String> lastV8String;
470
v8ExternalStringSlow(StringImpl * stringImpl)471 v8::Local<v8::String> v8ExternalStringSlow(StringImpl* stringImpl)
472 {
473 if (!stringImpl->length())
474 return v8::String::Empty();
475
476 if (!stringImplCacheEnabled)
477 return makeExternalString(String(stringImpl));
478
479 StringCache& stringCache = getStringCache();
480 v8::String* cachedV8String = stringCache.get(stringImpl);
481 if (cachedV8String) {
482 v8::Persistent<v8::String> handle(cachedV8String);
483 if (!handle.IsNearDeath() && !handle.IsEmpty()) {
484 lastStringImpl = stringImpl;
485 lastV8String = handle;
486 return v8::Local<v8::String>::New(handle);
487 }
488 }
489
490 v8::Local<v8::String> newString = makeExternalString(String(stringImpl));
491 if (newString.IsEmpty())
492 return newString;
493
494 v8::Persistent<v8::String> wrapper = v8::Persistent<v8::String>::New(newString);
495 if (wrapper.IsEmpty())
496 return newString;
497
498 stringImpl->ref();
499 wrapper.MakeWeak(stringImpl, cachedStringCallback);
500 stringCache.set(stringImpl, *wrapper);
501
502 lastStringImpl = stringImpl;
503 lastV8String = wrapper;
504
505 return newString;
506 }
507
createRawTemplate()508 v8::Persistent<v8::FunctionTemplate> createRawTemplate()
509 {
510 v8::HandleScope scope;
511 v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8Proxy::checkNewLegal);
512 return v8::Persistent<v8::FunctionTemplate>::New(result);
513 }
514
configureTemplate(v8::Persistent<v8::FunctionTemplate> desc,const char * interfaceName,v8::Persistent<v8::FunctionTemplate> parentClass,int fieldCount,const BatchedAttribute * attributes,size_t attributeCount,const BatchedCallback * callbacks,size_t callbackCount)515 v8::Local<v8::Signature> configureTemplate(v8::Persistent<v8::FunctionTemplate> desc,
516 const char *interfaceName,
517 v8::Persistent<v8::FunctionTemplate> parentClass,
518 int fieldCount,
519 const BatchedAttribute* attributes,
520 size_t attributeCount,
521 const BatchedCallback* callbacks,
522 size_t callbackCount)
523 {
524 desc->SetClassName(v8::String::New(interfaceName));
525 v8::Local<v8::ObjectTemplate> instance = desc->InstanceTemplate();
526 instance->SetInternalFieldCount(fieldCount);
527 if (!parentClass.IsEmpty())
528 desc->Inherit(parentClass);
529 if (attributeCount)
530 batchConfigureAttributes(instance, desc->PrototypeTemplate(),
531 attributes, attributeCount);
532 v8::Local<v8::Signature> defaultSignature = v8::Signature::New(desc);
533 if (callbackCount)
534 batchConfigureCallbacks(desc->PrototypeTemplate(),
535 defaultSignature,
536 static_cast<v8::PropertyAttribute>(v8::DontDelete),
537 callbacks, callbackCount);
538 return defaultSignature;
539 }
540
getToStringName()541 v8::Persistent<v8::String> getToStringName()
542 {
543 DEFINE_STATIC_LOCAL(v8::Persistent<v8::String>, value, ());
544 if (value.IsEmpty())
545 value = v8::Persistent<v8::String>::New(v8::String::New("toString"));
546 return value;
547 }
548
constructorToString(const v8::Arguments & args)549 static v8::Handle<v8::Value> constructorToString(const v8::Arguments& args)
550 {
551 // The DOM constructors' toString functions grab the current toString
552 // for Functions by taking the toString function of itself and then
553 // calling it with the constructor as its receiver. This means that
554 // changes to the Function prototype chain or toString function are
555 // reflected when printing DOM constructors. The only wart is that
556 // changes to a DOM constructor's toString's toString will cause the
557 // toString of the DOM constructor itself to change. This is extremely
558 // obscure and unlikely to be a problem.
559 v8::Handle<v8::Value> value = args.Callee()->Get(getToStringName());
560 if (!value->IsFunction())
561 return v8::String::New("");
562 return v8::Handle<v8::Function>::Cast(value)->Call(args.This(), 0, 0);
563 }
564
getToStringTemplate()565 v8::Persistent<v8::FunctionTemplate> getToStringTemplate()
566 {
567 DEFINE_STATIC_LOCAL(v8::Persistent<v8::FunctionTemplate>, toStringTemplate, ());
568 if (toStringTemplate.IsEmpty())
569 toStringTemplate = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(constructorToString));
570 return toStringTemplate;
571 }
572
getElementStringAttr(const v8::AccessorInfo & info,const QualifiedName & name)573 v8::Handle<v8::Value> getElementStringAttr(const v8::AccessorInfo& info,
574 const QualifiedName& name)
575 {
576 Element* imp = V8Element::toNative(info.Holder());
577 return v8ExternalString(imp->getAttribute(name));
578 }
579
setElementStringAttr(const v8::AccessorInfo & info,const QualifiedName & name,v8::Local<v8::Value> value)580 void setElementStringAttr(const v8::AccessorInfo& info,
581 const QualifiedName& name,
582 v8::Local<v8::Value> value)
583 {
584 Element* imp = V8Element::toNative(info.Holder());
585 AtomicString v = toAtomicWebCoreStringWithNullCheck(value);
586 imp->setAttribute(name, v);
587 }
588
v8ValueToWebCoreDOMStringList(v8::Handle<v8::Value> value)589 PassRefPtr<DOMStringList> v8ValueToWebCoreDOMStringList(v8::Handle<v8::Value> value)
590 {
591 v8::Local<v8::Value> v8Value(v8::Local<v8::Value>::New(value));
592 if (!v8Value->IsArray())
593 return 0;
594
595 RefPtr<DOMStringList> ret = DOMStringList::create();
596 v8::Local<v8::Array> v8Array = v8::Local<v8::Array>::Cast(v8Value);
597 for (size_t i = 0; i < v8Array->Length(); ++i) {
598 v8::Local<v8::Value> indexedValue = v8Array->Get(v8::Integer::New(i));
599 ret->append(v8ValueToWebCoreString(indexedValue));
600 }
601 return ret.release();
602 }
603
604 } // namespace WebCore
605