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 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 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 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 129 static HashMap<void*, SVGElement*>& svgObjectToContextMap() 130 { 131 typedef HashMap<void*, SVGElement*> SvgObjectToContextMap; 132 DEFINE_STATIC_LOCAL(SvgObjectToContextMap, staticSvgObjectToContextMap, ()); 133 return staticSvgObjectToContextMap; 134 } 135 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 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 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 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 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 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 236 V8Proxy::~V8Proxy() 237 { 238 clearForClose(); 239 windowShell()->destroyGlobal(); 240 } 241 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 586 V8Proxy* V8Proxy::retrieve() 587 { 588 DOMWindow* window = retrieveWindow(currentContext()); 589 ASSERT(window); 590 return retrieve(window->frame()); 591 } 592 593 V8Proxy* V8Proxy::retrieve(Frame* frame) 594 { 595 if (!frame) 596 return 0; 597 return frame->script()->canExecuteScripts() ? frame->script()->proxy() : 0; 598 } 599 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 607 void V8Proxy::disconnectFrame() 608 { 609 } 610 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 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 634 void V8Proxy::clearForClose() 635 { 636 resetIsolatedWorlds(); 637 windowShell()->clearForClose(); 638 } 639 640 void V8Proxy::clearForNavigation() 641 { 642 resetIsolatedWorlds(); 643 windowShell()->clearForNavigation(); 644 } 645 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 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 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 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 729 v8::Local<v8::Context> V8Proxy::mainWorldContext() 730 { 731 windowShell()->initContextIfNeeded(); 732 return v8::Local<v8::Context>::New(windowShell()->context()); 733 } 734 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 744 v8::Local<v8::Context> V8Proxy::currentContext() 745 { 746 return v8::Context::GetCurrent(); 747 } 748 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 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 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. 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 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 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 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 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 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 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 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 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 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