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