1/*
2 * Copyright (C) 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 "V8Proxy.h"
33
34#include "CSSMutableStyleDeclaration.h"
35#include "CachedMetadata.h"
36#include "DateExtension.h"
37#include "DocumentLoader.h"
38#include "Frame.h"
39#include "FrameLoaderClient.h"
40#include "IDBDatabaseException.h"
41#include "IDBFactoryBackendInterface.h"
42#include "IDBPendingTransactionMonitor.h"
43#include "InspectorInstrumentation.h"
44#include "Page.h"
45#include "PageGroup.h"
46#include "PlatformBridge.h"
47#include "ScriptController.h"
48#include "Settings.h"
49#include "StorageNamespace.h"
50#include "V8Binding.h"
51#include "V8BindingState.h"
52#include "V8Collection.h"
53#include "V8DOMCoreException.h"
54#include "V8DOMMap.h"
55#include "V8DOMWindow.h"
56#include "V8EventException.h"
57#include "V8FileException.h"
58#include "V8HiddenPropertyName.h"
59#include "V8IsolatedContext.h"
60#include "V8RangeException.h"
61#include "V8SQLException.h"
62#include "V8XMLHttpRequestException.h"
63#include "V8XPathException.h"
64#include "WorkerContext.h"
65#include "WorkerContextExecutionProxy.h"
66
67#if ENABLE(INDEXED_DATABASE)
68#include "V8IDBDatabaseException.h"
69#endif
70
71#if ENABLE(SVG)
72#include "V8SVGException.h"
73#endif
74
75#include <algorithm>
76#include <stdio.h>
77#include <utility>
78#include <wtf/Assertions.h>
79#include <wtf/OwnArrayPtr.h>
80#include <wtf/OwnPtr.h>
81#include <wtf/StdLibExtras.h>
82#include <wtf/StringExtras.h>
83#include <wtf/UnusedParam.h>
84#include <wtf/text/StringConcatenate.h>
85
86#ifdef ANDROID_INSTRUMENT
87#include "TimeCounter.h"
88#endif
89
90#if PLATFORM(ANDROID)
91#include <wtf/text/CString.h>
92#endif
93
94namespace WebCore {
95
96// Static list of registered extensions
97V8Extensions V8Proxy::m_extensions;
98
99void batchConfigureAttributes(v8::Handle<v8::ObjectTemplate> instance,
100                              v8::Handle<v8::ObjectTemplate> proto,
101                              const BatchedAttribute* attributes,
102                              size_t attributeCount)
103{
104    for (size_t i = 0; i < attributeCount; ++i)
105        configureAttribute(instance, proto, attributes[i]);
106}
107
108void batchConfigureCallbacks(v8::Handle<v8::ObjectTemplate> proto,
109                             v8::Handle<v8::Signature> signature,
110                             v8::PropertyAttribute attributes,
111                             const BatchedCallback* callbacks,
112                             size_t callbackCount)
113{
114    for (size_t i = 0; i < callbackCount; ++i) {
115        proto->Set(v8::String::New(callbacks[i].name),
116                   v8::FunctionTemplate::New(callbacks[i].callback,
117                                             v8::Handle<v8::Value>(),
118                                             signature),
119                   attributes);
120    }
121}
122
123void batchConfigureConstants(v8::Handle<v8::FunctionTemplate> functionDescriptor,
124                             v8::Handle<v8::ObjectTemplate> proto,
125                             const BatchedConstant* constants,
126                             size_t constantCount)
127{
128    for (size_t i = 0; i < constantCount; ++i) {
129        const BatchedConstant* constant = &constants[i];
130        functionDescriptor->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
131        proto->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
132    }
133}
134
135typedef HashMap<Node*, v8::Object*> DOMNodeMap;
136typedef HashMap<void*, v8::Object*> DOMObjectMap;
137typedef HashMap<int, v8::FunctionTemplate*> FunctionTemplateMap;
138
139bool AllowAllocation::m_current = false;
140
141static void addMessageToConsole(Page* page, const String& message, const String& sourceID, unsigned lineNumber)
142{
143    ASSERT(page);
144    Console* console = page->mainFrame()->domWindow()->console();
145    console->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, lineNumber, sourceID);
146}
147
148void logInfo(Frame* frame, const String& message, const String& url)
149{
150    Page* page = frame->page();
151    if (!page)
152        return;
153    addMessageToConsole(page, message, url, 0);
154}
155
156void V8Proxy::reportUnsafeAccessTo(Frame* target)
157{
158    ASSERT(target);
159    Document* targetDocument = target->document();
160    if (!targetDocument)
161        return;
162
163    Frame* source = V8Proxy::retrieveFrameForEnteredContext();
164    if (!source)
165        return;
166    Page* page = source->page();
167    if (!page)
168        return;
169
170    Document* sourceDocument = source->document();
171    if (!sourceDocument)
172        return; // Ignore error if the source document is gone.
173
174    // FIXME: This error message should contain more specifics of why the same
175    // origin check has failed.
176    String str = makeString("Unsafe JavaScript attempt to access frame with URL ", targetDocument->url().string(),
177                            " from frame with URL ", sourceDocument->url().string(), ". Domains, protocols and ports must match.\n");
178
179    // Build a console message with fake source ID and line number.
180    const String kSourceID = "";
181    const int kLineNumber = 1;
182
183    // NOTE: Safari prints the message in the target page, but it seems like
184    // it should be in the source page. Even for delayed messages, we put it in
185    // the source page.
186    addMessageToConsole(page, str, kSourceID, kLineNumber);
187}
188
189static void handleFatalErrorInV8()
190{
191    // FIXME: We temporarily deal with V8 internal error situations
192    // such as out-of-memory by crashing the renderer.
193    CRASH();
194}
195
196V8Proxy::V8Proxy(Frame* frame)
197    : m_frame(frame)
198    , m_windowShell(V8DOMWindowShell::create(frame))
199    , m_inlineCode(false)
200    , m_timerCallback(false)
201    , m_recursion(0)
202{
203}
204
205V8Proxy::~V8Proxy()
206{
207    clearForClose();
208    windowShell()->destroyGlobal();
209}
210
211v8::Handle<v8::Script> V8Proxy::compileScript(v8::Handle<v8::String> code, const String& fileName, const TextPosition0& scriptStartPosition, v8::ScriptData* scriptData)
212#ifdef ANDROID_INSTRUMENT
213{
214    android::TimeCounter::start(android::TimeCounter::JavaScriptParseTimeCounter);
215    v8::Handle<v8::Script> script = compileScriptInternal(code, fileName, scriptStartPosition, scriptData);
216    android::TimeCounter::record(android::TimeCounter::JavaScriptParseTimeCounter, __FUNCTION__);
217    return script;
218}
219
220v8::Handle<v8::Script> V8Proxy::compileScriptInternal(v8::Handle<v8::String> code, const String& fileName, const TextPosition0& scriptStartPosition, v8::ScriptData* scriptData)
221#endif
222{
223    const uint16_t* fileNameString = fromWebCoreString(fileName);
224    v8::Handle<v8::String> name = v8::String::New(fileNameString, fileName.length());
225    v8::Handle<v8::Integer> line = v8::Integer::New(scriptStartPosition.m_line.zeroBasedInt());
226    v8::Handle<v8::Integer> column = v8::Integer::New(scriptStartPosition.m_column.zeroBasedInt());
227    v8::ScriptOrigin origin(name, line, column);
228    v8::Handle<v8::Script> script = v8::Script::Compile(code, &origin, scriptData);
229    return script;
230}
231
232bool V8Proxy::handleOutOfMemory()
233{
234    v8::Local<v8::Context> context = v8::Context::GetCurrent();
235
236    if (!context->HasOutOfMemoryException())
237        return false;
238
239    // Warning, error, disable JS for this frame?
240    Frame* frame = V8Proxy::retrieveFrame(context);
241
242    V8Proxy* proxy = V8Proxy::retrieve(frame);
243    if (proxy) {
244        // Clean m_context, and event handlers.
245        proxy->clearForClose();
246
247        proxy->windowShell()->destroyGlobal();
248    }
249
250#if PLATFORM(CHROMIUM)
251    PlatformBridge::notifyJSOutOfMemory(frame);
252#endif
253
254    // Disable JS.
255    Settings* settings = frame->settings();
256    ASSERT(settings);
257    settings->setJavaScriptEnabled(false);
258
259    return true;
260}
261
262void V8Proxy::evaluateInIsolatedWorld(int worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup)
263{
264    // FIXME: This will need to get reorganized once we have a windowShell for the isolated world.
265    windowShell()->initContextIfNeeded();
266
267    v8::HandleScope handleScope;
268    V8IsolatedContext* isolatedContext = 0;
269
270    if (worldID > 0) {
271        IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(worldID);
272        if (iter != m_isolatedWorlds.end()) {
273            isolatedContext = iter->second;
274        } else {
275            isolatedContext = new V8IsolatedContext(this, extensionGroup);
276            if (isolatedContext->context().IsEmpty()) {
277                delete isolatedContext;
278                return;
279            }
280
281            // FIXME: We should change this to using window shells to match JSC.
282            m_isolatedWorlds.set(worldID, isolatedContext);
283
284            // Setup context id for JS debugger.
285            if (!setInjectedScriptContextDebugId(isolatedContext->context())) {
286                m_isolatedWorlds.take(worldID);
287                delete isolatedContext;
288                return;
289            }
290        }
291    } else {
292        isolatedContext = new V8IsolatedContext(this, extensionGroup);
293        if (isolatedContext->context().IsEmpty()) {
294            delete isolatedContext;
295            return;
296        }
297    }
298
299    v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolatedContext->context());
300    v8::Context::Scope context_scope(context);
301    for (size_t i = 0; i < sources.size(); ++i)
302      evaluate(sources[i], 0);
303
304    if (worldID == 0)
305      isolatedContext->destroy();
306}
307
308bool V8Proxy::setInjectedScriptContextDebugId(v8::Handle<v8::Context> targetContext)
309{
310    // Setup context id for JS debugger.
311    v8::Context::Scope contextScope(targetContext);
312    v8::Handle<v8::Context> context = windowShell()->context();
313    if (context.IsEmpty())
314        return false;
315    int debugId = contextDebugId(context);
316
317    char buffer[32];
318    if (debugId == -1)
319        snprintf(buffer, sizeof(buffer), "injected");
320    else
321        snprintf(buffer, sizeof(buffer), "injected,%d", debugId);
322    targetContext->SetData(v8::String::New(buffer));
323
324    return true;
325}
326
327PassOwnPtr<v8::ScriptData> V8Proxy::precompileScript(v8::Handle<v8::String> code, CachedScript* cachedScript)
328{
329    // A pseudo-randomly chosen ID used to store and retrieve V8 ScriptData from
330    // the CachedScript. If the format changes, this ID should be changed too.
331    static const unsigned dataTypeID = 0xECC13BD7;
332
333    // Very small scripts are not worth the effort to preparse.
334    static const int minPreparseLength = 1024;
335
336    if (!cachedScript || code->Length() < minPreparseLength)
337        return 0;
338
339    CachedMetadata* cachedMetadata = cachedScript->cachedMetadata(dataTypeID);
340    if (cachedMetadata)
341        return v8::ScriptData::New(cachedMetadata->data(), cachedMetadata->size());
342
343    OwnPtr<v8::ScriptData> scriptData(v8::ScriptData::PreCompile(code));
344    cachedScript->setCachedMetadata(dataTypeID, scriptData->Data(), scriptData->Length());
345
346    return scriptData.release();
347}
348
349bool V8Proxy::executingScript() const
350{
351    return m_recursion;
352}
353
354v8::Local<v8::Value> V8Proxy::evaluate(const ScriptSourceCode& source, Node* node)
355{
356    ASSERT(v8::Context::InContext());
357
358    V8GCController::checkMemoryUsage();
359
360    InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, source.url().isNull() ? String() : source.url().string(), source.startLine());
361
362    v8::Local<v8::Value> result;
363    {
364        // Isolate exceptions that occur when compiling and executing
365        // the code. These exceptions should not interfere with
366        // javascript code we might evaluate from C++ when returning
367        // from here.
368        v8::TryCatch tryCatch;
369        tryCatch.SetVerbose(true);
370
371        // Compile the script.
372        v8::Local<v8::String> code = v8ExternalString(source.source());
373#if PLATFORM(CHROMIUM)
374        PlatformBridge::traceEventBegin("v8.compile", node, "");
375#endif
376        OwnPtr<v8::ScriptData> scriptData = precompileScript(code, source.cachedScript());
377
378        // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at
379        // 1, whereas v8 starts at 0.
380        v8::Handle<v8::Script> script = compileScript(code, source.url(), WTF::toZeroBasedTextPosition(source.startPosition()), scriptData.get());
381#if PLATFORM(CHROMIUM)
382        PlatformBridge::traceEventEnd("v8.compile", node, "");
383
384        PlatformBridge::traceEventBegin("v8.run", node, "");
385#endif
386        // Set inlineCode to true for <a href="javascript:doSomething()">
387        // and false for <script>doSomething</script>. We make a rough guess at
388        // this based on whether the script source has a URL.
389        result = runScript(script, source.url().string().isNull());
390    }
391#if PLATFORM(CHROMIUM)
392    PlatformBridge::traceEventEnd("v8.run", node, "");
393#endif
394
395    InspectorInstrumentation::didEvaluateScript(cookie);
396
397    return result;
398}
399
400v8::Local<v8::Value> V8Proxy::runScript(v8::Handle<v8::Script> script, bool isInlineCode)
401#ifdef ANDROID_INSTRUMENT
402{
403    android::TimeCounter::start(android::TimeCounter::JavaScriptExecuteTimeCounter);
404    v8::Local<v8::Value> result = runScriptInternal(script, isInlineCode);
405    android::TimeCounter::record(android::TimeCounter::JavaScriptExecuteTimeCounter, __FUNCTION__);
406    return result;
407}
408
409v8::Local<v8::Value> V8Proxy::runScriptInternal(v8::Handle<v8::Script> script, bool isInlineCode)
410#endif
411{
412    if (script.IsEmpty())
413        return notHandledByInterceptor();
414
415    V8GCController::checkMemoryUsage();
416    // Compute the source string and prevent against infinite recursion.
417    if (m_recursion >= kMaxRecursionDepth) {
418        v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')");
419        // FIXME: Ideally, we should be able to re-use the origin of the
420        // script passed to us as the argument instead of using an empty string
421        // and 0 baseLine.
422        script = compileScript(code, "", TextPosition0::minimumPosition());
423    }
424
425    if (handleOutOfMemory())
426        ASSERT(script.IsEmpty());
427
428    if (script.IsEmpty())
429        return notHandledByInterceptor();
430
431    // Save the previous value of the inlineCode flag and update the flag for
432    // the duration of the script invocation.
433    bool previousInlineCode = inlineCode();
434    setInlineCode(isInlineCode);
435
436    // Run the script and keep track of the current recursion depth.
437    v8::Local<v8::Value> result;
438    v8::TryCatch tryCatch;
439    tryCatch.SetVerbose(true);
440    {
441        // See comment in V8Proxy::callFunction.
442        m_frame->keepAlive();
443
444        m_recursion++;
445        result = script->Run();
446        m_recursion--;
447    }
448
449    // Release the storage mutex if applicable.
450    didLeaveScriptContext();
451
452    if (handleOutOfMemory())
453        ASSERT(result.IsEmpty());
454
455    // Handle V8 internal error situation (Out-of-memory).
456    if (tryCatch.HasCaught()) {
457        ASSERT(result.IsEmpty());
458        return notHandledByInterceptor();
459    }
460
461    if (result.IsEmpty())
462        return notHandledByInterceptor();
463
464    // Restore inlineCode flag.
465    setInlineCode(previousInlineCode);
466
467    if (v8::V8::IsDead())
468        handleFatalErrorInV8();
469
470    return result;
471}
472
473v8::Local<v8::Value> V8Proxy::callFunction(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
474{
475#ifdef ANDROID_INSTRUMENT
476    android::TimeCounter::start(android::TimeCounter::JavaScriptExecuteTimeCounter);
477#endif
478    V8GCController::checkMemoryUsage();
479    v8::Local<v8::Value> result;
480    {
481        if (m_recursion >= kMaxRecursionDepth) {
482            v8::Local<v8::String> code = v8::String::New("throw new RangeError('Maximum call stack size exceeded.')");
483            if (code.IsEmpty())
484                return result;
485            v8::Local<v8::Script> script = v8::Script::Compile(code);
486            if (script.IsEmpty())
487                return result;
488            script->Run();
489            return result;
490        }
491
492        // Evaluating the JavaScript could cause the frame to be deallocated,
493        // so we start the keep alive timer here.
494        // Frame::keepAlive method adds the ref count of the frame and sets a
495        // timer to decrease the ref count. It assumes that the current JavaScript
496        // execution finishs before firing the timer.
497        m_frame->keepAlive();
498
499        InspectorInstrumentationCookie cookie;
500        if (InspectorInstrumentation::hasFrontends()) {
501            v8::ScriptOrigin origin = function->GetScriptOrigin();
502            String resourceName("undefined");
503            int lineNumber = 1;
504            if (!origin.ResourceName().IsEmpty()) {
505                resourceName = toWebCoreString(origin.ResourceName());
506                lineNumber = function->GetScriptLineNumber() + 1;
507            }
508            cookie = InspectorInstrumentation::willCallFunction(m_frame, resourceName, lineNumber);
509        }
510
511        m_recursion++;
512        result = function->Call(receiver, argc, args);
513        m_recursion--;
514
515        InspectorInstrumentation::didCallFunction(cookie);
516    }
517
518    // Release the storage mutex if applicable.
519    didLeaveScriptContext();
520
521    if (v8::V8::IsDead())
522        handleFatalErrorInV8();
523
524#ifdef ANDROID_INSTRUMENT
525    android::TimeCounter::record(android::TimeCounter::JavaScriptExecuteTimeCounter, __FUNCTION__);
526#endif
527    return result;
528}
529
530v8::Local<v8::Value> V8Proxy::callFunctionWithoutFrame(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
531{
532    V8GCController::checkMemoryUsage();
533    v8::Local<v8::Value> result = function->Call(receiver, argc, args);
534
535    if (v8::V8::IsDead())
536        handleFatalErrorInV8();
537
538    return result;
539}
540
541v8::Local<v8::Value> V8Proxy::newInstance(v8::Handle<v8::Function> constructor, int argc, v8::Handle<v8::Value> args[])
542{
543    // No artificial limitations on the depth of recursion, see comment in
544    // V8Proxy::callFunction.
545    v8::Local<v8::Value> result;
546    {
547        // See comment in V8Proxy::callFunction.
548        m_frame->keepAlive();
549
550        result = constructor->NewInstance(argc, args);
551    }
552
553    if (v8::V8::IsDead())
554        handleFatalErrorInV8();
555
556    return result;
557}
558
559DOMWindow* V8Proxy::retrieveWindow(v8::Handle<v8::Context> context)
560{
561    v8::Handle<v8::Object> global = context->Global();
562    ASSERT(!global.IsEmpty());
563    global = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), global);
564    ASSERT(!global.IsEmpty());
565    return V8DOMWindow::toNative(global);
566}
567
568Frame* V8Proxy::retrieveFrame(v8::Handle<v8::Context> context)
569{
570    DOMWindow* window = retrieveWindow(context);
571    Frame* frame = window->frame();
572    if (frame && frame->domWindow() == window)
573        return frame;
574    // We return 0 here because |context| is detached from the Frame.  If we
575    // did return |frame| we could get in trouble because the frame could be
576    // navigated to another security origin.
577    return 0;
578}
579
580Frame* V8Proxy::retrieveFrameForEnteredContext()
581{
582    v8::Handle<v8::Context> context = v8::Context::GetEntered();
583    if (context.IsEmpty())
584        return 0;
585    return retrieveFrame(context);
586}
587
588Frame* V8Proxy::retrieveFrameForCurrentContext()
589{
590    v8::Handle<v8::Context> context = v8::Context::GetCurrent();
591    if (context.IsEmpty())
592        return 0;
593    return retrieveFrame(context);
594}
595
596Frame* V8Proxy::retrieveFrameForCallingContext()
597{
598    v8::Handle<v8::Context> context = v8::Context::GetCalling();
599    if (context.IsEmpty())
600        return 0;
601    return retrieveFrame(context);
602}
603
604V8Proxy* V8Proxy::retrieve()
605{
606    DOMWindow* window = retrieveWindow(currentContext());
607    ASSERT(window);
608    return retrieve(window->frame());
609}
610
611V8Proxy* V8Proxy::retrieve(Frame* frame)
612{
613    if (!frame)
614        return 0;
615    return frame->script()->canExecuteScripts(NotAboutToExecuteScript) ? frame->script()->proxy() : 0;
616}
617
618V8Proxy* V8Proxy::retrieve(ScriptExecutionContext* context)
619{
620    if (!context || !context->isDocument())
621        return 0;
622    return retrieve(static_cast<Document*>(context)->frame());
623}
624
625void V8Proxy::disconnectFrame()
626{
627}
628
629void V8Proxy::didLeaveScriptContext()
630{
631    Page* page = m_frame->page();
632    if (!page)
633        return;
634#if ENABLE(INDEXED_DATABASE)
635    // If we've just left a script context and indexed database has been
636    // instantiated, we must let its transaction coordinator know so it can terminate
637    // any not-yet-started transactions.
638    IDBPendingTransactionMonitor::abortPendingTransactions();
639#endif // ENABLE(INDEXED_DATABASE)
640    // If we've just left a top level script context and local storage has been
641    // instantiated, we must ensure that any storage locks have been freed.
642    // Per http://dev.w3.org/html5/spec/Overview.html#storage-mutex
643    if (m_recursion != 0)
644        return;
645    if (page->group().hasLocalStorage())
646        page->group().localStorage()->unlock();
647}
648
649void V8Proxy::resetIsolatedWorlds()
650{
651    for (IsolatedWorldMap::iterator iter = m_isolatedWorlds.begin();
652         iter != m_isolatedWorlds.end(); ++iter) {
653        iter->second->destroy();
654    }
655    m_isolatedWorlds.clear();
656}
657
658void V8Proxy::clearForClose()
659{
660    resetIsolatedWorlds();
661    windowShell()->clearForClose();
662}
663
664void V8Proxy::clearForNavigation()
665{
666    resetIsolatedWorlds();
667    windowShell()->clearForNavigation();
668}
669
670void V8Proxy::setDOMException(int exceptionCode)
671{
672    if (exceptionCode <= 0)
673        return;
674
675    ExceptionCodeDescription description;
676    getExceptionCodeDescription(exceptionCode, description);
677
678    v8::Handle<v8::Value> exception;
679    switch (description.type) {
680    case DOMExceptionType:
681        exception = toV8(DOMCoreException::create(description));
682        break;
683    case RangeExceptionType:
684        exception = toV8(RangeException::create(description));
685        break;
686    case EventExceptionType:
687        exception = toV8(EventException::create(description));
688        break;
689    case XMLHttpRequestExceptionType:
690        exception = toV8(XMLHttpRequestException::create(description));
691        break;
692#if ENABLE(SVG)
693    case SVGExceptionType:
694        exception = toV8(SVGException::create(description));
695        break;
696#endif
697#if ENABLE(XPATH)
698    case XPathExceptionType:
699        exception = toV8(XPathException::create(description));
700        break;
701#endif
702#if ENABLE(DATABASE)
703    case SQLExceptionType:
704        exception = toV8(SQLException::create(description));
705        break;
706#endif
707#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
708    case FileExceptionType:
709        exception = toV8(FileException::create(description));
710        break;
711#endif
712#if ENABLE(INDEXED_DATABASE)
713    case IDBDatabaseExceptionType:
714        exception = toV8(IDBDatabaseException::create(description));
715        break;
716#endif
717    default:
718        ASSERT_NOT_REACHED();
719    }
720
721    if (!exception.IsEmpty())
722        v8::ThrowException(exception);
723}
724
725v8::Handle<v8::Value> V8Proxy::throwError(ErrorType type, const char* message)
726{
727    switch (type) {
728    case RangeError:
729        return v8::ThrowException(v8::Exception::RangeError(v8String(message)));
730    case ReferenceError:
731        return v8::ThrowException(v8::Exception::ReferenceError(v8String(message)));
732    case SyntaxError:
733        return v8::ThrowException(v8::Exception::SyntaxError(v8String(message)));
734    case TypeError:
735        return v8::ThrowException(v8::Exception::TypeError(v8String(message)));
736    case GeneralError:
737        return v8::ThrowException(v8::Exception::Error(v8String(message)));
738    default:
739        ASSERT_NOT_REACHED();
740        return notHandledByInterceptor();
741    }
742}
743
744v8::Handle<v8::Value> V8Proxy::throwTypeError()
745{
746    return throwError(TypeError, "Type error");
747}
748
749v8::Handle<v8::Value> V8Proxy::throwSyntaxError()
750{
751    return throwError(SyntaxError, "Syntax error");
752}
753
754v8::Local<v8::Context> V8Proxy::context(Frame* frame)
755{
756    v8::Local<v8::Context> context = V8Proxy::mainWorldContext(frame);
757    if (context.IsEmpty())
758        return v8::Local<v8::Context>();
759
760    if (V8IsolatedContext* isolatedContext = V8IsolatedContext::getEntered()) {
761        context = v8::Local<v8::Context>::New(isolatedContext->context());
762        if (frame != V8Proxy::retrieveFrame(context))
763            return v8::Local<v8::Context>();
764    }
765
766    return context;
767}
768
769v8::Local<v8::Context> V8Proxy::context()
770{
771    if (V8IsolatedContext* isolatedContext = V8IsolatedContext::getEntered()) {
772        RefPtr<SharedPersistent<v8::Context> > context = isolatedContext->sharedContext();
773        if (m_frame != V8Proxy::retrieveFrame(context->get()))
774            return v8::Local<v8::Context>();
775        return v8::Local<v8::Context>::New(context->get());
776    }
777    return mainWorldContext();
778}
779
780v8::Local<v8::Context> V8Proxy::mainWorldContext()
781{
782    windowShell()->initContextIfNeeded();
783    return v8::Local<v8::Context>::New(windowShell()->context());
784}
785
786v8::Local<v8::Context> V8Proxy::mainWorldContext(Frame* frame)
787{
788    V8Proxy* proxy = retrieve(frame);
789    if (!proxy)
790        return v8::Local<v8::Context>();
791
792    return proxy->mainWorldContext();
793}
794
795v8::Local<v8::Context> V8Proxy::currentContext()
796{
797    return v8::Context::GetCurrent();
798}
799
800v8::Handle<v8::Value> V8Proxy::checkNewLegal(const v8::Arguments& args)
801{
802    if (!AllowAllocation::m_current)
803        return throwError(TypeError, "Illegal constructor");
804
805    return args.This();
806}
807
808void V8Proxy::registerExtensionWithV8(v8::Extension* extension)
809{
810    // If the extension exists in our list, it was already registered with V8.
811    if (!registeredExtensionWithV8(extension))
812        v8::RegisterExtension(extension);
813}
814
815bool V8Proxy::registeredExtensionWithV8(v8::Extension* extension)
816{
817    for (size_t i = 0; i < m_extensions.size(); ++i) {
818        if (m_extensions[i] == extension)
819            return true;
820    }
821
822    return false;
823}
824
825void V8Proxy::registerExtension(v8::Extension* extension)
826{
827    registerExtensionWithV8(extension);
828    m_extensions.append(extension);
829}
830
831bool V8Proxy::setContextDebugId(int debugId)
832{
833    ASSERT(debugId > 0);
834    v8::HandleScope scope;
835    v8::Handle<v8::Context> context = windowShell()->context();
836    if (context.IsEmpty())
837        return false;
838    if (!context->GetData()->IsUndefined())
839        return false;
840
841    v8::Context::Scope contextScope(context);
842
843    char buffer[32];
844    snprintf(buffer, sizeof(buffer), "page,%d", debugId);
845    context->SetData(v8::String::New(buffer));
846
847    return true;
848}
849
850int V8Proxy::contextDebugId(v8::Handle<v8::Context> context)
851{
852    v8::HandleScope scope;
853    if (!context->GetData()->IsString())
854        return -1;
855    v8::String::AsciiValue ascii(context->GetData());
856    char* comma = strnstr(*ascii, ",", ascii.length());
857    if (!comma)
858        return -1;
859    return atoi(comma + 1);
860}
861
862v8::Local<v8::Context> toV8Context(ScriptExecutionContext* context, const WorldContextHandle& worldContext)
863{
864    if (context->isDocument()) {
865        if (V8Proxy* proxy = V8Proxy::retrieve(context))
866            return worldContext.adjustedContext(proxy);
867#if ENABLE(WORKERS)
868    } else if (context->isWorkerContext()) {
869        if (WorkerContextExecutionProxy* proxy = static_cast<WorkerContext*>(context)->script()->proxy())
870            return proxy->context();
871#endif
872    }
873    return v8::Local<v8::Context>();
874}
875
876}  // namespace WebCore
877