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