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