• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
batchConfigureAttributes(v8::Handle<v8::ObjectTemplate> instance,v8::Handle<v8::ObjectTemplate> proto,const BatchedAttribute * attributes,size_t attributeCount)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 
batchConfigureCallbacks(v8::Handle<v8::ObjectTemplate> proto,v8::Handle<v8::Signature> signature,v8::PropertyAttribute attributes,const BatchedCallback * callbacks,size_t callbackCount)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 
batchConfigureConstants(v8::Handle<v8::FunctionTemplate> functionDescriptor,v8::Handle<v8::ObjectTemplate> proto,const BatchedConstant * constants,size_t constantCount)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 
addMessageToConsole(Page * page,const String & message,const String & sourceID,unsigned lineNumber)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 
logInfo(Frame * frame,const String & message,const String & url)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 
reportUnsafeAccessTo(Frame * target)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 
handleFatalErrorInV8()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 
V8Proxy(Frame * frame)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 
~V8Proxy()197 V8Proxy::~V8Proxy()
198 {
199     clearForClose();
200     windowShell()->destroyGlobal();
201 }
202 
compileScript(v8::Handle<v8::String> code,const String & fileName,const TextPosition0 & scriptStartPosition,v8::ScriptData * scriptData)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 
handleOutOfMemory()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 
evaluateInIsolatedWorld(int worldID,const Vector<ScriptSourceCode> & sources,int extensionGroup)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 
setInjectedScriptContextDebugId(v8::Handle<v8::Context> targetContext)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 
precompileScript(v8::Handle<v8::String> code,CachedScript * cachedScript)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 
executingScript() const331 bool V8Proxy::executingScript() const
332 {
333     return m_recursion;
334 }
335 
evaluate(const ScriptSourceCode & source,Node * node)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 
runScript(v8::Handle<v8::Script> script,bool isInlineCode)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     v8::TryCatch tryCatch;
411     tryCatch.SetVerbose(true);
412     {
413         // See comment in V8Proxy::callFunction.
414         m_frame->keepAlive();
415 
416         m_recursion++;
417         result = script->Run();
418         m_recursion--;
419     }
420 
421     // Release the storage mutex if applicable.
422     didLeaveScriptContext();
423 
424     if (handleOutOfMemory())
425         ASSERT(result.IsEmpty());
426 
427     // Handle V8 internal error situation (Out-of-memory).
428     if (tryCatch.HasCaught()) {
429         ASSERT(result.IsEmpty());
430         return notHandledByInterceptor();
431     }
432 
433     if (result.IsEmpty())
434         return notHandledByInterceptor();
435 
436     // Restore inlineCode flag.
437     setInlineCode(previousInlineCode);
438 
439     if (v8::V8::IsDead())
440         handleFatalErrorInV8();
441 
442     return result;
443 }
444 
callFunction(v8::Handle<v8::Function> function,v8::Handle<v8::Object> receiver,int argc,v8::Handle<v8::Value> args[])445 v8::Local<v8::Value> V8Proxy::callFunction(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
446 {
447     V8GCController::checkMemoryUsage();
448     v8::Local<v8::Value> result;
449     {
450         if (m_recursion >= kMaxRecursionDepth) {
451             v8::Local<v8::String> code = v8::String::New("throw new RangeError('Maximum call stack size exceeded.')");
452             if (code.IsEmpty())
453                 return result;
454             v8::Local<v8::Script> script = v8::Script::Compile(code);
455             if (script.IsEmpty())
456                 return result;
457             script->Run();
458             return result;
459         }
460 
461         // Evaluating the JavaScript could cause the frame to be deallocated,
462         // so we start the keep alive timer here.
463         // Frame::keepAlive method adds the ref count of the frame and sets a
464         // timer to decrease the ref count. It assumes that the current JavaScript
465         // execution finishs before firing the timer.
466         m_frame->keepAlive();
467 
468         InspectorInstrumentationCookie cookie;
469         if (InspectorInstrumentation::hasFrontends()) {
470             v8::ScriptOrigin origin = function->GetScriptOrigin();
471             String resourceName("undefined");
472             int lineNumber = 1;
473             if (!origin.ResourceName().IsEmpty()) {
474                 resourceName = toWebCoreString(origin.ResourceName());
475                 lineNumber = function->GetScriptLineNumber() + 1;
476             }
477             cookie = InspectorInstrumentation::willCallFunction(m_frame, resourceName, lineNumber);
478         }
479 
480         m_recursion++;
481         result = function->Call(receiver, argc, args);
482         m_recursion--;
483 
484         InspectorInstrumentation::didCallFunction(cookie);
485     }
486 
487     // Release the storage mutex if applicable.
488     didLeaveScriptContext();
489 
490     if (v8::V8::IsDead())
491         handleFatalErrorInV8();
492 
493     return result;
494 }
495 
callFunctionWithoutFrame(v8::Handle<v8::Function> function,v8::Handle<v8::Object> receiver,int argc,v8::Handle<v8::Value> args[])496 v8::Local<v8::Value> V8Proxy::callFunctionWithoutFrame(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
497 {
498     V8GCController::checkMemoryUsage();
499     v8::Local<v8::Value> result = function->Call(receiver, argc, args);
500 
501     if (v8::V8::IsDead())
502         handleFatalErrorInV8();
503 
504     return result;
505 }
506 
newInstance(v8::Handle<v8::Function> constructor,int argc,v8::Handle<v8::Value> args[])507 v8::Local<v8::Value> V8Proxy::newInstance(v8::Handle<v8::Function> constructor, int argc, v8::Handle<v8::Value> args[])
508 {
509     // No artificial limitations on the depth of recursion, see comment in
510     // V8Proxy::callFunction.
511     v8::Local<v8::Value> result;
512     {
513         // See comment in V8Proxy::callFunction.
514         m_frame->keepAlive();
515 
516         result = constructor->NewInstance(argc, args);
517     }
518 
519     if (v8::V8::IsDead())
520         handleFatalErrorInV8();
521 
522     return result;
523 }
524 
retrieveWindow(v8::Handle<v8::Context> context)525 DOMWindow* V8Proxy::retrieveWindow(v8::Handle<v8::Context> context)
526 {
527     v8::Handle<v8::Object> global = context->Global();
528     ASSERT(!global.IsEmpty());
529     global = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), global);
530     ASSERT(!global.IsEmpty());
531     return V8DOMWindow::toNative(global);
532 }
533 
retrieveFrame(v8::Handle<v8::Context> context)534 Frame* V8Proxy::retrieveFrame(v8::Handle<v8::Context> context)
535 {
536     DOMWindow* window = retrieveWindow(context);
537     Frame* frame = window->frame();
538     if (frame && frame->domWindow() == window)
539         return frame;
540     // We return 0 here because |context| is detached from the Frame.  If we
541     // did return |frame| we could get in trouble because the frame could be
542     // navigated to another security origin.
543     return 0;
544 }
545 
retrieveFrameForEnteredContext()546 Frame* V8Proxy::retrieveFrameForEnteredContext()
547 {
548     v8::Handle<v8::Context> context = v8::Context::GetEntered();
549     if (context.IsEmpty())
550         return 0;
551     return retrieveFrame(context);
552 }
553 
retrieveFrameForCurrentContext()554 Frame* V8Proxy::retrieveFrameForCurrentContext()
555 {
556     v8::Handle<v8::Context> context = v8::Context::GetCurrent();
557     if (context.IsEmpty())
558         return 0;
559     return retrieveFrame(context);
560 }
561 
retrieveFrameForCallingContext()562 Frame* V8Proxy::retrieveFrameForCallingContext()
563 {
564     v8::Handle<v8::Context> context = v8::Context::GetCalling();
565     if (context.IsEmpty())
566         return 0;
567     return retrieveFrame(context);
568 }
569 
retrieve()570 V8Proxy* V8Proxy::retrieve()
571 {
572     DOMWindow* window = retrieveWindow(currentContext());
573     ASSERT(window);
574     return retrieve(window->frame());
575 }
576 
retrieve(Frame * frame)577 V8Proxy* V8Proxy::retrieve(Frame* frame)
578 {
579     if (!frame)
580         return 0;
581     return frame->script()->canExecuteScripts(NotAboutToExecuteScript) ? frame->script()->proxy() : 0;
582 }
583 
retrieve(ScriptExecutionContext * context)584 V8Proxy* V8Proxy::retrieve(ScriptExecutionContext* context)
585 {
586     if (!context || !context->isDocument())
587         return 0;
588     return retrieve(static_cast<Document*>(context)->frame());
589 }
590 
disconnectFrame()591 void V8Proxy::disconnectFrame()
592 {
593 }
594 
didLeaveScriptContext()595 void V8Proxy::didLeaveScriptContext()
596 {
597     Page* page = m_frame->page();
598     if (!page)
599         return;
600 #if ENABLE(INDEXED_DATABASE)
601     // If we've just left a script context and indexed database has been
602     // instantiated, we must let its transaction coordinator know so it can terminate
603     // any not-yet-started transactions.
604     IDBPendingTransactionMonitor::abortPendingTransactions();
605 #endif // ENABLE(INDEXED_DATABASE)
606     // If we've just left a top level script context and local storage has been
607     // instantiated, we must ensure that any storage locks have been freed.
608     // Per http://dev.w3.org/html5/spec/Overview.html#storage-mutex
609     if (m_recursion != 0)
610         return;
611     if (page->group().hasLocalStorage())
612         page->group().localStorage()->unlock();
613 }
614 
resetIsolatedWorlds()615 void V8Proxy::resetIsolatedWorlds()
616 {
617     for (IsolatedWorldMap::iterator iter = m_isolatedWorlds.begin();
618          iter != m_isolatedWorlds.end(); ++iter) {
619         iter->second->destroy();
620     }
621     m_isolatedWorlds.clear();
622 }
623 
clearForClose()624 void V8Proxy::clearForClose()
625 {
626     resetIsolatedWorlds();
627     windowShell()->clearForClose();
628 }
629 
clearForNavigation()630 void V8Proxy::clearForNavigation()
631 {
632     resetIsolatedWorlds();
633     windowShell()->clearForNavigation();
634 }
635 
setDOMException(int exceptionCode)636 void V8Proxy::setDOMException(int exceptionCode)
637 {
638     if (exceptionCode <= 0)
639         return;
640 
641     ExceptionCodeDescription description;
642     getExceptionCodeDescription(exceptionCode, description);
643 
644     v8::Handle<v8::Value> exception;
645     switch (description.type) {
646     case DOMExceptionType:
647         exception = toV8(DOMCoreException::create(description));
648         break;
649     case RangeExceptionType:
650         exception = toV8(RangeException::create(description));
651         break;
652     case EventExceptionType:
653         exception = toV8(EventException::create(description));
654         break;
655     case XMLHttpRequestExceptionType:
656         exception = toV8(XMLHttpRequestException::create(description));
657         break;
658 #if ENABLE(SVG)
659     case SVGExceptionType:
660         exception = toV8(SVGException::create(description));
661         break;
662 #endif
663 #if ENABLE(XPATH)
664     case XPathExceptionType:
665         exception = toV8(XPathException::create(description));
666         break;
667 #endif
668 #if ENABLE(DATABASE)
669     case SQLExceptionType:
670         exception = toV8(SQLException::create(description));
671         break;
672 #endif
673 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
674     case FileExceptionType:
675         exception = toV8(FileException::create(description));
676         break;
677 #endif
678 #if ENABLE(INDEXED_DATABASE)
679     case IDBDatabaseExceptionType:
680         exception = toV8(IDBDatabaseException::create(description));
681         break;
682 #endif
683     default:
684         ASSERT_NOT_REACHED();
685     }
686 
687     if (!exception.IsEmpty())
688         v8::ThrowException(exception);
689 }
690 
throwError(ErrorType type,const char * message)691 v8::Handle<v8::Value> V8Proxy::throwError(ErrorType type, const char* message)
692 {
693     switch (type) {
694     case RangeError:
695         return v8::ThrowException(v8::Exception::RangeError(v8String(message)));
696     case ReferenceError:
697         return v8::ThrowException(v8::Exception::ReferenceError(v8String(message)));
698     case SyntaxError:
699         return v8::ThrowException(v8::Exception::SyntaxError(v8String(message)));
700     case TypeError:
701         return v8::ThrowException(v8::Exception::TypeError(v8String(message)));
702     case GeneralError:
703         return v8::ThrowException(v8::Exception::Error(v8String(message)));
704     default:
705         ASSERT_NOT_REACHED();
706         return notHandledByInterceptor();
707     }
708 }
709 
throwTypeError()710 v8::Handle<v8::Value> V8Proxy::throwTypeError()
711 {
712     return throwError(TypeError, "Type error");
713 }
714 
throwSyntaxError()715 v8::Handle<v8::Value> V8Proxy::throwSyntaxError()
716 {
717     return throwError(SyntaxError, "Syntax error");
718 }
719 
context(Frame * frame)720 v8::Local<v8::Context> V8Proxy::context(Frame* frame)
721 {
722     v8::Local<v8::Context> context = V8Proxy::mainWorldContext(frame);
723     if (context.IsEmpty())
724         return v8::Local<v8::Context>();
725 
726     if (V8IsolatedContext* isolatedContext = V8IsolatedContext::getEntered()) {
727         context = v8::Local<v8::Context>::New(isolatedContext->context());
728         if (frame != V8Proxy::retrieveFrame(context))
729             return v8::Local<v8::Context>();
730     }
731 
732     return context;
733 }
734 
context()735 v8::Local<v8::Context> V8Proxy::context()
736 {
737     if (V8IsolatedContext* isolatedContext = V8IsolatedContext::getEntered()) {
738         RefPtr<SharedPersistent<v8::Context> > context = isolatedContext->sharedContext();
739         if (m_frame != V8Proxy::retrieveFrame(context->get()))
740             return v8::Local<v8::Context>();
741         return v8::Local<v8::Context>::New(context->get());
742     }
743     return mainWorldContext();
744 }
745 
mainWorldContext()746 v8::Local<v8::Context> V8Proxy::mainWorldContext()
747 {
748     windowShell()->initContextIfNeeded();
749     return v8::Local<v8::Context>::New(windowShell()->context());
750 }
751 
mainWorldContext(Frame * frame)752 v8::Local<v8::Context> V8Proxy::mainWorldContext(Frame* frame)
753 {
754     V8Proxy* proxy = retrieve(frame);
755     if (!proxy)
756         return v8::Local<v8::Context>();
757 
758     return proxy->mainWorldContext();
759 }
760 
currentContext()761 v8::Local<v8::Context> V8Proxy::currentContext()
762 {
763     return v8::Context::GetCurrent();
764 }
765 
checkNewLegal(const v8::Arguments & args)766 v8::Handle<v8::Value> V8Proxy::checkNewLegal(const v8::Arguments& args)
767 {
768     if (!AllowAllocation::m_current)
769         return throwError(TypeError, "Illegal constructor");
770 
771     return args.This();
772 }
773 
registerExtensionWithV8(v8::Extension * extension)774 void V8Proxy::registerExtensionWithV8(v8::Extension* extension)
775 {
776     // If the extension exists in our list, it was already registered with V8.
777     if (!registeredExtensionWithV8(extension))
778         v8::RegisterExtension(extension);
779 }
780 
registeredExtensionWithV8(v8::Extension * extension)781 bool V8Proxy::registeredExtensionWithV8(v8::Extension* extension)
782 {
783     for (size_t i = 0; i < m_extensions.size(); ++i) {
784         if (m_extensions[i] == extension)
785             return true;
786     }
787 
788     return false;
789 }
790 
registerExtension(v8::Extension * extension)791 void V8Proxy::registerExtension(v8::Extension* extension)
792 {
793     registerExtensionWithV8(extension);
794     m_extensions.append(extension);
795 }
796 
setContextDebugId(int debugId)797 bool V8Proxy::setContextDebugId(int debugId)
798 {
799     ASSERT(debugId > 0);
800     v8::HandleScope scope;
801     v8::Handle<v8::Context> context = windowShell()->context();
802     if (context.IsEmpty())
803         return false;
804     if (!context->GetData()->IsUndefined())
805         return false;
806 
807     v8::Context::Scope contextScope(context);
808 
809     char buffer[32];
810     snprintf(buffer, sizeof(buffer), "page,%d", debugId);
811     context->SetData(v8::String::New(buffer));
812 
813     return true;
814 }
815 
contextDebugId(v8::Handle<v8::Context> context)816 int V8Proxy::contextDebugId(v8::Handle<v8::Context> context)
817 {
818     v8::HandleScope scope;
819     if (!context->GetData()->IsString())
820         return -1;
821     v8::String::AsciiValue ascii(context->GetData());
822     char* comma = strnstr(*ascii, ",", ascii.length());
823     if (!comma)
824         return -1;
825     return atoi(comma + 1);
826 }
827 
toV8Context(ScriptExecutionContext * context,const WorldContextHandle & worldContext)828 v8::Local<v8::Context> toV8Context(ScriptExecutionContext* context, const WorldContextHandle& worldContext)
829 {
830     if (context->isDocument()) {
831         if (V8Proxy* proxy = V8Proxy::retrieve(context))
832             return worldContext.adjustedContext(proxy);
833 #if ENABLE(WORKERS)
834     } else if (context->isWorkerContext()) {
835         if (WorkerContextExecutionProxy* proxy = static_cast<WorkerContext*>(context)->script()->proxy())
836             return proxy->context();
837 #endif
838     }
839     return v8::Local<v8::Context>();
840 }
841 
842 }  // namespace WebCore
843