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