Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (c) 2010-2011 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 "bindings/core/v8/ScriptDebugServer.h"
     33 
     34 #include "bindings/core/v8/ScopedPersistent.h"
     35 #include "bindings/core/v8/ScriptCallStackFactory.h"
     36 #include "bindings/core/v8/ScriptController.h"
     37 #include "bindings/core/v8/ScriptSourceCode.h"
     38 #include "bindings/core/v8/ScriptValue.h"
     39 #include "bindings/core/v8/V8Binding.h"
     40 #include "bindings/core/v8/V8JavaScriptCallFrame.h"
     41 #include "bindings/core/v8/V8ScriptRunner.h"
     42 #include "core/inspector/JavaScriptCallFrame.h"
     43 #include "core/inspector/ScriptDebugListener.h"
     44 #include "platform/JSONValues.h"
     45 #include "public/platform/Platform.h"
     46 #include "public/platform/WebData.h"
     47 #include "wtf/StdLibExtras.h"
     48 #include "wtf/Vector.h"
     49 #include "wtf/dtoa/utils.h"
     50 #include "wtf/text/CString.h"
     51 
     52 namespace blink {
     53 
     54 namespace {
     55 
     56 class ClientDataImpl : public v8::Debug::ClientData {
     57 public:
     58     ClientDataImpl(PassOwnPtr<ScriptDebugServer::Task> task) : m_task(task) { }
     59     virtual ~ClientDataImpl() { }
     60     ScriptDebugServer::Task* task() const { return m_task.get(); }
     61 private:
     62     OwnPtr<ScriptDebugServer::Task> m_task;
     63 };
     64 
     65 const char stepIntoV8MethodName[] = "stepIntoStatement";
     66 const char stepOutV8MethodName[] = "stepOutOfFunction";
     67 }
     68 
     69 v8::Local<v8::Value> ScriptDebugServer::callDebuggerMethod(const char* functionName, int argc, v8::Handle<v8::Value> argv[])
     70 {
     71     v8::Handle<v8::Object> debuggerScript = m_debuggerScript.newLocal(m_isolate);
     72     v8::Handle<v8::Function> function = v8::Local<v8::Function>::Cast(debuggerScript->Get(v8AtomicString(m_isolate, functionName)));
     73     ASSERT(m_isolate->InContext());
     74     return V8ScriptRunner::callInternalFunction(function, debuggerScript, argc, argv, m_isolate);
     75 }
     76 
     77 ScriptDebugServer::ScriptDebugServer(v8::Isolate* isolate)
     78     : m_pauseOnExceptionsState(DontPauseOnExceptions)
     79     , m_breakpointsActivated(true)
     80     , m_isolate(isolate)
     81     , m_runningNestedMessageLoop(false)
     82 {
     83 }
     84 
     85 ScriptDebugServer::~ScriptDebugServer()
     86 {
     87 }
     88 
     89 String ScriptDebugServer::setBreakpoint(const String& sourceID, const ScriptBreakpoint& scriptBreakpoint, int* actualLineNumber, int* actualColumnNumber, bool interstatementLocation)
     90 {
     91     v8::HandleScope scope(m_isolate);
     92     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
     93     v8::Context::Scope contextScope(debuggerContext);
     94 
     95     v8::Local<v8::Object> info = v8::Object::New(m_isolate);
     96     info->Set(v8AtomicString(m_isolate, "sourceID"), v8String(debuggerContext->GetIsolate(), sourceID));
     97     info->Set(v8AtomicString(m_isolate, "lineNumber"), v8::Integer::New(debuggerContext->GetIsolate(), scriptBreakpoint.lineNumber));
     98     info->Set(v8AtomicString(m_isolate, "columnNumber"), v8::Integer::New(debuggerContext->GetIsolate(), scriptBreakpoint.columnNumber));
     99     info->Set(v8AtomicString(m_isolate, "interstatementLocation"), v8Boolean(interstatementLocation, debuggerContext->GetIsolate()));
    100     info->Set(v8AtomicString(m_isolate, "condition"), v8String(debuggerContext->GetIsolate(), scriptBreakpoint.condition));
    101 
    102     v8::Handle<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "setBreakpoint")));
    103     v8::Handle<v8::Value> breakpointId = v8::Debug::Call(setBreakpointFunction, info);
    104     if (breakpointId.IsEmpty() || !breakpointId->IsString())
    105         return "";
    106     *actualLineNumber = info->Get(v8AtomicString(m_isolate, "lineNumber"))->Int32Value();
    107     *actualColumnNumber = info->Get(v8AtomicString(m_isolate, "columnNumber"))->Int32Value();
    108     return toCoreString(breakpointId.As<v8::String>());
    109 }
    110 
    111 void ScriptDebugServer::removeBreakpoint(const String& breakpointId)
    112 {
    113     v8::HandleScope scope(m_isolate);
    114     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    115     v8::Context::Scope contextScope(debuggerContext);
    116 
    117     v8::Local<v8::Object> info = v8::Object::New(m_isolate);
    118     info->Set(v8AtomicString(m_isolate, "breakpointId"), v8String(debuggerContext->GetIsolate(), breakpointId));
    119 
    120     v8::Handle<v8::Function> removeBreakpointFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "removeBreakpoint")));
    121     v8::Debug::Call(removeBreakpointFunction, info);
    122 }
    123 
    124 void ScriptDebugServer::clearBreakpoints()
    125 {
    126     ensureDebuggerScriptCompiled();
    127     v8::HandleScope scope(m_isolate);
    128     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    129     v8::Context::Scope contextScope(debuggerContext);
    130 
    131     v8::Handle<v8::Function> clearBreakpoints = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "clearBreakpoints")));
    132     v8::Debug::Call(clearBreakpoints);
    133 }
    134 
    135 void ScriptDebugServer::setBreakpointsActivated(bool activated)
    136 {
    137     ensureDebuggerScriptCompiled();
    138     v8::HandleScope scope(m_isolate);
    139     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    140     v8::Context::Scope contextScope(debuggerContext);
    141 
    142     v8::Local<v8::Object> info = v8::Object::New(m_isolate);
    143     info->Set(v8AtomicString(m_isolate, "enabled"), v8::Boolean::New(m_isolate, activated));
    144     v8::Handle<v8::Function> setBreakpointsActivated = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "setBreakpointsActivated")));
    145     v8::Debug::Call(setBreakpointsActivated, info);
    146 
    147     m_breakpointsActivated = activated;
    148 }
    149 
    150 ScriptDebugServer::PauseOnExceptionsState ScriptDebugServer::pauseOnExceptionsState()
    151 {
    152     ensureDebuggerScriptCompiled();
    153     v8::HandleScope scope(m_isolate);
    154     v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
    155 
    156     v8::Handle<v8::Value> argv[] = { v8Undefined() };
    157     v8::Handle<v8::Value> result = callDebuggerMethod("pauseOnExceptionsState", 0, argv);
    158     return static_cast<ScriptDebugServer::PauseOnExceptionsState>(result->Int32Value());
    159 }
    160 
    161 void ScriptDebugServer::setPauseOnExceptionsState(PauseOnExceptionsState pauseOnExceptionsState)
    162 {
    163     ensureDebuggerScriptCompiled();
    164     v8::HandleScope scope(m_isolate);
    165     v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
    166 
    167     v8::Handle<v8::Value> argv[] = { v8::Int32::New(m_isolate, pauseOnExceptionsState) };
    168     callDebuggerMethod("setPauseOnExceptionsState", 1, argv);
    169 }
    170 
    171 void ScriptDebugServer::setPauseOnNextStatement(bool pause)
    172 {
    173     ASSERT(!isPaused());
    174     if (pause)
    175         v8::Debug::DebugBreak(m_isolate);
    176     else
    177         v8::Debug::CancelDebugBreak(m_isolate);
    178 }
    179 
    180 bool ScriptDebugServer::pausingOnNextStatement()
    181 {
    182     return v8::Debug::CheckDebugBreak(m_isolate);
    183 }
    184 
    185 bool ScriptDebugServer::canBreakProgram()
    186 {
    187     if (!m_breakpointsActivated)
    188         return false;
    189     return m_isolate->InContext();
    190 }
    191 
    192 void ScriptDebugServer::breakProgram()
    193 {
    194     if (isPaused()) {
    195         ASSERT(!m_runningNestedMessageLoop);
    196         v8::Handle<v8::Value> exception;
    197         v8::Handle<v8::Array> hitBreakpoints;
    198         handleProgramBreak(m_pausedScriptState.get(), m_executionState, exception, hitBreakpoints);
    199         return;
    200     }
    201 
    202     if (!canBreakProgram())
    203         return;
    204 
    205     v8::HandleScope scope(m_isolate);
    206     if (m_breakProgramCallbackTemplate.isEmpty()) {
    207         v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(m_isolate);
    208         templ->SetCallHandler(&ScriptDebugServer::breakProgramCallback, v8::External::New(m_isolate, this));
    209         m_breakProgramCallbackTemplate.set(m_isolate, templ);
    210     }
    211 
    212     v8::Handle<v8::Function> breakProgramFunction = m_breakProgramCallbackTemplate.newLocal(m_isolate)->GetFunction();
    213     v8::Debug::Call(breakProgramFunction);
    214 }
    215 
    216 void ScriptDebugServer::continueProgram()
    217 {
    218     if (isPaused())
    219         quitMessageLoopOnPause();
    220     m_pausedScriptState.clear();
    221     m_executionState.Clear();
    222 }
    223 
    224 void ScriptDebugServer::stepIntoStatement()
    225 {
    226     ASSERT(isPaused());
    227     ASSERT(!m_executionState.IsEmpty());
    228     v8::HandleScope handleScope(m_isolate);
    229     v8::Handle<v8::Value> argv[] = { m_executionState };
    230     callDebuggerMethod(stepIntoV8MethodName, 1, argv);
    231     continueProgram();
    232 }
    233 
    234 void ScriptDebugServer::stepOverStatement()
    235 {
    236     ASSERT(isPaused());
    237     ASSERT(!m_executionState.IsEmpty());
    238     v8::HandleScope handleScope(m_isolate);
    239     v8::Handle<v8::Value> argv[] = { m_executionState };
    240     callDebuggerMethod("stepOverStatement", 1, argv);
    241     continueProgram();
    242 }
    243 
    244 void ScriptDebugServer::stepOutOfFunction()
    245 {
    246     ASSERT(isPaused());
    247     ASSERT(!m_executionState.IsEmpty());
    248     v8::HandleScope handleScope(m_isolate);
    249     v8::Handle<v8::Value> argv[] = { m_executionState };
    250     callDebuggerMethod(stepOutV8MethodName, 1, argv);
    251     continueProgram();
    252 }
    253 
    254 bool ScriptDebugServer::setScriptSource(const String& sourceID, const String& newContent, bool preview, String* error, RefPtr<TypeBuilder::Debugger::SetScriptSourceError>& errorData, ScriptValue* newCallFrames, RefPtr<JSONObject>* result)
    255 {
    256     class EnableLiveEditScope {
    257     public:
    258         explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) { v8::Debug::SetLiveEditEnabled(m_isolate, true); }
    259         ~EnableLiveEditScope() { v8::Debug::SetLiveEditEnabled(m_isolate, false); }
    260     private:
    261         v8::Isolate* m_isolate;
    262     };
    263 
    264     ensureDebuggerScriptCompiled();
    265     v8::HandleScope scope(m_isolate);
    266 
    267     OwnPtr<v8::Context::Scope> contextScope;
    268     v8::Handle<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    269     if (!isPaused())
    270         contextScope = adoptPtr(new v8::Context::Scope(debuggerContext));
    271 
    272     v8::Handle<v8::Value> argv[] = { v8String(m_isolate, sourceID), v8String(m_isolate, newContent), v8Boolean(preview, m_isolate) };
    273 
    274     v8::Local<v8::Value> v8result;
    275     {
    276         EnableLiveEditScope enableLiveEditScope(m_isolate);
    277         v8::TryCatch tryCatch;
    278         tryCatch.SetVerbose(false);
    279         v8result = callDebuggerMethod("liveEditScriptSource", 3, argv);
    280         if (tryCatch.HasCaught()) {
    281             v8::Local<v8::Message> message = tryCatch.Message();
    282             if (!message.IsEmpty())
    283                 *error = toCoreStringWithUndefinedOrNullCheck(message->Get());
    284             else
    285                 *error = "Unknown error.";
    286             return false;
    287         }
    288     }
    289     ASSERT(!v8result.IsEmpty());
    290     v8::Local<v8::Object> resultTuple = v8result->ToObject();
    291     int code = static_cast<int>(resultTuple->Get(0)->ToInteger()->Value());
    292     switch (code) {
    293     case 0:
    294         {
    295             v8::Local<v8::Value> normalResult = resultTuple->Get(1);
    296             RefPtr<JSONValue> jsonResult = v8ToJSONValue(m_isolate, normalResult, JSONValue::maxDepth);
    297             if (jsonResult)
    298                 *result = jsonResult->asObject();
    299             // Call stack may have changed after if the edited function was on the stack.
    300             if (!preview && isPaused())
    301                 *newCallFrames = currentCallFrames();
    302             return true;
    303         }
    304     // Compile error.
    305     case 1:
    306         {
    307             RefPtr<TypeBuilder::Debugger::SetScriptSourceError::CompileError> compileError =
    308                 TypeBuilder::Debugger::SetScriptSourceError::CompileError::create()
    309                     .setMessage(toCoreStringWithUndefinedOrNullCheck(resultTuple->Get(2)))
    310                     .setLineNumber(resultTuple->Get(3)->ToInteger()->Value())
    311                     .setColumnNumber(resultTuple->Get(4)->ToInteger()->Value());
    312 
    313             *error = toCoreStringWithUndefinedOrNullCheck(resultTuple->Get(1));
    314             errorData = TypeBuilder::Debugger::SetScriptSourceError::create();
    315             errorData->setCompileError(compileError);
    316             return false;
    317         }
    318     }
    319     *error = "Unknown error.";
    320     return false;
    321 }
    322 
    323 int ScriptDebugServer::frameCount()
    324 {
    325     ASSERT(isPaused());
    326     ASSERT(!m_executionState.IsEmpty());
    327     v8::Handle<v8::Value> argv[] = { m_executionState };
    328     v8::Handle<v8::Value> result = callDebuggerMethod("frameCount", WTF_ARRAY_LENGTH(argv), argv);
    329     if (result->IsInt32())
    330         return result->Int32Value();
    331     return 0;
    332 }
    333 
    334 PassRefPtrWillBeRawPtr<JavaScriptCallFrame> ScriptDebugServer::toJavaScriptCallFrameUnsafe(const ScriptValue& value)
    335 {
    336     if (value.isEmpty())
    337         return nullptr;
    338     ASSERT(value.isObject());
    339     return V8JavaScriptCallFrame::toImpl(v8::Handle<v8::Object>::Cast(value.v8ValueUnsafe()));
    340 }
    341 
    342 PassRefPtrWillBeRawPtr<JavaScriptCallFrame> ScriptDebugServer::wrapCallFrames(int maximumLimit, ScopeInfoDetails scopeDetails)
    343 {
    344     const int scopeBits = 2;
    345     COMPILE_ASSERT(NoScopes < (1 << scopeBits), not_enough_bits_to_encode_ScopeInfoDetails);
    346 
    347     ASSERT(maximumLimit >= 0);
    348     int data = (maximumLimit << scopeBits) | scopeDetails;
    349     v8::Handle<v8::Value> currentCallFrameV8;
    350     if (m_executionState.IsEmpty()) {
    351         v8::Handle<v8::Function> currentCallFrameFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "currentCallFrame")));
    352         currentCallFrameV8 = v8::Debug::Call(currentCallFrameFunction, v8::Integer::New(m_isolate, data));
    353     } else {
    354         v8::Handle<v8::Value> argv[] = { m_executionState, v8::Integer::New(m_isolate, data) };
    355         currentCallFrameV8 = callDebuggerMethod("currentCallFrame", WTF_ARRAY_LENGTH(argv), argv);
    356     }
    357     ASSERT(!currentCallFrameV8.IsEmpty());
    358     if (!currentCallFrameV8->IsObject())
    359         return nullptr;
    360     return JavaScriptCallFrame::create(v8::Debug::GetDebugContext(), v8::Handle<v8::Object>::Cast(currentCallFrameV8));
    361 }
    362 
    363 ScriptValue ScriptDebugServer::currentCallFramesInner(ScopeInfoDetails scopeDetails)
    364 {
    365     if (!m_isolate->InContext())
    366         return ScriptValue();
    367     v8::HandleScope handleScope(m_isolate);
    368 
    369     // Filter out stack traces entirely consisting of V8's internal scripts.
    370     v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(m_isolate, 1);
    371     if (!stackTrace->GetFrameCount())
    372         return ScriptValue();
    373 
    374     RefPtrWillBeRawPtr<JavaScriptCallFrame> currentCallFrame = wrapCallFrames(0, scopeDetails);
    375     if (!currentCallFrame)
    376         return ScriptValue();
    377 
    378     ScriptState* scriptState = m_pausedScriptState ? m_pausedScriptState.get() : ScriptState::current(m_isolate);
    379     ScriptState::Scope scope(scriptState);
    380     return ScriptValue(scriptState, toV8(currentCallFrame.release(), scriptState->context()->Global(), m_isolate));
    381 }
    382 
    383 ScriptValue ScriptDebugServer::currentCallFrames()
    384 {
    385     return currentCallFramesInner(AllScopes);
    386 }
    387 
    388 ScriptValue ScriptDebugServer::currentCallFramesForAsyncStack()
    389 {
    390     return currentCallFramesInner(FastAsyncScopes);
    391 }
    392 
    393 PassRefPtrWillBeRawPtr<JavaScriptCallFrame> ScriptDebugServer::callFrameNoScopes(int index)
    394 {
    395     v8::Handle<v8::Value> currentCallFrameV8;
    396     if (m_executionState.IsEmpty()) {
    397         v8::Handle<v8::Function> currentCallFrameFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "currentCallFrameByIndex")));
    398         currentCallFrameV8 = v8::Debug::Call(currentCallFrameFunction, v8::Integer::New(m_isolate, index));
    399     } else {
    400         v8::Handle<v8::Value> argv[] = { m_executionState, v8::Integer::New(m_isolate, index) };
    401         currentCallFrameV8 = callDebuggerMethod("currentCallFrameByIndex", WTF_ARRAY_LENGTH(argv), argv);
    402     }
    403     ASSERT(!currentCallFrameV8.IsEmpty());
    404     if (!currentCallFrameV8->IsObject())
    405         return nullptr;
    406     return JavaScriptCallFrame::create(v8::Debug::GetDebugContext(), v8::Handle<v8::Object>::Cast(currentCallFrameV8));
    407 }
    408 
    409 void ScriptDebugServer::interruptAndRun(PassOwnPtr<Task> task, v8::Isolate* isolate)
    410 {
    411     v8::Debug::DebugBreakForCommand(isolate, new ClientDataImpl(task));
    412 }
    413 
    414 void ScriptDebugServer::runPendingTasks()
    415 {
    416     v8::Debug::ProcessDebugMessages();
    417 }
    418 
    419 static ScriptDebugServer* toScriptDebugServer(v8::Handle<v8::Value> data)
    420 {
    421     void* p = v8::Handle<v8::External>::Cast(data)->Value();
    422     return static_cast<ScriptDebugServer*>(p);
    423 }
    424 
    425 void ScriptDebugServer::breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
    426 {
    427     ASSERT(2 == info.Length());
    428     ScriptDebugServer* thisPtr = toScriptDebugServer(info.Data());
    429     ScriptState* pausedScriptState = ScriptState::current(thisPtr->m_isolate);
    430     v8::Handle<v8::Value> exception;
    431     v8::Handle<v8::Array> hitBreakpoints;
    432     thisPtr->handleProgramBreak(pausedScriptState, v8::Handle<v8::Object>::Cast(info[0]), exception, hitBreakpoints);
    433 }
    434 
    435 void ScriptDebugServer::handleProgramBreak(ScriptState* pausedScriptState, v8::Handle<v8::Object> executionState, v8::Handle<v8::Value> exception, v8::Handle<v8::Array> hitBreakpointNumbers)
    436 {
    437     // Don't allow nested breaks.
    438     if (m_runningNestedMessageLoop)
    439         return;
    440 
    441     ScriptDebugListener* listener = getDebugListenerForContext(pausedScriptState->context());
    442     if (!listener)
    443         return;
    444 
    445     Vector<String> breakpointIds;
    446     if (!hitBreakpointNumbers.IsEmpty()) {
    447         breakpointIds.resize(hitBreakpointNumbers->Length());
    448         for (size_t i = 0; i < hitBreakpointNumbers->Length(); i++) {
    449             v8::Handle<v8::Value> hitBreakpointNumber = hitBreakpointNumbers->Get(i);
    450             ASSERT(!hitBreakpointNumber.IsEmpty() && hitBreakpointNumber->IsInt32());
    451             breakpointIds[i] = String::number(hitBreakpointNumber->Int32Value());
    452         }
    453     }
    454 
    455     m_pausedScriptState = pausedScriptState;
    456     m_executionState = executionState;
    457     ScriptDebugListener::SkipPauseRequest result = listener->didPause(pausedScriptState, currentCallFrames(), ScriptValue(pausedScriptState, exception), breakpointIds);
    458     if (result == ScriptDebugListener::NoSkip) {
    459         m_runningNestedMessageLoop = true;
    460         runMessageLoopOnPause(pausedScriptState->context());
    461         m_runningNestedMessageLoop = false;
    462     }
    463     m_pausedScriptState.clear();
    464     m_executionState.Clear();
    465 
    466     if (result == ScriptDebugListener::StepInto) {
    467         v8::Handle<v8::Value> argv[] = { executionState };
    468         callDebuggerMethod(stepIntoV8MethodName, 1, argv);
    469     } else if (result == ScriptDebugListener::StepOut) {
    470         v8::Handle<v8::Value> argv[] = { executionState };
    471         callDebuggerMethod(stepOutV8MethodName, 1, argv);
    472     }
    473 }
    474 
    475 void ScriptDebugServer::v8DebugEventCallback(const v8::Debug::EventDetails& eventDetails)
    476 {
    477     ScriptDebugServer* thisPtr = toScriptDebugServer(eventDetails.GetCallbackData());
    478     thisPtr->handleV8DebugEvent(eventDetails);
    479 }
    480 
    481 static v8::Handle<v8::Value> callInternalGetterFunction(v8::Handle<v8::Object> object, const char* functionName, v8::Isolate* isolate)
    482 {
    483     v8::Handle<v8::Value> getterValue = object->Get(v8AtomicString(isolate, functionName));
    484     ASSERT(!getterValue.IsEmpty() && getterValue->IsFunction());
    485     return V8ScriptRunner::callInternalFunction(v8::Handle<v8::Function>::Cast(getterValue), object, 0, 0, isolate);
    486 }
    487 
    488 void ScriptDebugServer::handleV8DebugEvent(const v8::Debug::EventDetails& eventDetails)
    489 {
    490     v8::DebugEvent event = eventDetails.GetEvent();
    491 
    492     if (event == v8::BreakForCommand) {
    493         ClientDataImpl* data = static_cast<ClientDataImpl*>(eventDetails.GetClientData());
    494         data->task()->run();
    495         return;
    496     }
    497 
    498     if (event != v8::AsyncTaskEvent && event != v8::Break && event != v8::Exception && event != v8::AfterCompile && event != v8::BeforeCompile && event != v8::CompileError && event != v8::PromiseEvent)
    499         return;
    500 
    501     v8::Handle<v8::Context> eventContext = eventDetails.GetEventContext();
    502     ASSERT(!eventContext.IsEmpty());
    503 
    504     ScriptDebugListener* listener = getDebugListenerForContext(eventContext);
    505     if (listener) {
    506         v8::HandleScope scope(m_isolate);
    507         if (event == v8::BeforeCompile) {
    508             preprocessBeforeCompile(eventDetails);
    509         } else if (event == v8::AfterCompile || event == v8::CompileError) {
    510             v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
    511             v8::Handle<v8::Value> argv[] = { eventDetails.GetEventData() };
    512             v8::Handle<v8::Value> value = callDebuggerMethod("getAfterCompileScript", 1, argv);
    513             ASSERT(value->IsObject());
    514             v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
    515             dispatchDidParseSource(listener, object, event != v8::AfterCompile ? CompileError : CompileSuccess);
    516         } else if (event == v8::Exception) {
    517             v8::Handle<v8::Object> eventData = eventDetails.GetEventData();
    518             v8::Handle<v8::Value> exception = callInternalGetterFunction(eventData, "exception", m_isolate);
    519             handleProgramBreak(ScriptState::from(eventContext), eventDetails.GetExecutionState(), exception, v8::Handle<v8::Array>());
    520         } else if (event == v8::Break) {
    521             v8::Handle<v8::Value> argv[] = { eventDetails.GetEventData() };
    522             v8::Handle<v8::Value> hitBreakpoints = callDebuggerMethod("getBreakpointNumbers", 1, argv);
    523             ASSERT(hitBreakpoints->IsArray());
    524             handleProgramBreak(ScriptState::from(eventContext), eventDetails.GetExecutionState(), v8::Handle<v8::Value>(), hitBreakpoints.As<v8::Array>());
    525         } else if (event == v8::AsyncTaskEvent) {
    526             if (listener->v8AsyncTaskEventsEnabled())
    527                 handleV8AsyncTaskEvent(listener, ScriptState::from(eventContext), eventDetails.GetExecutionState(), eventDetails.GetEventData());
    528         } else if (event == v8::PromiseEvent) {
    529             if (listener->v8PromiseEventsEnabled())
    530                 handleV8PromiseEvent(listener, ScriptState::from(eventContext), eventDetails.GetExecutionState(), eventDetails.GetEventData());
    531         }
    532     }
    533 }
    534 
    535 void ScriptDebugServer::handleV8AsyncTaskEvent(ScriptDebugListener* listener, ScriptState* pausedScriptState, v8::Handle<v8::Object> executionState, v8::Handle<v8::Object> eventData)
    536 {
    537     String type = toCoreStringWithUndefinedOrNullCheck(callInternalGetterFunction(eventData, "type", m_isolate));
    538     String name = toCoreStringWithUndefinedOrNullCheck(callInternalGetterFunction(eventData, "name", m_isolate));
    539     int id = callInternalGetterFunction(eventData, "id", m_isolate)->ToInteger()->Value();
    540 
    541     m_pausedScriptState = pausedScriptState;
    542     m_executionState = executionState;
    543     listener->didReceiveV8AsyncTaskEvent(pausedScriptState->executionContext(), type, name, id);
    544     m_pausedScriptState.clear();
    545     m_executionState.Clear();
    546 }
    547 
    548 void ScriptDebugServer::handleV8PromiseEvent(ScriptDebugListener* listener, ScriptState* pausedScriptState, v8::Handle<v8::Object> executionState, v8::Handle<v8::Object> eventData)
    549 {
    550     v8::Handle<v8::Value> argv[] = { eventData };
    551     v8::Handle<v8::Value> value = callDebuggerMethod("getPromiseDetails", 1, argv);
    552     ASSERT(value->IsObject());
    553     v8::Handle<v8::Object> promiseDetails = v8::Handle<v8::Object>::Cast(value);
    554     v8::Handle<v8::Object> promise = promiseDetails->Get(v8AtomicString(m_isolate, "promise"))->ToObject();
    555     int status = promiseDetails->Get(v8AtomicString(m_isolate, "status"))->ToInteger()->Value();
    556     v8::Handle<v8::Value> parentPromise = promiseDetails->Get(v8AtomicString(m_isolate, "parentPromise"));
    557 
    558     m_pausedScriptState = pausedScriptState;
    559     m_executionState = executionState;
    560     listener->didReceiveV8PromiseEvent(pausedScriptState, promise, parentPromise, status);
    561     m_pausedScriptState.clear();
    562     m_executionState.Clear();
    563 }
    564 
    565 void ScriptDebugServer::dispatchDidParseSource(ScriptDebugListener* listener, v8::Handle<v8::Object> object, CompileResult compileResult)
    566 {
    567     v8::Handle<v8::Value> id = object->Get(v8AtomicString(m_isolate, "id"));
    568     ASSERT(!id.IsEmpty() && id->IsInt32());
    569     String sourceID = String::number(id->Int32Value());
    570 
    571     ScriptDebugListener::Script script;
    572     script.url = toCoreStringWithUndefinedOrNullCheck(object->Get(v8AtomicString(m_isolate, "name")));
    573     script.sourceURL = toCoreStringWithUndefinedOrNullCheck(object->Get(v8AtomicString(m_isolate, "sourceURL")));
    574     script.sourceMappingURL = toCoreStringWithUndefinedOrNullCheck(object->Get(v8AtomicString(m_isolate, "sourceMappingURL")));
    575     script.source = toCoreStringWithUndefinedOrNullCheck(object->Get(v8AtomicString(m_isolate, "source")));
    576     script.startLine = object->Get(v8AtomicString(m_isolate, "startLine"))->ToInteger()->Value();
    577     script.startColumn = object->Get(v8AtomicString(m_isolate, "startColumn"))->ToInteger()->Value();
    578     script.endLine = object->Get(v8AtomicString(m_isolate, "endLine"))->ToInteger()->Value();
    579     script.endColumn = object->Get(v8AtomicString(m_isolate, "endColumn"))->ToInteger()->Value();
    580     script.isContentScript = object->Get(v8AtomicString(m_isolate, "isContentScript"))->ToBoolean()->Value();
    581 
    582     listener->didParseSource(sourceID, script, compileResult);
    583 }
    584 
    585 void ScriptDebugServer::ensureDebuggerScriptCompiled()
    586 {
    587     if (!m_debuggerScript.isEmpty())
    588         return;
    589 
    590     v8::HandleScope scope(m_isolate);
    591     v8::Context::Scope contextScope(v8::Debug::GetDebugContext());
    592     const blink::WebData& debuggerScriptSourceResource = blink::Platform::current()->loadResource("DebuggerScriptSource.js");
    593     v8::Handle<v8::String> source = v8String(m_isolate, String(debuggerScriptSourceResource.data(), debuggerScriptSourceResource.size()));
    594     v8::Local<v8::Value> value = V8ScriptRunner::compileAndRunInternalScript(source, m_isolate);
    595     ASSERT(!value.IsEmpty());
    596     ASSERT(value->IsObject());
    597     m_debuggerScript.set(m_isolate, v8::Handle<v8::Object>::Cast(value));
    598 }
    599 
    600 void ScriptDebugServer::discardDebuggerScript()
    601 {
    602     ASSERT(!m_debuggerScript.isEmpty());
    603     m_debuggerScript.clear();
    604 }
    605 
    606 v8::Local<v8::Value> ScriptDebugServer::functionScopes(v8::Handle<v8::Function> function)
    607 {
    608     ensureDebuggerScriptCompiled();
    609 
    610     v8::Handle<v8::Value> argv[] = { function };
    611     return callDebuggerMethod("getFunctionScopes", 1, argv);
    612 }
    613 
    614 v8::Local<v8::Value> ScriptDebugServer::collectionEntries(v8::Handle<v8::Object>& object)
    615 {
    616     ensureDebuggerScriptCompiled();
    617 
    618     v8::Handle<v8::Value> argv[] = { object };
    619     return callDebuggerMethod("getCollectionEntries", 1, argv);
    620 }
    621 
    622 v8::Local<v8::Value> ScriptDebugServer::getInternalProperties(v8::Handle<v8::Object>& object)
    623 {
    624     if (m_debuggerScript.isEmpty())
    625         return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate));
    626 
    627     v8::Handle<v8::Value> argv[] = { object };
    628     return callDebuggerMethod("getInternalProperties", 1, argv);
    629 }
    630 
    631 v8::Handle<v8::Value> ScriptDebugServer::setFunctionVariableValue(v8::Handle<v8::Value> functionValue, int scopeNumber, const String& variableName, v8::Handle<v8::Value> newValue)
    632 {
    633     v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
    634     if (m_debuggerScript.isEmpty())
    635         return m_isolate->ThrowException(v8::String::NewFromUtf8(m_isolate, "Debugging is not enabled."));
    636 
    637     v8::Handle<v8::Value> argv[] = {
    638         functionValue,
    639         v8::Handle<v8::Value>(v8::Integer::New(debuggerContext->GetIsolate(), scopeNumber)),
    640         v8String(debuggerContext->GetIsolate(), variableName),
    641         newValue
    642     };
    643     return callDebuggerMethod("setFunctionVariableValue", 4, argv);
    644 }
    645 
    646 
    647 bool ScriptDebugServer::isPaused()
    648 {
    649     return m_pausedScriptState;
    650 }
    651 
    652 void ScriptDebugServer::compileScript(ScriptState* scriptState, const String& expression, const String& sourceURL, String* scriptId, String* exceptionDetailsText, int* lineNumber, int* columnNumber, RefPtrWillBeRawPtr<ScriptCallStack>* stackTrace)
    653 {
    654     if (scriptState->contextIsValid())
    655         return;
    656     ScriptState::Scope scope(scriptState);
    657 
    658     v8::Handle<v8::String> source = v8String(m_isolate, expression);
    659     v8::TryCatch tryCatch;
    660     v8::Local<v8::Script> script = V8ScriptRunner::compileScript(source, sourceURL, TextPosition(), 0, 0, m_isolate);
    661     if (tryCatch.HasCaught()) {
    662         v8::Local<v8::Message> message = tryCatch.Message();
    663         if (!message.IsEmpty()) {
    664             *exceptionDetailsText = toCoreStringWithUndefinedOrNullCheck(message->Get());
    665             *lineNumber = message->GetLineNumber();
    666             *columnNumber = message->GetStartColumn();
    667             *stackTrace = createScriptCallStack(message->GetStackTrace(), message->GetStackTrace()->GetFrameCount(), m_isolate);
    668         }
    669         return;
    670     }
    671     if (script.IsEmpty())
    672         return;
    673 
    674     *scriptId = String::number(script->GetUnboundScript()->GetId());
    675     m_compiledScripts.set(*scriptId, adoptPtr(new ScopedPersistent<v8::Script>(m_isolate, script)));
    676 }
    677 
    678 void ScriptDebugServer::clearCompiledScripts()
    679 {
    680     m_compiledScripts.clear();
    681 }
    682 
    683 void ScriptDebugServer::runScript(ScriptState* scriptState, const String& scriptId, ScriptValue* result, bool* wasThrown, String* exceptionDetailsText, int* lineNumber, int* columnNumber, RefPtrWillBeRawPtr<ScriptCallStack>* stackTrace)
    684 {
    685     if (!m_compiledScripts.contains(scriptId))
    686         return;
    687     v8::HandleScope handleScope(m_isolate);
    688     ScopedPersistent<v8::Script>* scriptHandle = m_compiledScripts.get(scriptId);
    689     v8::Local<v8::Script> script = scriptHandle->newLocal(m_isolate);
    690     m_compiledScripts.remove(scriptId);
    691     if (script.IsEmpty())
    692         return;
    693 
    694     if (scriptState->contextIsValid())
    695         return;
    696     ScriptState::Scope scope(scriptState);
    697     v8::TryCatch tryCatch;
    698     v8::Local<v8::Value> value = V8ScriptRunner::runCompiledScript(script, scriptState->executionContext(), m_isolate);
    699     *wasThrown = false;
    700     if (tryCatch.HasCaught()) {
    701         *wasThrown = true;
    702         *result = ScriptValue(scriptState, tryCatch.Exception());
    703         v8::Local<v8::Message> message = tryCatch.Message();
    704         if (!message.IsEmpty()) {
    705             *exceptionDetailsText = toCoreStringWithUndefinedOrNullCheck(message->Get());
    706             *lineNumber = message->GetLineNumber();
    707             *columnNumber = message->GetStartColumn();
    708             *stackTrace = createScriptCallStack(message->GetStackTrace(), message->GetStackTrace()->GetFrameCount(), m_isolate);
    709         }
    710     } else {
    711         *result = ScriptValue(scriptState, value);
    712     }
    713 }
    714 
    715 PassOwnPtr<ScriptSourceCode> ScriptDebugServer::preprocess(LocalFrame*, const ScriptSourceCode&)
    716 {
    717     return PassOwnPtr<ScriptSourceCode>();
    718 }
    719 
    720 String ScriptDebugServer::preprocessEventListener(LocalFrame*, const String& source, const String& url, const String& functionName)
    721 {
    722     return source;
    723 }
    724 
    725 } // namespace blink
    726