Home | History | Annotate | Download | only in v8
      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