Home | History | Annotate | Download | only in v8
      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 "bindings/v8/V8Binding.h"
     33 
     34 #include "V8DOMStringList.h"
     35 #include "V8Element.h"
     36 #include "V8NodeFilter.h"
     37 #include "V8Window.h"
     38 #include "V8WorkerGlobalScope.h"
     39 #include "V8XPathNSResolver.h"
     40 #include "bindings/v8/ScriptController.h"
     41 #include "bindings/v8/V8NodeFilterCondition.h"
     42 #include "bindings/v8/V8ObjectConstructor.h"
     43 #include "bindings/v8/V8WindowShell.h"
     44 #include "bindings/v8/WorkerScriptController.h"
     45 #include "bindings/v8/custom/V8CustomXPathNSResolver.h"
     46 #include "core/dom/DOMStringList.h"
     47 #include "core/dom/Element.h"
     48 #include "core/dom/NodeFilter.h"
     49 #include "core/dom/QualifiedName.h"
     50 #include "core/inspector/BindingVisitors.h"
     51 #include "core/loader/FrameLoader.h"
     52 #include "core/loader/FrameLoaderClient.h"
     53 #include "core/page/Frame.h"
     54 #include "core/page/Settings.h"
     55 #include "core/workers/WorkerGlobalScope.h"
     56 #include "core/xml/XPathNSResolver.h"
     57 #include "wtf/ArrayBufferContents.h"
     58 #include "wtf/MainThread.h"
     59 #include "wtf/MathExtras.h"
     60 #include "wtf/StdLibExtras.h"
     61 #include "wtf/Threading.h"
     62 #include "wtf/text/AtomicString.h"
     63 #include "wtf/text/CString.h"
     64 #include "wtf/text/StringBuffer.h"
     65 #include "wtf/text/StringHash.h"
     66 #include "wtf/text/WTFString.h"
     67 
     68 namespace WebCore {
     69 
     70 v8::Handle<v8::Value> setDOMException(int exceptionCode, v8::Isolate* isolate)
     71 {
     72     return V8ThrowException::throwDOMException(exceptionCode, isolate);
     73 }
     74 
     75 v8::Handle<v8::Value> setDOMException(int exceptionCode, const String& message, v8::Isolate* isolate)
     76 {
     77     return V8ThrowException::throwDOMException(exceptionCode, message, isolate);
     78 }
     79 
     80 v8::Handle<v8::Value> throwError(V8ErrorType errorType, const String& message, v8::Isolate* isolate)
     81 {
     82     return V8ThrowException::throwError(errorType, message, isolate);
     83 }
     84 
     85 v8::Handle<v8::Value> throwError(v8::Handle<v8::Value> exception)
     86 {
     87     return V8ThrowException::throwError(exception);
     88 }
     89 
     90 v8::Handle<v8::Value> throwTypeError(v8::Isolate* isolate)
     91 {
     92     return V8ThrowException::throwTypeError(String(), isolate);
     93 }
     94 
     95 v8::Handle<v8::Value> throwTypeError(const String& message, v8::Isolate* isolate)
     96 {
     97     return V8ThrowException::throwTypeError(message, isolate);
     98 }
     99 
    100 v8::Handle<v8::Value> throwNotEnoughArgumentsError(v8::Isolate* isolate)
    101 {
    102     return V8ThrowException::throwNotEnoughArgumentsError(isolate);
    103 }
    104 
    105 class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
    106     virtual void* Allocate(size_t size) OVERRIDE
    107     {
    108         void* data;
    109         WTF::ArrayBufferContents::allocateMemory(size, WTF::ArrayBufferContents::ZeroInitialize, data);
    110         return data;
    111     }
    112 
    113     virtual void* AllocateUninitialized(size_t size) OVERRIDE
    114     {
    115         void* data;
    116         WTF::ArrayBufferContents::allocateMemory(size, WTF::ArrayBufferContents::DontInitialize, data);
    117         return data;
    118     }
    119 
    120     virtual void Free(void*)
    121     {
    122         IMMEDIATE_CRASH();
    123     }
    124 
    125     virtual void Free(void* data, size_t size) OVERRIDE
    126     {
    127         WTF::ArrayBufferContents::freeMemory(data, size);
    128     }
    129 };
    130 
    131 v8::ArrayBuffer::Allocator* v8ArrayBufferAllocator()
    132 {
    133     DEFINE_STATIC_LOCAL(ArrayBufferAllocator, arrayBufferAllocator, ());
    134     return &arrayBufferAllocator;
    135 }
    136 
    137 
    138 v8::Handle<v8::Value> v8Array(PassRefPtr<DOMStringList> stringList, v8::Isolate* isolate)
    139 {
    140     if (!stringList)
    141         return v8::Array::New();
    142     v8::Local<v8::Array> result = v8::Array::New(stringList->length());
    143     for (unsigned i = 0; i < stringList->length(); ++i)
    144         result->Set(v8::Integer::New(i, isolate), v8String(stringList->item(i), isolate));
    145     return result;
    146 }
    147 
    148 Vector<v8::Handle<v8::Value> > toVectorOfArguments(const v8::FunctionCallbackInfo<v8::Value>& args)
    149 {
    150     Vector<v8::Handle<v8::Value> > result;
    151     size_t length = args.Length();
    152     for (size_t i = 0; i < length; ++i)
    153         result.append(args[i]);
    154     return result;
    155 }
    156 
    157 PassRefPtr<NodeFilter> toNodeFilter(v8::Handle<v8::Value> callback, v8::Isolate* isolate)
    158 {
    159     RefPtr<NodeFilter> filter = NodeFilter::create();
    160 
    161     // FIXME: Should pass in appropriate creationContext
    162     v8::Handle<v8::Object> filterWrapper = toV8(filter, v8::Handle<v8::Object>(), isolate).As<v8::Object>();
    163 
    164     RefPtr<NodeFilterCondition> condition = V8NodeFilterCondition::create(callback, filterWrapper);
    165     filter->setCondition(condition.release());
    166 
    167     return filter.release();
    168 }
    169 
    170 static const int8_t kMaxInt8 = 127;
    171 static const int8_t kMinInt8 = -128;
    172 static const uint8_t kMaxUInt8 = 255;
    173 const int32_t kMaxInt32 = 0x7fffffff;
    174 const int32_t kMinInt32 = -kMaxInt32 - 1;
    175 const uint32_t kMaxUInt32 = 0xffffffff;
    176 const int64_t kJSMaxInteger = 0x20000000000000LL - 1; // 2^53 - 1, maximum integer exactly representable in ECMAScript.
    177 
    178 static double enforceRange(double x, double minimum, double maximum, bool& ok)
    179 {
    180     if (std::isnan(x) || std::isinf(x)) {
    181         ok = false;
    182         return 0;
    183     }
    184     x = trunc(x);
    185     if (x < minimum || x > maximum) {
    186         ok = false;
    187         return 0;
    188     }
    189     return x;
    190 }
    191 
    192 int8_t toInt8(v8::Handle<v8::Value> value, IntegerConversionConfiguration configuration, bool& ok)
    193 {
    194     ok = true;
    195 
    196     // Fast case. The value is already a 32-bit integer in the right range.
    197     if (value->IsInt32()) {
    198         int32_t result = value->Int32Value();
    199         if (result >= kMinInt8 && result <= kMaxInt8)
    200             return static_cast<int8_t>(result);
    201         if (configuration == EnforceRange) {
    202             ok = false;
    203             return 0;
    204         }
    205         result %= 256; // 2^8.
    206         return static_cast<int8_t>(result > kMaxInt8 ? result - 256 : result);
    207     }
    208 
    209     // Can the value be converted to a number?
    210     v8::Local<v8::Number> numberObject = value->ToNumber();
    211     if (numberObject.IsEmpty()) {
    212         ok = false;
    213         return 0;
    214     }
    215 
    216     if (configuration == EnforceRange)
    217         return enforceRange(numberObject->Value(), kMinInt8, kMaxInt8, ok);
    218 
    219     double numberValue = numberObject->Value();
    220     if (std::isnan(numberValue) || std::isinf(numberValue) || !numberValue)
    221         return 0;
    222 
    223     numberValue = numberValue < 0 ? -floor(abs(numberValue)) : floor(abs(numberValue));
    224     numberValue = fmod(numberValue, 256); // 2^8.
    225 
    226     return static_cast<int8_t>(numberValue > kMaxInt8 ? numberValue - 256 : numberValue);
    227 }
    228 
    229 uint8_t toUInt8(v8::Handle<v8::Value> value, IntegerConversionConfiguration configuration, bool& ok)
    230 {
    231     ok = true;
    232 
    233     // Fast case. The value is a 32-bit signed integer - possibly positive?
    234     if (value->IsInt32()) {
    235         int32_t result = value->Int32Value();
    236         if (result >= 0 && result <= kMaxUInt8)
    237             return static_cast<uint8_t>(result);
    238         if (configuration == EnforceRange) {
    239             ok = false;
    240             return 0;
    241         }
    242         // Converting to uint8_t will cause the resulting value to be the value modulo 2^8.
    243         return static_cast<uint8_t>(result);
    244     }
    245 
    246     // Can the value be converted to a number?
    247     v8::Local<v8::Number> numberObject = value->ToNumber();
    248     if (numberObject.IsEmpty()) {
    249         ok = false;
    250         return 0;
    251     }
    252 
    253     if (configuration == EnforceRange)
    254         return enforceRange(numberObject->Value(), 0, kMaxUInt8, ok);
    255 
    256     // Does the value convert to nan or to an infinity?
    257     double numberValue = numberObject->Value();
    258     if (std::isnan(numberValue) || std::isinf(numberValue) || !numberValue)
    259         return 0;
    260 
    261     numberValue = numberValue < 0 ? -floor(abs(numberValue)) : floor(abs(numberValue));
    262     return static_cast<uint8_t>(fmod(numberValue, 256)); // 2^8.
    263 }
    264 
    265 int32_t toInt32(v8::Handle<v8::Value> value, IntegerConversionConfiguration configuration, bool& ok)
    266 {
    267     ok = true;
    268 
    269     // Fast case. The value is already a 32-bit integer.
    270     if (value->IsInt32())
    271         return value->Int32Value();
    272 
    273     // Can the value be converted to a number?
    274     v8::Local<v8::Number> numberObject = value->ToNumber();
    275     if (numberObject.IsEmpty()) {
    276         ok = false;
    277         return 0;
    278     }
    279 
    280     if (configuration == EnforceRange)
    281         return enforceRange(numberObject->Value(), kMinInt32, kMaxInt32, ok);
    282 
    283     // Does the value convert to nan or to an infinity?
    284     double numberValue = numberObject->Value();
    285     if (std::isnan(numberValue) || std::isinf(numberValue))
    286         return 0;
    287     return numberObject->Int32Value();
    288 }
    289 
    290 uint32_t toUInt32(v8::Handle<v8::Value> value, IntegerConversionConfiguration configuration, bool& ok)
    291 {
    292     ok = true;
    293 
    294     // Fast case. The value is already a 32-bit unsigned integer.
    295     if (value->IsUint32())
    296         return value->Uint32Value();
    297 
    298     // Fast case. The value is a 32-bit signed integer - possibly positive?
    299     if (value->IsInt32()) {
    300         int32_t result = value->Int32Value();
    301         if (result >= 0)
    302             return result;
    303         if (configuration == EnforceRange) {
    304             ok = false;
    305             return 0;
    306         }
    307         return result;
    308     }
    309 
    310     // Can the value be converted to a number?
    311     v8::Local<v8::Number> numberObject = value->ToNumber();
    312     if (numberObject.IsEmpty()) {
    313         ok = false;
    314         return 0;
    315     }
    316 
    317     if (configuration == EnforceRange)
    318         return enforceRange(numberObject->Value(), 0, kMaxUInt32, ok);
    319 
    320     // Does the value convert to nan or to an infinity?
    321     double numberValue = numberObject->Value();
    322     if (std::isnan(numberValue) || std::isinf(numberValue))
    323         return 0;
    324     return numberObject->Uint32Value();
    325 }
    326 
    327 int64_t toInt64(v8::Handle<v8::Value> value, IntegerConversionConfiguration configuration, bool& ok)
    328 {
    329     ok = true;
    330 
    331     // Fast case. The value is a 32-bit integer.
    332     if (value->IsInt32())
    333         return value->Int32Value();
    334 
    335     // Can the value be converted to a number?
    336     v8::Local<v8::Number> numberObject = value->ToNumber();
    337     if (numberObject.IsEmpty()) {
    338         ok = false;
    339         return 0;
    340     }
    341 
    342     double x = numberObject->Value();
    343 
    344     if (configuration == EnforceRange)
    345         return enforceRange(x, -kJSMaxInteger, kJSMaxInteger, ok);
    346 
    347     // NaNs and +/-Infinity should be 0, otherwise modulo 2^64.
    348     unsigned long long integer;
    349     doubleToInteger(x, integer);
    350     return integer;
    351 }
    352 
    353 uint64_t toUInt64(v8::Handle<v8::Value> value, IntegerConversionConfiguration configuration, bool& ok)
    354 {
    355     ok = true;
    356 
    357     // Fast case. The value is a 32-bit unsigned integer.
    358     if (value->IsUint32())
    359         return value->Uint32Value();
    360 
    361     // Fast case. The value is a 32-bit integer.
    362     if (value->IsInt32()) {
    363         int32_t result = value->Int32Value();
    364         if (result >= 0)
    365             return result;
    366         if (configuration == EnforceRange) {
    367             ok = false;
    368             return 0;
    369         }
    370         return result;
    371     }
    372 
    373     // Can the value be converted to a number?
    374     v8::Local<v8::Number> numberObject = value->ToNumber();
    375     if (numberObject.IsEmpty()) {
    376         ok = false;
    377         return 0;
    378     }
    379 
    380     double x = numberObject->Value();
    381 
    382     if (configuration == EnforceRange)
    383         return enforceRange(x, 0, kJSMaxInteger, ok);
    384 
    385     // NaNs and +/-Infinity should be 0, otherwise modulo 2^64.
    386     unsigned long long integer;
    387     doubleToInteger(x, integer);
    388     return integer;
    389 }
    390 
    391 v8::Handle<v8::FunctionTemplate> createRawTemplate(v8::Isolate* isolate)
    392 {
    393     v8::HandleScope scope(isolate);
    394     v8::Local<v8::FunctionTemplate> result = v8::FunctionTemplate::New(V8ObjectConstructor::isValidConstructorMode);
    395     return scope.Close(result);
    396 }
    397 
    398 PassRefPtr<DOMStringList> toDOMStringList(v8::Handle<v8::Value> value, v8::Isolate* isolate)
    399 {
    400     v8::Local<v8::Value> v8Value(v8::Local<v8::Value>::New(value));
    401 
    402     if (V8DOMStringList::HasInstance(v8Value, isolate, worldType(isolate))) {
    403         RefPtr<DOMStringList> ret = V8DOMStringList::toNative(v8::Handle<v8::Object>::Cast(v8Value));
    404         return ret.release();
    405     }
    406 
    407     if (!v8Value->IsArray())
    408         return 0;
    409 
    410     RefPtr<DOMStringList> ret = DOMStringList::create();
    411     v8::Local<v8::Array> v8Array = v8::Local<v8::Array>::Cast(v8Value);
    412     for (size_t i = 0; i < v8Array->Length(); ++i) {
    413         v8::Local<v8::Value> indexedValue = v8Array->Get(v8::Integer::New(i, isolate));
    414         ret->append(toWebCoreString(indexedValue));
    415     }
    416     return ret.release();
    417 }
    418 
    419 PassRefPtr<XPathNSResolver> toXPathNSResolver(v8::Handle<v8::Value> value, v8::Isolate* isolate)
    420 {
    421     RefPtr<XPathNSResolver> resolver;
    422     if (V8XPathNSResolver::HasInstance(value, isolate, worldType(isolate)))
    423         resolver = V8XPathNSResolver::toNative(v8::Handle<v8::Object>::Cast(value));
    424     else if (value->IsObject())
    425         resolver = V8CustomXPathNSResolver::create(value->ToObject(), isolate);
    426     return resolver;
    427 }
    428 
    429 v8::Handle<v8::Object> toInnerGlobalObject(v8::Handle<v8::Context> context)
    430 {
    431     return v8::Handle<v8::Object>::Cast(context->Global()->GetPrototype());
    432 }
    433 
    434 DOMWindow* toDOMWindow(v8::Handle<v8::Context> context)
    435 {
    436     v8::Handle<v8::Object> global = context->Global();
    437     ASSERT(!global.IsEmpty());
    438     v8::Handle<v8::Object> window = global->FindInstanceInPrototypeChain(V8Window::GetTemplate(context->GetIsolate(), MainWorld));
    439     if (!window.IsEmpty())
    440         return V8Window::toNative(window);
    441     window = global->FindInstanceInPrototypeChain(V8Window::GetTemplate(context->GetIsolate(), IsolatedWorld));
    442     ASSERT(!window.IsEmpty());
    443     return V8Window::toNative(window);
    444 }
    445 
    446 ScriptExecutionContext* toScriptExecutionContext(v8::Handle<v8::Context> context)
    447 {
    448     v8::Handle<v8::Object> global = context->Global();
    449     v8::Handle<v8::Object> windowWrapper = global->FindInstanceInPrototypeChain(V8Window::GetTemplate(context->GetIsolate(), MainWorld));
    450     if (!windowWrapper.IsEmpty())
    451         return V8Window::toNative(windowWrapper)->scriptExecutionContext();
    452     windowWrapper = global->FindInstanceInPrototypeChain(V8Window::GetTemplate(context->GetIsolate(), IsolatedWorld));
    453     if (!windowWrapper.IsEmpty())
    454         return V8Window::toNative(windowWrapper)->scriptExecutionContext();
    455     v8::Handle<v8::Object> workerWrapper = global->FindInstanceInPrototypeChain(V8WorkerGlobalScope::GetTemplate(context->GetIsolate(), WorkerWorld));
    456     if (!workerWrapper.IsEmpty())
    457         return V8WorkerGlobalScope::toNative(workerWrapper)->scriptExecutionContext();
    458     // FIXME: Is this line of code reachable?
    459     return 0;
    460 }
    461 
    462 DOMWindow* activeDOMWindow()
    463 {
    464     v8::Handle<v8::Context> context = v8::Context::GetCalling();
    465     if (context.IsEmpty()) {
    466         // Unfortunately, when processing script from a plug-in, we might not
    467         // have a calling context. In those cases, we fall back to the
    468         // entered context.
    469         context = v8::Context::GetEntered();
    470     }
    471     return toDOMWindow(context);
    472 }
    473 
    474 DOMWindow* firstDOMWindow()
    475 {
    476     return toDOMWindow(v8::Context::GetEntered());
    477 }
    478 
    479 Document* currentDocument()
    480 {
    481     return toDOMWindow(v8::Context::GetCurrent())->document();
    482 }
    483 
    484 Frame* toFrameIfNotDetached(v8::Handle<v8::Context> context)
    485 {
    486     DOMWindow* window = toDOMWindow(context);
    487     if (window->isCurrentlyDisplayedInFrame())
    488         return window->frame();
    489     // We return 0 here because |context| is detached from the Frame. If we
    490     // did return |frame| we could get in trouble because the frame could be
    491     // navigated to another security origin.
    492     return 0;
    493 }
    494 
    495 v8::Local<v8::Context> toV8Context(ScriptExecutionContext* context, DOMWrapperWorld* world)
    496 {
    497     if (context->isDocument()) {
    498         ASSERT(world);
    499         if (Frame* frame = toDocument(context)->frame())
    500             return frame->script()->windowShell(world)->context();
    501     } else if (context->isWorkerGlobalScope()) {
    502         ASSERT(!world);
    503         if (WorkerScriptController* script = toWorkerGlobalScope(context)->script())
    504             return script->context();
    505     }
    506     return v8::Local<v8::Context>();
    507 }
    508 
    509 bool handleOutOfMemory()
    510 {
    511     v8::Local<v8::Context> context = v8::Context::GetCurrent();
    512 
    513     if (!context->HasOutOfMemoryException())
    514         return false;
    515 
    516     // Warning, error, disable JS for this frame?
    517     Frame* frame = toFrameIfNotDetached(context);
    518     if (!frame)
    519         return true;
    520 
    521     frame->script()->clearForOutOfMemory();
    522     frame->loader()->client()->didExhaustMemoryAvailableForScript();
    523 
    524     if (Settings* settings = frame->settings())
    525         settings->setScriptEnabled(false);
    526 
    527     return true;
    528 }
    529 
    530 v8::Local<v8::Value> handleMaxRecursionDepthExceeded()
    531 {
    532     throwError(v8RangeError, "Maximum call stack size exceeded.", v8::Isolate::GetCurrent());
    533     return v8::Local<v8::Value>();
    534 }
    535 
    536 void crashIfV8IsDead()
    537 {
    538     if (v8::V8::IsDead()) {
    539         // FIXME: We temporarily deal with V8 internal error situations
    540         // such as out-of-memory by crashing the renderer.
    541         CRASH();
    542     }
    543 }
    544 
    545 WrapperWorldType worldType(v8::Isolate* isolate)
    546 {
    547     V8PerIsolateData* data = V8PerIsolateData::from(isolate);
    548     if (!data->workerDOMDataStore())
    549         return worldTypeInMainThread(isolate);
    550     return WorkerWorld;
    551 }
    552 
    553 WrapperWorldType worldTypeInMainThread(v8::Isolate* isolate)
    554 {
    555     if (!DOMWrapperWorld::isolatedWorldsExist())
    556         return MainWorld;
    557     ASSERT(!v8::Context::GetEntered().IsEmpty());
    558     DOMWrapperWorld* isolatedWorld = DOMWrapperWorld::isolatedWorld(v8::Context::GetEntered());
    559     if (isolatedWorld)
    560         return IsolatedWorld;
    561     return MainWorld;
    562 }
    563 
    564 DOMWrapperWorld* isolatedWorldForIsolate(v8::Isolate* isolate)
    565 {
    566     V8PerIsolateData* data = V8PerIsolateData::from(isolate);
    567     if (data->workerDOMDataStore())
    568         return 0;
    569     if (!DOMWrapperWorld::isolatedWorldsExist())
    570         return 0;
    571     ASSERT(v8::Context::InContext());
    572     return DOMWrapperWorld::isolatedWorld(v8::Context::GetCurrent());
    573 }
    574 
    575 v8::Local<v8::Value> getHiddenValueFromMainWorldWrapper(v8::Isolate* isolate, ScriptWrappable* wrappable, v8::Handle<v8::String> key)
    576 {
    577     v8::Local<v8::Object> wrapper = wrappable->newLocalWrapper(isolate);
    578     return wrapper.IsEmpty() ? v8::Local<v8::Value>() : wrapper->GetHiddenValue(key);
    579 }
    580 
    581 } // namespace WebCore
    582