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