• 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 "CString.h"
36 #include "DOMObjectsInclude.h"
37 #include "DocumentLoader.h"
38 #include "FrameLoaderClient.h"
39 #include "ScriptController.h"
40 #include "V8Binding.h"
41 #include "V8Collection.h"
42 #include "V8ConsoleMessage.h"
43 #include "V8CustomBinding.h"
44 #include "V8DOMMap.h"
45 #include "V8DOMWindow.h"
46 #include "V8HiddenPropertyName.h"
47 #include "V8Index.h"
48 #include "V8IsolatedWorld.h"
49 
50 #include <v8.h>
51 #include <v8-debug.h>
52 #include <wtf/Assertions.h>
53 #include <wtf/OwnArrayPtr.h>
54 #include <wtf/StdLibExtras.h>
55 #include <wtf/UnusedParam.h>
56 
57 #if PLATFORM(CHROMIUM)
58 #include "ChromiumBridge.h"
59 #endif
60 
61 #ifdef ANDROID_INSTRUMENT
62 #include "TimeCounter.h"
63 #endif
64 
65 namespace WebCore {
66 
67 v8::Persistent<v8::Context> V8Proxy::m_utilityContext;
68 
69 // Static list of registered extensions
70 V8ExtensionList V8Proxy::m_extensions;
71 
72 const char* V8Proxy::kContextDebugDataType = "type";
73 const char* V8Proxy::kContextDebugDataValue = "value";
74 
batchConfigureAttributes(v8::Handle<v8::ObjectTemplate> instance,v8::Handle<v8::ObjectTemplate> proto,const BatchedAttribute * attributes,size_t attributeCount)75 void batchConfigureAttributes(v8::Handle<v8::ObjectTemplate> instance, v8::Handle<v8::ObjectTemplate> proto, const BatchedAttribute* attributes, size_t attributeCount)
76 {
77     for (size_t i = 0; i < attributeCount; ++i) {
78         const BatchedAttribute* attribute = &attributes[i];
79         (attribute->onProto ? proto : instance)->SetAccessor(v8::String::New(attribute->name),
80             attribute->getter,
81             attribute->setter,
82             attribute->data == V8ClassIndex::INVALID_CLASS_INDEX ? v8::Handle<v8::Value>() : v8::Integer::New(V8ClassIndex::ToInt(attribute->data)),
83             attribute->settings,
84             attribute->attribute);
85     }
86 }
87 
batchConfigureConstants(v8::Handle<v8::FunctionTemplate> functionDescriptor,v8::Handle<v8::ObjectTemplate> proto,const BatchedConstant * constants,size_t constantCount)88 void batchConfigureConstants(v8::Handle<v8::FunctionTemplate> functionDescriptor, v8::Handle<v8::ObjectTemplate> proto, const BatchedConstant* constants, size_t constantCount)
89 {
90     for (size_t i = 0; i < constantCount; ++i) {
91         const BatchedConstant* constant = &constants[i];
92         functionDescriptor->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
93         proto->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
94     }
95 }
96 
97 typedef HashMap<Node*, v8::Object*> DOMNodeMap;
98 typedef HashMap<void*, v8::Object*> DOMObjectMap;
99 
100 #if ENABLE(SVG)
101 // Map of SVG objects with contexts to their contexts
svgObjectToContextMap()102 static HashMap<void*, SVGElement*>& svgObjectToContextMap()
103 {
104     typedef HashMap<void*, SVGElement*> SvgObjectToContextMap;
105     DEFINE_STATIC_LOCAL(SvgObjectToContextMap, staticSvgObjectToContextMap, ());
106     return staticSvgObjectToContextMap;
107 }
108 
setSVGContext(void * object,SVGElement * context)109 void V8Proxy::setSVGContext(void* object, SVGElement* context)
110 {
111     if (!object)
112         return;
113 
114     SVGElement* oldContext = svgObjectToContextMap().get(object);
115 
116     if (oldContext == context)
117         return;
118 
119     if (oldContext)
120         oldContext->deref();
121 
122     if (context)
123         context->ref();
124 
125     svgObjectToContextMap().set(object, context);
126 }
127 
svgContext(void * object)128 SVGElement* V8Proxy::svgContext(void* object)
129 {
130     return svgObjectToContextMap().get(object);
131 }
132 
133 #endif
134 
135 typedef HashMap<int, v8::FunctionTemplate*> FunctionTemplateMap;
136 
137 bool AllowAllocation::m_current = false;
138 
logInfo(Frame * frame,const String & message,const String & url)139 void logInfo(Frame* frame, const String& message, const String& url)
140 {
141     Page* page = frame->page();
142     if (!page)
143         return;
144     V8ConsoleMessage consoleMessage(message, url, 0);
145     consoleMessage.dispatchNow(page);
146 }
147 
148 enum DelayReporting {
149     ReportLater,
150     ReportNow
151 };
152 
reportUnsafeAccessTo(Frame * target,DelayReporting delay)153 static void reportUnsafeAccessTo(Frame* target, DelayReporting delay)
154 {
155     ASSERT(target);
156     Document* targetDocument = target->document();
157     if (!targetDocument)
158         return;
159 
160     Frame* source = V8Proxy::retrieveFrameForEnteredContext();
161     if (!source || !source->document())
162         return; // Ignore error if the source document is gone.
163 
164     Document* sourceDocument = source->document();
165 
166     // FIXME: This error message should contain more specifics of why the same
167     // origin check has failed.
168     String str = String::format("Unsafe JavaScript attempt to access frame "
169                                 "with URL %s from frame with URL %s. "
170                                 "Domains, protocols and ports must match.\n",
171                                 targetDocument->url().string().utf8().data(),
172                                 sourceDocument->url().string().utf8().data());
173 
174     // Build a console message with fake source ID and line number.
175     const String kSourceID = "";
176     const int kLineNumber = 1;
177     V8ConsoleMessage message(str, kSourceID, kLineNumber);
178 
179     if (delay == ReportNow) {
180         // NOTE: Safari prints the message in the target page, but it seems like
181         // it should be in the source page. Even for delayed messages, we put it in
182         // the source page; see V8ConsoleMessage::processDelayed().
183         message.dispatchNow(source->page());
184     } else {
185         ASSERT(delay == ReportLater);
186         // We cannot safely report the message eagerly, because this may cause
187         // allocations and GCs internally in V8 and we cannot handle that at this
188         // point. Therefore we delay the reporting.
189         message.dispatchLater();
190     }
191 }
192 
reportUnsafeJavaScriptAccess(v8::Local<v8::Object> host,v8::AccessType type,v8::Local<v8::Value> data)193 static void reportUnsafeJavaScriptAccess(v8::Local<v8::Object> host, v8::AccessType type, v8::Local<v8::Value> data)
194 {
195     Frame* target = V8Custom::GetTargetFrame(host, data);
196     if (target)
197         reportUnsafeAccessTo(target, ReportLater);
198 }
199 
handleFatalErrorInV8()200 static void handleFatalErrorInV8()
201 {
202     // FIXME: We temporarily deal with V8 internal error situations
203     // such as out-of-memory by crashing the renderer.
204     CRASH();
205 }
206 
reportFatalErrorInV8(const char * location,const char * message)207 static void reportFatalErrorInV8(const char* location, const char* message)
208 {
209     // V8 is shutdown, we cannot use V8 api.
210     // The only thing we can do is to disable JavaScript.
211     // FIXME: clean up V8Proxy and disable JavaScript.
212     printf("V8 error: %s (%s)\n", message, location);
213     handleFatalErrorInV8();
214 }
215 
~V8Proxy()216 V8Proxy::~V8Proxy()
217 {
218     clearForClose();
219     destroyGlobal();
220 }
221 
destroyGlobal()222 void V8Proxy::destroyGlobal()
223 {
224     if (!m_global.IsEmpty()) {
225 #ifndef NDEBUG
226         V8GCController::unregisterGlobalHandle(this, m_global);
227 #endif
228         m_global.Dispose();
229         m_global.Clear();
230     }
231 }
232 
disconnectEventListenersInList(V8EventListenerList & list)233 static void disconnectEventListenersInList(V8EventListenerList& list)
234 {
235     V8EventListenerList::iterator it = list.begin();
236     while (it != list.end()) {
237         (*it)->disconnectFrame();
238         ++it;
239     }
240     list.clear();
241 }
242 
disconnectEventListeners()243 void V8Proxy::disconnectEventListeners()
244 {
245     disconnectEventListenersInList(m_eventListeners);
246     disconnectEventListenersInList(m_objectListeners);
247 }
248 
compileScript(v8::Handle<v8::String> code,const String & fileName,int baseLine)249 v8::Handle<v8::Script> V8Proxy::compileScript(v8::Handle<v8::String> code, const String& fileName, int baseLine)
250 #ifdef ANDROID_INSTRUMENT
251 {
252     android::TimeCounter::start(android::TimeCounter::JavaScriptParseTimeCounter);
253     v8::Handle<v8::Script> script = compileScriptInternal(code, fileName, baseLine);
254     android::TimeCounter::record(android::TimeCounter::JavaScriptParseTimeCounter, __FUNCTION__);
255     return script;
256 }
257 
compileScriptInternal(v8::Handle<v8::String> code,const String & fileName,int baseLine)258 v8::Handle<v8::Script> V8Proxy::compileScriptInternal(v8::Handle<v8::String> code, const String& fileName, int baseLine)
259 #endif
260 {
261     const uint16_t* fileNameString = fromWebCoreString(fileName);
262     v8::Handle<v8::String> name = v8::String::New(fileNameString, fileName.length());
263     v8::Handle<v8::Integer> line = v8::Integer::New(baseLine);
264     v8::ScriptOrigin origin(name, line);
265     v8::Handle<v8::Script> script = v8::Script::Compile(code, &origin);
266     return script;
267 }
268 
handleOutOfMemory()269 bool V8Proxy::handleOutOfMemory()
270 {
271     v8::Local<v8::Context> context = v8::Context::GetCurrent();
272 
273     if (!context->HasOutOfMemoryException())
274         return false;
275 
276     // Warning, error, disable JS for this frame?
277     Frame* frame = V8Proxy::retrieveFrame(context);
278 
279     V8Proxy* proxy = V8Proxy::retrieve(frame);
280     if (proxy) {
281         // Clean m_context, and event handlers.
282         proxy->clearForClose();
283 
284         proxy->destroyGlobal();
285     }
286 
287 #if PLATFORM(CHROMIUM)
288     // TODO (andreip): ChromeBridge -> BrowserBridge?
289     ChromiumBridge::notifyJSOutOfMemory(frame);
290 #endif
291     // Disable JS.
292     Settings* settings = frame->settings();
293     ASSERT(settings);
294     settings->setJavaScriptEnabled(false);
295 
296     return true;
297 }
298 
evaluateInNewWorld(const Vector<ScriptSourceCode> & sources,int extensionGroup)299 void V8Proxy::evaluateInNewWorld(const Vector<ScriptSourceCode>& sources, int extensionGroup)
300 {
301     initContextIfNeeded();
302     V8IsolatedWorld::evaluate(sources, this, extensionGroup);
303 }
304 
evaluateInNewContext(const Vector<ScriptSourceCode> & sources,int extensionGroup)305 void V8Proxy::evaluateInNewContext(const Vector<ScriptSourceCode>& sources, int extensionGroup)
306 {
307     initContextIfNeeded();
308 
309     v8::HandleScope handleScope;
310 
311     // Set up the DOM window as the prototype of the new global object.
312     v8::Handle<v8::Context> windowContext = m_context;
313     v8::Handle<v8::Object> windowGlobal = windowContext->Global();
314     v8::Handle<v8::Object> windowWrapper = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, windowGlobal);
315 
316     ASSERT(V8DOMWrapper::convertDOMWrapperToNative<DOMWindow>(windowWrapper) == m_frame->domWindow());
317 
318     v8::Persistent<v8::Context> context = createNewContext(v8::Handle<v8::Object>(), extensionGroup);
319     v8::Context::Scope contextScope(context);
320 
321     // Setup context id for JS debugger.
322     v8::Handle<v8::Object> contextData = v8::Object::New();
323     v8::Handle<v8::Value> windowContextData = windowContext->GetData();
324     if (windowContextData->IsObject()) {
325         v8::Handle<v8::String> propertyName = v8::String::New(kContextDebugDataValue);
326         contextData->Set(propertyName, v8::Object::Cast(*windowContextData)->Get(propertyName));
327     }
328     contextData->Set(v8::String::New(kContextDebugDataType), v8::String::New("injected"));
329     context->SetData(contextData);
330 
331     v8::Handle<v8::Object> global = context->Global();
332 
333     v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__");
334     global->Set(implicitProtoString, windowWrapper);
335 
336     // Give the code running in the new context a way to get access to the
337     // original context.
338     global->Set(v8::String::New("contentWindow"), windowGlobal);
339 
340     m_frame->loader()->client()->didCreateIsolatedScriptContext();
341 
342     // Run code in the new context.
343     for (size_t i = 0; i < sources.size(); ++i)
344         evaluate(sources[i], 0);
345 
346     // Using the default security token means that the canAccess is always
347     // called, which is slow.
348     // FIXME: Use tokens where possible. This will mean keeping track of all
349     // created contexts so that they can all be updated when the document domain
350     // changes.
351     context->UseDefaultSecurityToken();
352     context.Dispose();
353 }
354 
evaluate(const ScriptSourceCode & source,Node * node)355 v8::Local<v8::Value> V8Proxy::evaluate(const ScriptSourceCode& source, Node* node)
356 {
357     ASSERT(v8::Context::InContext());
358     LOCK_V8;
359 
360     // Compile the script.
361     v8::Local<v8::String> code = v8ExternalString(source.source());
362 #if PLATFORM(CHROMIUM)
363     // TODO(andreip): ChromeBridge->BrowserBridge?
364     ChromiumBridge::traceEventBegin("v8.compile", node, "");
365 #endif
366 
367     // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at
368     // 1, whereas v8 starts at 0.
369     v8::Handle<v8::Script> script = compileScript(code, source.url(), source.startLine() - 1);
370 #if PLATFORM(CHROMIUM)
371     // TODO(andreip): ChromeBridge->BrowserBridge?
372     ChromiumBridge::traceEventEnd("v8.compile", node, "");
373     ChromiumBridge::traceEventBegin("v8.run", node, "");
374 #endif
375     v8::Local<v8::Value> result;
376     {
377         // Isolate exceptions that occur when executing the code. These
378         // exceptions should not interfere with javascript code we might
379         // evaluate from C++ when returning from here.
380         v8::TryCatch tryCatch;
381         tryCatch.SetVerbose(true);
382 
383         // Set inlineCode to true for <a href="javascript:doSomething()">
384         // and false for <script>doSomething</script>. We make a rough guess at
385         // this based on whether the script source has a URL.
386         result = runScript(script, source.url().string().isNull());
387     }
388 #if PLATFORM(CHROMIUM)
389     // TODO(andreip): ChromeBridge->BrowserBridge?
390     ChromiumBridge::traceEventEnd("v8.run", node, "");
391 #endif
392     return result;
393 }
394 
runScript(v8::Handle<v8::Script> script,bool isInlineCode)395 v8::Local<v8::Value> V8Proxy::runScript(v8::Handle<v8::Script> script, bool isInlineCode)
396 #ifdef ANDROID_INSTRUMENT
397 {
398     android::TimeCounter::start(android::TimeCounter::JavaScriptExecuteTimeCounter);
399     v8::Local<v8::Value> result = runScriptInternal(script, isInlineCode);
400     android::TimeCounter::record(android::TimeCounter::JavaScriptExecuteTimeCounter, __FUNCTION__);
401     return result;
402 }
403 
runScriptInternal(v8::Handle<v8::Script> script,bool isInlineCode)404 v8::Local<v8::Value> V8Proxy::runScriptInternal(v8::Handle<v8::Script> script, bool isInlineCode)
405 #endif
406 {
407     if (script.IsEmpty())
408         return notHandledByInterceptor();
409 
410     // Compute the source string and prevent against infinite recursion.
411     if (m_recursion >= kMaxRecursionDepth) {
412         v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')");
413         // FIXME: Ideally, we should be able to re-use the origin of the
414         // script passed to us as the argument instead of using an empty string
415         // and 0 baseLine.
416         script = compileScript(code, "", 0);
417     }
418 
419     if (handleOutOfMemory())
420         ASSERT(script.IsEmpty());
421 
422     if (script.IsEmpty())
423         return notHandledByInterceptor();
424 
425     // Save the previous value of the inlineCode flag and update the flag for
426     // the duration of the script invocation.
427     bool previousInlineCode = inlineCode();
428     setInlineCode(isInlineCode);
429 
430     // Run the script and keep track of the current recursion depth.
431     v8::Local<v8::Value> result;
432     {
433         V8ConsoleMessage::Scope scope;
434         m_recursion++;
435 
436         // See comment in V8Proxy::callFunction.
437         m_frame->keepAlive();
438 
439         result = script->Run();
440         m_recursion--;
441     }
442 
443     if (handleOutOfMemory())
444         ASSERT(result.IsEmpty());
445 
446     // Handle V8 internal error situation (Out-of-memory).
447     if (result.IsEmpty())
448         return notHandledByInterceptor();
449 
450     // Restore inlineCode flag.
451     setInlineCode(previousInlineCode);
452 
453     if (v8::V8::IsDead())
454         handleFatalErrorInV8();
455 
456     return result;
457 }
458 
callFunction(v8::Handle<v8::Function> function,v8::Handle<v8::Object> receiver,int argc,v8::Handle<v8::Value> args[])459 v8::Local<v8::Value> V8Proxy::callFunction(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
460 {
461 #ifdef ANDROID_INSTRUMENT
462     android::TimeCounter::start(android::TimeCounter::JavaScriptExecuteTimeCounter);
463 #endif
464 
465     // For now, we don't put any artificial limitations on the depth
466     // of recursion that stems from calling functions. This is in
467     // contrast to the script evaluations.
468     v8::Local<v8::Value> result;
469     {
470         V8ConsoleMessage::Scope scope;
471 
472         // Evaluating the JavaScript could cause the frame to be deallocated,
473         // so we start the keep alive timer here.
474         // Frame::keepAlive method adds the ref count of the frame and sets a
475         // timer to decrease the ref count. It assumes that the current JavaScript
476         // execution finishs before firing the timer.
477         m_frame->keepAlive();
478 
479         result = function->Call(receiver, argc, args);
480     }
481 
482     if (v8::V8::IsDead())
483         handleFatalErrorInV8();
484 
485 #ifdef ANDROID_INSTRUMENT
486     android::TimeCounter::record(android::TimeCounter::JavaScriptExecuteTimeCounter, __FUNCTION__);
487 #endif
488     return result;
489 }
490 
newInstance(v8::Handle<v8::Function> constructor,int argc,v8::Handle<v8::Value> args[])491 v8::Local<v8::Value> V8Proxy::newInstance(v8::Handle<v8::Function> constructor, int argc, v8::Handle<v8::Value> args[])
492 {
493     // No artificial limitations on the depth of recursion, see comment in
494     // V8Proxy::callFunction.
495     v8::Local<v8::Value> result;
496     {
497         V8ConsoleMessage::Scope scope;
498 
499         // See comment in V8Proxy::callFunction.
500         m_frame->keepAlive();
501 
502         result = constructor->NewInstance(argc, args);
503     }
504 
505     if (v8::V8::IsDead())
506         handleFatalErrorInV8();
507 
508     return result;
509 }
510 
createWrapperFromCache(V8ClassIndex::V8WrapperType type)511 v8::Local<v8::Object> V8Proxy::createWrapperFromCache(V8ClassIndex::V8WrapperType type)
512 {
513     int classIndex = V8ClassIndex::ToInt(type);
514     v8::Local<v8::Object> clone(m_wrapperBoilerplates->CloneElementAt(classIndex));
515     if (!clone.IsEmpty())
516         return clone;
517 
518     // Not in cache.
519     initContextIfNeeded();
520     v8::Context::Scope scope(m_context);
521     v8::Local<v8::Function> function = V8DOMWrapper::getConstructor(type, getHiddenObjectPrototype(m_context));
522     v8::Local<v8::Object> instance = SafeAllocation::newInstance(function);
523     if (!instance.IsEmpty()) {
524         m_wrapperBoilerplates->Set(v8::Integer::New(classIndex), instance);
525         return instance->Clone();
526     }
527     return notHandledByInterceptor();
528 }
529 
isContextInitialized()530 bool V8Proxy::isContextInitialized()
531 {
532     // m_context, m_global, and m_wrapperBoilerplates should
533     // all be non-empty if if m_context is non-empty.
534     ASSERT(m_context.IsEmpty() || !m_global.IsEmpty());
535     ASSERT(m_context.IsEmpty() || !m_wrapperBoilerplates.IsEmpty());
536     return !m_context.IsEmpty();
537 }
538 
retrieveWindow(v8::Handle<v8::Context> context)539 DOMWindow* V8Proxy::retrieveWindow(v8::Handle<v8::Context> context)
540 {
541     v8::Handle<v8::Object> global = context->Global();
542     ASSERT(!global.IsEmpty());
543     global = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, global);
544     ASSERT(!global.IsEmpty());
545     return V8DOMWrapper::convertToNativeObject<DOMWindow>(V8ClassIndex::DOMWINDOW, global);
546 }
547 
retrieveFrame(v8::Handle<v8::Context> context)548 Frame* V8Proxy::retrieveFrame(v8::Handle<v8::Context> context)
549 {
550     return retrieveWindow(context)->frame();
551 }
552 
retrieveFrameForEnteredContext()553 Frame* V8Proxy::retrieveFrameForEnteredContext()
554 {
555     v8::Handle<v8::Context> context = v8::Context::GetEntered();
556     if (context.IsEmpty())
557         return 0;
558     return retrieveFrame(context);
559 }
560 
retrieveFrameForCurrentContext()561 Frame* V8Proxy::retrieveFrameForCurrentContext()
562 {
563     v8::Handle<v8::Context> context = v8::Context::GetCurrent();
564     if (context.IsEmpty())
565         return 0;
566     return retrieveFrame(context);
567 }
568 
retrieveFrameForCallingContext()569 Frame* V8Proxy::retrieveFrameForCallingContext()
570 {
571     v8::Handle<v8::Context> context = v8::Context::GetCalling();
572     if (context.IsEmpty())
573         return 0;
574     return retrieveFrame(context);
575 }
576 
retrieve()577 V8Proxy* V8Proxy::retrieve()
578 {
579     DOMWindow* window = retrieveWindow(currentContext());
580     ASSERT(window);
581     return retrieve(window->frame());
582 }
583 
retrieve(Frame * frame)584 V8Proxy* V8Proxy::retrieve(Frame* frame)
585 {
586     if (!frame)
587         return 0;
588     return frame->script()->isEnabled() ? frame->script()->proxy() : 0;
589 }
590 
retrieve(ScriptExecutionContext * context)591 V8Proxy* V8Proxy::retrieve(ScriptExecutionContext* context)
592 {
593     if (!context->isDocument())
594         return 0;
595     return retrieve(static_cast<Document*>(context)->frame());
596 }
597 
disconnectFrame()598 void V8Proxy::disconnectFrame()
599 {
600     disconnectEventListeners();
601 }
602 
isEnabled()603 bool V8Proxy::isEnabled()
604 {
605     Settings* settings = m_frame->settings();
606     if (!settings)
607         return false;
608 
609     // In the common case, JavaScript is enabled and we're done.
610     if (settings->isJavaScriptEnabled())
611         return true;
612 
613     // If JavaScript has been disabled, we need to look at the frame to tell
614     // whether this script came from the web or the embedder. Scripts from the
615     // embedder are safe to run, but scripts from the other sources are
616     // disallowed.
617     Document* document = m_frame->document();
618     if (!document)
619         return false;
620 
621     SecurityOrigin* origin = document->securityOrigin();
622     if (origin->protocol().isEmpty())
623         return false; // Uninitialized document
624 
625     if (origin->protocol() == "http" || origin->protocol() == "https")
626         return false; // Web site
627 
628     // FIXME: the following are application decisions, and they should
629     // not be made at this layer. instead, we should bridge out to the
630     // embedder to allow them to override policy here.
631 
632 #if PLATFORM(CHROMIUM)
633     // TODO(andreip): ChromeBridge->BrowserBridge?
634     if (origin->protocol() == ChromiumBridge::uiResourceProtocol())
635         return true;   // Embedder's scripts are ok to run
636 #endif
637 
638     // If the scheme is ftp: or file:, an empty file name indicates a directory
639     // listing, which requires JavaScript to function properly.
640     const char* kDirProtocols[] = { "ftp", "file" };
641 #if PLATFORM(ANDROID)
642     // TODO(andreip): Port arraysize function to Android. There's one in Gears.
643     for (size_t i = 0; i < 2; ++i) {
644 #else
645     for (size_t i = 0; i < arraysize(kDirProtocols); ++i) {
646 #endif
647         if (origin->protocol() == kDirProtocols[i]) {
648             const KURL& url = document->url();
649             return url.pathAfterLastSlash() == url.pathEnd();
650         }
651     }
652 
653     return false; // Other protocols fall through to here
654 }
655 
656 void V8Proxy::updateDocumentWrapper(v8::Handle<v8::Value> wrapper)
657 {
658     clearDocumentWrapper();
659 
660     ASSERT(m_document.IsEmpty());
661     m_document = v8::Persistent<v8::Value>::New(wrapper);
662 #ifndef NDEBUG
663     V8GCController::registerGlobalHandle(PROXY, this, m_document);
664 #endif
665 }
666 
667 void V8Proxy::clearDocumentWrapper()
668 {
669     if (!m_document.IsEmpty()) {
670 #ifndef NDEBUG
671         V8GCController::unregisterGlobalHandle(this, m_document);
672 #endif
673         m_document.Dispose();
674         m_document.Clear();
675     }
676 }
677 
678 void V8Proxy::updateDocumentWrapperCache()
679 {
680     LOCK_V8;
681     v8::HandleScope handleScope;
682     v8::Context::Scope contextScope(context());
683 
684     // If the document has no frame, NodeToV8Object might get the
685     // document wrapper for a document that is about to be deleted.
686     // If the ForceSet below causes a garbage collection, the document
687     // might get deleted and the global handle for the document
688     // wrapper cleared. Using the cleared global handle will lead to
689     // crashes. In this case we clear the cache and let the DOMWindow
690     // accessor handle access to the document.
691     if (!m_frame->document()->frame()) {
692         clearDocumentWrapperCache();
693         return;
694     }
695 
696     v8::Handle<v8::Value> documentWrapper = V8DOMWrapper::convertNodeToV8Object(m_frame->document());
697 
698     // If instantiation of the document wrapper fails, clear the cache
699     // and let the DOMWindow accessor handle access to the document.
700     if (documentWrapper.IsEmpty()) {
701         clearDocumentWrapperCache();
702         return;
703     }
704     m_context->Global()->ForceSet(v8::String::New("document"), documentWrapper, static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
705 }
706 
707 void V8Proxy::clearDocumentWrapperCache()
708 {
709     ASSERT(!m_context.IsEmpty());
710     m_context->Global()->ForceDelete(v8::String::New("document"));
711 }
712 
713 void V8Proxy::disposeContextHandles()
714 {
715     if (!m_context.IsEmpty()) {
716         m_frame->loader()->client()->didDestroyScriptContextForFrame();
717         m_context.Dispose();
718         m_context.Clear();
719     }
720 
721     if (!m_wrapperBoilerplates.IsEmpty()) {
722 #ifndef NDEBUG
723         V8GCController::unregisterGlobalHandle(this, m_wrapperBoilerplates);
724 #endif
725         m_wrapperBoilerplates.Dispose();
726         m_wrapperBoilerplates.Clear();
727     }
728 }
729 
730 void V8Proxy::clearForClose()
731 {
732     if (!m_context.IsEmpty()) {
733         LOCK_V8;
734         v8::HandleScope handleScope;
735 
736         clearDocumentWrapper();
737         disposeContextHandles();
738     }
739 }
740 
741 void V8Proxy::clearForNavigation()
742 {
743     disconnectEventListeners();
744 
745     if (!m_context.IsEmpty()) {
746         LOCK_V8;
747         v8::HandleScope handle;
748         clearDocumentWrapper();
749 
750         v8::Context::Scope contextScope(m_context);
751 
752         // Clear the document wrapper cache before turning on access checks on
753         // the old DOMWindow wrapper. This way, access to the document wrapper
754         // will be protected by the security checks on the DOMWindow wrapper.
755         clearDocumentWrapperCache();
756 
757         // Turn on access check on the old DOMWindow wrapper.
758         v8::Handle<v8::Object> wrapper = V8DOMWrapper::lookupDOMWrapper(V8ClassIndex::DOMWINDOW, m_global);
759         ASSERT(!wrapper.IsEmpty());
760         wrapper->TurnOnAccessCheck();
761 
762         // Separate the context from its global object.
763         m_context->DetachGlobal();
764 
765         disposeContextHandles();
766     }
767 }
768 
769 void V8Proxy::setSecurityToken()
770 {
771     Document* document = m_frame->document();
772     // Setup security origin and security token.
773     if (!document) {
774         m_context->UseDefaultSecurityToken();
775         return;
776     }
777 
778     // Ask the document's SecurityOrigin to generate a security token.
779     // If two tokens are equal, then the SecurityOrigins canAccess each other.
780     // If two tokens are not equal, then we have to call canAccess.
781     // Note: we can't use the HTTPOrigin if it was set from the DOM.
782     SecurityOrigin* origin = document->securityOrigin();
783     String token;
784     if (!origin->domainWasSetInDOM())
785         token = document->securityOrigin()->toString();
786 
787     // An empty or "null" token means we always have to call
788     // canAccess. The toString method on securityOrigins returns the
789     // string "null" for empty security origins and for security
790     // origins that should only allow access to themselves. In this
791     // case, we use the global object as the security token to avoid
792     // calling canAccess when a script accesses its own objects.
793     if (token.isEmpty() || token == "null") {
794         m_context->UseDefaultSecurityToken();
795         return;
796     }
797 
798     CString utf8Token = token.utf8();
799     // NOTE: V8 does identity comparison in fast path, must use a symbol
800     // as the security token.
801     m_context->SetSecurityToken(v8::String::NewSymbol(utf8Token.data(), utf8Token.length()));
802 }
803 
804 void V8Proxy::updateDocument()
805 {
806     if (!m_frame->document())
807         return;
808 
809     if (m_global.IsEmpty())
810         return;
811 
812     // There is an existing JavaScript wrapper for the global object
813     // of this frame. JavaScript code in other frames might hold a
814     // reference to this wrapper. We eagerly initialize the JavaScript
815     // context for the new document to make property access on the
816     // global object wrapper succeed.
817     initContextIfNeeded();
818 
819     // We have a new document and we need to update the cache.
820     updateDocumentWrapperCache();
821 
822     updateSecurityOrigin();
823 }
824 
825 void V8Proxy::updateSecurityOrigin()
826 {
827     LOCK_V8;
828     v8::HandleScope scope;
829     setSecurityToken();
830 }
831 
832 // Same origin policy implementation:
833 //
834 // Same origin policy prevents JS code from domain A access JS & DOM objects
835 // in a different domain B. There are exceptions and several objects are
836 // accessible by cross-domain code. For example, the window.frames object is
837 // accessible by code from a different domain, but window.document is not.
838 //
839 // The binding code sets security check callbacks on a function template,
840 // and accessing instances of the template calls the callback function.
841 // The callback function checks same origin policy.
842 //
843 // Callback functions are expensive. V8 uses a security token string to do
844 // fast access checks for the common case where source and target are in the
845 // same domain. A security token is a string object that represents
846 // the protocol/url/port of a domain.
847 //
848 // There are special cases where a security token matching is not enough.
849 // For example, JavaScript can set its domain to a super domain by calling
850 // document.setDomain(...). In these cases, the binding code can reset
851 // a context's security token to its global object so that the fast access
852 // check will always fail.
853 
854 // Check if the current execution context can access a target frame.
855 // First it checks same domain policy using the lexical context
856 //
857 // This is equivalent to KJS::Window::allowsAccessFrom(ExecState*, String&).
858 bool V8Proxy::canAccessPrivate(DOMWindow* targetWindow)
859 {
860     ASSERT(targetWindow);
861 
862     String message;
863 
864     DOMWindow* originWindow = retrieveWindow(currentContext());
865     if (originWindow == targetWindow)
866         return true;
867 
868     if (!originWindow)
869         return false;
870 
871     const SecurityOrigin* activeSecurityOrigin = originWindow->securityOrigin();
872     const SecurityOrigin* targetSecurityOrigin = targetWindow->securityOrigin();
873 
874     // We have seen crashes were the security origin of the target has not been
875     // initialized. Defend against that.
876     if (!targetSecurityOrigin)
877         return false;
878 
879     if (activeSecurityOrigin->canAccess(targetSecurityOrigin))
880         return true;
881 
882     // Allow access to a "about:blank" page if the dynamic context is a
883     // detached context of the same frame as the blank page.
884     if (targetSecurityOrigin->isEmpty() && originWindow->frame() == targetWindow->frame())
885         return true;
886 
887     return false;
888 }
889 
890 bool V8Proxy::canAccessFrame(Frame* target, bool reportError)
891 {
892     // The subject is detached from a frame, deny accesses.
893     if (!target)
894         return false;
895 
896     if (!canAccessPrivate(target->domWindow())) {
897         if (reportError)
898             reportUnsafeAccessTo(target, ReportNow);
899         return false;
900     }
901     return true;
902 }
903 
904 bool V8Proxy::checkNodeSecurity(Node* node)
905 {
906     if (!node)
907         return false;
908 
909     Frame* target = node->document()->frame();
910 
911     if (!target)
912         return false;
913 
914     return canAccessFrame(target, true);
915 }
916 
917 v8::Persistent<v8::Context> V8Proxy::createNewContext(v8::Handle<v8::Object> global, int extensionGroup)
918 {
919     v8::Persistent<v8::Context> result;
920 
921     // The activeDocumentLoader pointer could be NULL during frame shutdown.
922     if (!m_frame->loader()->activeDocumentLoader())
923         return result;
924 
925     // Create a new environment using an empty template for the shadow
926     // object. Reuse the global object if one has been created earlier.
927     v8::Persistent<v8::ObjectTemplate> globalTemplate = V8DOMWindow::GetShadowObjectTemplate();
928     if (globalTemplate.IsEmpty())
929         return result;
930 
931     // Install a security handler with V8.
932     globalTemplate->SetAccessCheckCallbacks(V8Custom::v8DOMWindowNamedSecurityCheck, V8Custom::v8DOMWindowIndexedSecurityCheck, v8::Integer::New(V8ClassIndex::DOMWINDOW));
933 
934     // Dynamically tell v8 about our extensions now.
935     OwnArrayPtr<const char*> extensionNames(new const char*[m_extensions.size()]);
936     int index = 0;
937     for (V8ExtensionList::iterator it = m_extensions.begin(); it != m_extensions.end(); ++it) {
938         if (it->group && it->group != extensionGroup)
939             continue;
940 
941         // Note: we check the loader URL here instead of the document URL
942         // because we might be currently loading an URL into a blank page.
943         // See http://code.google.com/p/chromium/issues/detail?id=10924
944         if (it->scheme.length() > 0 && (it->scheme != m_frame->loader()->activeDocumentLoader()->url().protocol() || it->scheme != m_frame->page()->mainFrame()->loader()->activeDocumentLoader()->url().protocol()))
945             continue;
946 
947         extensionNames[index++] = it->extension->name();
948     }
949     v8::ExtensionConfiguration extensions(index, extensionNames.get());
950     result = v8::Context::New(&extensions, globalTemplate, global);
951 
952     return result;
953 }
954 
955 bool V8Proxy::installDOMWindow(v8::Handle<v8::Context> context, DOMWindow* window)
956 {
957     v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__");
958     if (implicitProtoString.IsEmpty())
959         return false;
960 
961     // Create a new JS window object and use it as the prototype for the  shadow global object.
962     v8::Handle<v8::Function> windowConstructor = V8DOMWrapper::getConstructor(V8ClassIndex::DOMWINDOW, getHiddenObjectPrototype(context));
963     v8::Local<v8::Object> jsWindow = SafeAllocation::newInstance(windowConstructor);
964     // Bail out if allocation failed.
965     if (jsWindow.IsEmpty())
966         return false;
967 
968     // Wrap the window.
969     V8DOMWrapper::setDOMWrapper(jsWindow, V8ClassIndex::ToInt(V8ClassIndex::DOMWINDOW), window);
970 
971     window->ref();
972     V8DOMWrapper::setJSWrapperForDOMObject(window, v8::Persistent<v8::Object>::New(jsWindow));
973 
974     // Insert the window instance as the prototype of the shadow object.
975     v8::Handle<v8::Object> v8Global = context->Global();
976     v8Global->Set(implicitProtoString, jsWindow);
977     return true;
978 }
979 
980 // Create a new environment and setup the global object.
981 //
982 // The global object corresponds to a DOMWindow instance. However, to
983 // allow properties of the JS DOMWindow instance to be shadowed, we
984 // use a shadow object as the global object and use the JS DOMWindow
985 // instance as the prototype for that shadow object. The JS DOMWindow
986 // instance is undetectable from javascript code because the __proto__
987 // accessors skip that object.
988 //
989 // The shadow object and the DOMWindow instance are seen as one object
990 // from javascript. The javascript object that corresponds to a
991 // DOMWindow instance is the shadow object. When mapping a DOMWindow
992 // instance to a V8 object, we return the shadow object.
993 //
994 // To implement split-window, see
995 //   1) https://bugs.webkit.org/show_bug.cgi?id=17249
996 //   2) https://wiki.mozilla.org/Gecko:SplitWindow
997 //   3) https://bugzilla.mozilla.org/show_bug.cgi?id=296639
998 // we need to split the shadow object further into two objects:
999 // an outer window and an inner window. The inner window is the hidden
1000 // prototype of the outer window. The inner window is the default
1001 // global object of the context. A variable declared in the global
1002 // scope is a property of the inner window.
1003 //
1004 // The outer window sticks to a Frame, it is exposed to JavaScript
1005 // via window.window, window.self, window.parent, etc. The outer window
1006 // has a security token which is the domain. The outer window cannot
1007 // have its own properties. window.foo = 'x' is delegated to the
1008 // inner window.
1009 //
1010 // When a frame navigates to a new page, the inner window is cut off
1011 // the outer window, and the outer window identify is preserved for
1012 // the frame. However, a new inner window is created for the new page.
1013 // If there are JS code holds a closure to the old inner window,
1014 // it won't be able to reach the outer window via its global object.
1015 void V8Proxy::initContextIfNeeded()
1016 {
1017     // Bail out if the context has already been initialized.
1018     if (!m_context.IsEmpty())
1019         return;
1020 
1021 #ifdef ANDROID_INSTRUMENT
1022     android::TimeCounter::start(android::TimeCounter::JavaScriptInitTimeCounter);
1023 #endif
1024     LOCK_V8;
1025     // Create a handle scope for all local handles.
1026     v8::HandleScope handleScope;
1027 
1028     // Setup the security handlers and message listener. This only has
1029     // to be done once.
1030     static bool isV8Initialized = false;
1031     if (!isV8Initialized) {
1032         // Tells V8 not to call the default OOM handler, binding code
1033         // will handle it.
1034         v8::V8::IgnoreOutOfMemoryException();
1035         v8::V8::SetFatalErrorHandler(reportFatalErrorInV8);
1036 
1037         v8::V8::SetGlobalGCPrologueCallback(&V8GCController::gcPrologue);
1038         v8::V8::SetGlobalGCEpilogueCallback(&V8GCController::gcEpilogue);
1039 
1040         v8::V8::AddMessageListener(&V8ConsoleMessage::handler);
1041 
1042         v8::V8::SetFailedAccessCheckCallbackFunction(reportUnsafeJavaScriptAccess);
1043 
1044         isV8Initialized = true;
1045     }
1046 
1047     m_context = createNewContext(m_global, 0);
1048     if (m_context.IsEmpty())
1049         return;
1050 
1051     // Starting from now, use local context only.
1052     v8::Local<v8::Context> v8Context = context();
1053     v8::Context::Scope contextScope(v8Context);
1054 
1055     // Store the first global object created so we can reuse it.
1056     if (m_global.IsEmpty()) {
1057         m_global = v8::Persistent<v8::Object>::New(v8Context->Global());
1058         // Bail out if allocation of the first global objects fails.
1059         if (m_global.IsEmpty()) {
1060             disposeContextHandles();
1061             return;
1062         }
1063 #ifndef NDEBUG
1064         V8GCController::registerGlobalHandle(PROXY, this, m_global);
1065 #endif
1066     }
1067 
1068     installHiddenObjectPrototype(m_context);
1069     m_wrapperBoilerplates = v8::Persistent<v8::Array>::New(v8::Array::New(V8ClassIndex::WRAPPER_TYPE_COUNT));
1070     // Bail out if allocation failed.
1071     if (m_wrapperBoilerplates.IsEmpty()) {
1072         disposeContextHandles();
1073         return;
1074     }
1075 #ifndef NDEBUG
1076     V8GCController::registerGlobalHandle(PROXY, this, m_wrapperBoilerplates);
1077 #endif
1078 
1079     if (!installDOMWindow(v8Context, m_frame->domWindow()))
1080         disposeContextHandles();
1081 
1082     updateDocument();
1083 
1084     setSecurityToken();
1085 
1086     m_frame->loader()->client()->didCreateScriptContextForFrame();
1087     m_frame->loader()->dispatchWindowObjectAvailable();
1088 #ifdef ANDROID_INSTRUMENT
1089     android::TimeCounter::record(android::TimeCounter::JavaScriptInitTimeCounter, __FUNCTION__);
1090 #endif
1091 }
1092 
1093 void V8Proxy::setDOMException(int exceptionCode)
1094 {
1095     if (exceptionCode <= 0)
1096         return;
1097 
1098     ExceptionCodeDescription description;
1099     getExceptionCodeDescription(exceptionCode, description);
1100 
1101     v8::Handle<v8::Value> exception;
1102     switch (description.type) {
1103     case DOMExceptionType:
1104         exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::DOMCOREEXCEPTION, DOMCoreException::create(description));
1105         break;
1106     case RangeExceptionType:
1107         exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::RANGEEXCEPTION, RangeException::create(description));
1108         break;
1109     case EventExceptionType:
1110         exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::EVENTEXCEPTION, EventException::create(description));
1111         break;
1112     case XMLHttpRequestExceptionType:
1113         exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::XMLHTTPREQUESTEXCEPTION, XMLHttpRequestException::create(description));
1114         break;
1115 #if ENABLE(SVG)
1116     case SVGExceptionType:
1117         exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::SVGEXCEPTION, SVGException::create(description));
1118         break;
1119 #endif
1120 #if ENABLE(XPATH)
1121     case XPathExceptionType:
1122         exception = V8DOMWrapper::convertToV8Object(V8ClassIndex::XPATHEXCEPTION, XPathException::create(description));
1123         break;
1124 #endif
1125     }
1126 
1127     ASSERT(!exception.IsEmpty());
1128     v8::ThrowException(exception);
1129 }
1130 
1131 v8::Handle<v8::Value> V8Proxy::throwError(ErrorType type, const char* message)
1132 {
1133     switch (type) {
1134     case RangeError:
1135         return v8::ThrowException(v8::Exception::RangeError(v8String(message)));
1136     case ReferenceError:
1137         return v8::ThrowException(v8::Exception::ReferenceError(v8String(message)));
1138     case SyntaxError:
1139         return v8::ThrowException(v8::Exception::SyntaxError(v8String(message)));
1140     case TypeError:
1141         return v8::ThrowException(v8::Exception::TypeError(v8String(message)));
1142     case GeneralError:
1143         return v8::ThrowException(v8::Exception::Error(v8String(message)));
1144     default:
1145         ASSERT_NOT_REACHED();
1146         return notHandledByInterceptor();
1147     }
1148 }
1149 
1150 v8::Local<v8::Context> V8Proxy::context(Frame* frame)
1151 {
1152     v8::Local<v8::Context> context = V8Proxy::mainWorldContext(frame);
1153     if (context.IsEmpty())
1154         return v8::Local<v8::Context>();
1155 
1156     if (V8IsolatedWorld* world = V8IsolatedWorld::getEntered()) {
1157         context = v8::Local<v8::Context>::New(world->context());
1158         if (frame != V8Proxy::retrieveFrame(context))
1159             return v8::Local<v8::Context>();
1160     }
1161 
1162     return context;
1163 }
1164 
1165 v8::Local<v8::Context> V8Proxy::mainWorldContext(Frame* frame)
1166 {
1167     V8Proxy* proxy = retrieve(frame);
1168     if (!proxy)
1169         return v8::Local<v8::Context>();
1170 
1171     proxy->initContextIfNeeded();
1172     return proxy->context();
1173 }
1174 
1175 v8::Local<v8::Context> V8Proxy::currentContext()
1176 {
1177     return v8::Context::GetCurrent();
1178 }
1179 
1180 v8::Handle<v8::Value> V8Proxy::checkNewLegal(const v8::Arguments& args)
1181 {
1182     if (!AllowAllocation::m_current)
1183         return throwError(TypeError, "Illegal constructor");
1184 
1185     return args.This();
1186 }
1187 
1188 void V8Proxy::bindJsObjectToWindow(Frame* frame, const char* name, int type, v8::Handle<v8::FunctionTemplate> descriptor, void* impl)
1189 {
1190     // Get environment.
1191     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(frame);
1192     if (v8Context.IsEmpty())
1193         return; // JS not enabled.
1194 
1195     v8::Context::Scope scope(v8Context);
1196     v8::Handle<v8::Object> instance = descriptor->GetFunction();
1197     V8DOMWrapper::setDOMWrapper(instance, type, impl);
1198 
1199     v8::Handle<v8::Object> global = v8Context->Global();
1200     global->Set(v8::String::New(name), instance);
1201 }
1202 
1203 void V8Proxy::processConsoleMessages()
1204 {
1205     V8ConsoleMessage::processDelayed();
1206 }
1207 
1208 // Create the utility context for holding JavaScript functions used internally
1209 // which are not visible to JavaScript executing on the page.
1210 void V8Proxy::createUtilityContext()
1211 {
1212     ASSERT(m_utilityContext.IsEmpty());
1213 
1214     v8::HandleScope scope;
1215     v8::Handle<v8::ObjectTemplate> globalTemplate = v8::ObjectTemplate::New();
1216     m_utilityContext = v8::Context::New(0, globalTemplate);
1217     v8::Context::Scope contextScope(m_utilityContext);
1218 
1219     // Compile JavaScript function for retrieving the source line of the top
1220     // JavaScript stack frame.
1221     DEFINE_STATIC_LOCAL(const char*, frameSourceLineSource,
1222         ("function frameSourceLine(exec_state) {"
1223         "  return exec_state.frame(0).sourceLine();"
1224         "}"));
1225     v8::Script::Compile(v8::String::New(frameSourceLineSource))->Run();
1226 
1227     // Compile JavaScript function for retrieving the source name of the top
1228     // JavaScript stack frame.
1229     DEFINE_STATIC_LOCAL(const char*, frameSourceNameSource,
1230         ("function frameSourceName(exec_state) {"
1231         "  var frame = exec_state.frame(0);"
1232         "  if (frame.func().resolved() && "
1233         "      frame.func().script() && "
1234         "      frame.func().script().name()) {"
1235         "    return frame.func().script().name();"
1236         "  }"
1237         "}"));
1238     v8::Script::Compile(v8::String::New(frameSourceNameSource))->Run();
1239 }
1240 
1241 int V8Proxy::sourceLineNumber()
1242 {
1243 #if PLATFORM(ANDROID)
1244     // TODO(andreip): consider V8's DEBUG flag here, rather than PLATFORM(ANDROID)
1245     // or, even better, the WEBKIT inspector flag.
1246     return 0;
1247 #else
1248     v8::HandleScope scope;
1249     v8::Handle<v8::Context> v8UtilityContext = V8Proxy::utilityContext();
1250     if (v8UtilityContext.IsEmpty())
1251         return 0;
1252     v8::Context::Scope contextScope(v8UtilityContext);
1253     v8::Handle<v8::Function> frameSourceLine;
1254     frameSourceLine = v8::Local<v8::Function>::Cast(v8UtilityContext->Global()->Get(v8::String::New("frameSourceLine")));
1255     if (frameSourceLine.IsEmpty())
1256         return 0;
1257     v8::Handle<v8::Value> result = v8::Debug::Call(frameSourceLine);
1258     if (result.IsEmpty())
1259         return 0;
1260     return result->Int32Value();
1261 #endif
1262 }
1263 
1264 String V8Proxy::sourceName()
1265 {
1266 #if PLATFORM(ANDROID)
1267     return String();
1268 #else
1269     v8::HandleScope scope;
1270     v8::Handle<v8::Context> v8UtilityContext = utilityContext();
1271     if (v8UtilityContext.IsEmpty())
1272         return String();
1273     v8::Context::Scope contextScope(v8UtilityContext);
1274     v8::Handle<v8::Function> frameSourceName;
1275     frameSourceName = v8::Local<v8::Function>::Cast(v8UtilityContext->Global()->Get(v8::String::New("frameSourceName")));
1276     if (frameSourceName.IsEmpty())
1277         return String();
1278     return toWebCoreString(v8::Debug::Call(frameSourceName));
1279 #endif
1280 }
1281 
1282 void V8Proxy::registerExtensionWithV8(v8::Extension* extension) {
1283     // If the extension exists in our list, it was already registered with V8.
1284     for (V8ExtensionList::iterator it = m_extensions.begin(); it != m_extensions.end(); ++it) {
1285         if (it->extension == extension)
1286             return;
1287     }
1288 
1289     v8::RegisterExtension(extension);
1290 }
1291 
1292 void V8Proxy::registerExtension(v8::Extension* extension, const String& schemeRestriction)
1293 {
1294     registerExtensionWithV8(extension);
1295     V8ExtensionInfo info = {schemeRestriction, 0, extension};
1296     m_extensions.append(info);
1297 }
1298 
1299 void V8Proxy::registerExtension(v8::Extension* extension, int extensionGroup)
1300 {
1301     registerExtensionWithV8(extension);
1302     V8ExtensionInfo info = {String(), extensionGroup, extension};
1303     m_extensions.append(info);
1304 }
1305 
1306 bool V8Proxy::setContextDebugId(int debugId)
1307 {
1308     ASSERT(debugId > 0);
1309     if (m_context.IsEmpty())
1310         return false;
1311     v8::HandleScope scope;
1312     if (!m_context->GetData()->IsUndefined())
1313         return false;
1314 
1315     v8::Context::Scope contextScope(m_context);
1316     v8::Handle<v8::Object> contextData = v8::Object::New();
1317     contextData->Set(v8::String::New(kContextDebugDataType), v8::String::New("page"));
1318     contextData->Set(v8::String::New(kContextDebugDataValue), v8::Integer::New(debugId));
1319     m_context->SetData(contextData);
1320     return true;
1321 }
1322 
1323 int V8Proxy::contextDebugId(v8::Handle<v8::Context> context)
1324 {
1325     v8::HandleScope scope;
1326     if (!context->GetData()->IsObject())
1327         return -1;
1328     v8::Handle<v8::Value> data = context->GetData()->ToObject()->Get( v8::String::New(kContextDebugDataValue));
1329     return data->IsInt32() ? data->Int32Value() : -1;
1330 }
1331 
1332 v8::Handle<v8::Value> V8Proxy::getHiddenObjectPrototype(v8::Handle<v8::Context> context)
1333 {
1334     return context->Global()->GetHiddenValue(V8HiddenPropertyName::objectPrototype());
1335 }
1336 
1337 void V8Proxy::installHiddenObjectPrototype(v8::Handle<v8::Context> context)
1338 {
1339     v8::Handle<v8::String> objectString = v8::String::New("Object");
1340     v8::Handle<v8::String> prototypeString = v8::String::New("prototype");
1341     v8::Handle<v8::String> hiddenObjectPrototypeString = V8HiddenPropertyName::objectPrototype();
1342     // Bail out if allocation failed.
1343     if (objectString.IsEmpty() || prototypeString.IsEmpty() || hiddenObjectPrototypeString.IsEmpty())
1344         return;
1345 
1346     v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(context->Global()->Get(objectString));
1347     v8::Handle<v8::Value> objectPrototype = object->Get(prototypeString);
1348 
1349     context->Global()->SetHiddenValue(hiddenObjectPrototypeString, objectPrototype);
1350 }
1351 
1352 }  // namespace WebCore
1353