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