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