• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2010-2011 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "config.h"
31 #include "core/inspector/InspectorDebuggerAgent.h"
32 #include "core/inspector/JavaScriptCallFrame.h"
33 
34 #include "bindings/v8/ScriptDebugServer.h"
35 #include "bindings/v8/ScriptObject.h"
36 #include "bindings/v8/ScriptRegexp.h"
37 #include "bindings/v8/ScriptSourceCode.h"
38 #include "core/dom/Document.h"
39 #include "core/fetch/Resource.h"
40 #include "core/inspector/ContentSearchUtils.h"
41 #include "core/inspector/InjectedScriptManager.h"
42 #include "core/inspector/InspectorPageAgent.h"
43 #include "core/inspector/InspectorState.h"
44 #include "core/inspector/InstrumentingAgents.h"
45 #include "core/inspector/ScriptArguments.h"
46 #include "core/inspector/ScriptCallStack.h"
47 #include "platform/JSONValues.h"
48 #include "wtf/text/WTFString.h"
49 
50 using WebCore::TypeBuilder::Array;
51 using WebCore::TypeBuilder::Debugger::BreakpointId;
52 using WebCore::TypeBuilder::Debugger::CallFrame;
53 using WebCore::TypeBuilder::Debugger::FunctionDetails;
54 using WebCore::TypeBuilder::Debugger::ScriptId;
55 using WebCore::TypeBuilder::Debugger::StackTrace;
56 using WebCore::TypeBuilder::Runtime::RemoteObject;
57 
58 namespace WebCore {
59 
60 namespace DebuggerAgentState {
61 static const char debuggerEnabled[] = "debuggerEnabled";
62 static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
63 static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
64 static const char asyncCallStackDepth[] = "asyncCallStackDepth";
65 
66 // Breakpoint properties.
67 static const char url[] = "url";
68 static const char isRegex[] = "isRegex";
69 static const char lineNumber[] = "lineNumber";
70 static const char columnNumber[] = "columnNumber";
71 static const char condition[] = "condition";
72 static const char isAnti[] = "isAnti";
73 static const char skipStackPattern[] = "skipStackPattern";
74 static const char skipAllPauses[] = "skipAllPauses";
75 static const char skipAllPausesExpiresOnReload[] = "skipAllPausesExpiresOnReload";
76 
77 };
78 
79 static const int numberOfStepsBeforeStepOut = 10;
80 
81 const char InspectorDebuggerAgent::backtraceObjectGroup[] = "backtrace";
82 
breakpointIdSuffix(InspectorDebuggerAgent::BreakpointSource source)83 static String breakpointIdSuffix(InspectorDebuggerAgent::BreakpointSource source)
84 {
85     switch (source) {
86     case InspectorDebuggerAgent::UserBreakpointSource:
87         break;
88     case InspectorDebuggerAgent::DebugCommandBreakpointSource:
89         return ":debug";
90     case InspectorDebuggerAgent::MonitorCommandBreakpointSource:
91         return ":monitor";
92     }
93     return String();
94 }
95 
generateBreakpointId(const String & scriptId,int lineNumber,int columnNumber,InspectorDebuggerAgent::BreakpointSource source)96 static String generateBreakpointId(const String& scriptId, int lineNumber, int columnNumber, InspectorDebuggerAgent::BreakpointSource source)
97 {
98     return scriptId + ':' + String::number(lineNumber) + ':' + String::number(columnNumber) + breakpointIdSuffix(source);
99 }
100 
InspectorDebuggerAgent(InstrumentingAgents * instrumentingAgents,InspectorCompositeState * inspectorState,InjectedScriptManager * injectedScriptManager)101 InspectorDebuggerAgent::InspectorDebuggerAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager)
102     : InspectorBaseAgent<InspectorDebuggerAgent>("Debugger", instrumentingAgents, inspectorState)
103     , m_injectedScriptManager(injectedScriptManager)
104     , m_frontend(0)
105     , m_pausedScriptState(0)
106     , m_javaScriptPauseScheduled(false)
107     , m_listener(0)
108     , m_skipStepInCount(numberOfStepsBeforeStepOut)
109     , m_skipAllPauses(false)
110 {
111     // FIXME: make breakReason optional so that there was no need to init it with "other".
112     clearBreakDetails();
113     m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, ScriptDebugServer::DontPauseOnExceptions);
114 }
115 
~InspectorDebuggerAgent()116 InspectorDebuggerAgent::~InspectorDebuggerAgent()
117 {
118     ASSERT(!m_instrumentingAgents->inspectorDebuggerAgent());
119 }
120 
enable()121 void InspectorDebuggerAgent::enable()
122 {
123     m_instrumentingAgents->setInspectorDebuggerAgent(this);
124 
125     // FIXME(WK44513): breakpoints activated flag should be synchronized between all front-ends
126     scriptDebugServer().setBreakpointsActivated(true);
127     startListeningScriptDebugServer();
128 
129     if (m_listener)
130         m_listener->debuggerWasEnabled();
131 }
132 
disable()133 void InspectorDebuggerAgent::disable()
134 {
135     m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, JSONObject::create());
136     m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, ScriptDebugServer::DontPauseOnExceptions);
137     m_state->setString(DebuggerAgentState::skipStackPattern, "");
138     m_state->setLong(DebuggerAgentState::asyncCallStackDepth, 0);
139     m_instrumentingAgents->setInspectorDebuggerAgent(0);
140 
141     stopListeningScriptDebugServer();
142     scriptDebugServer().clearBreakpoints();
143     scriptDebugServer().clearCompiledScripts();
144     clear();
145 
146     if (m_listener)
147         m_listener->debuggerWasDisabled();
148 
149     m_skipAllPauses = false;
150 }
151 
enabled()152 bool InspectorDebuggerAgent::enabled()
153 {
154     return m_state->getBoolean(DebuggerAgentState::debuggerEnabled);
155 }
156 
enable(ErrorString *)157 void InspectorDebuggerAgent::enable(ErrorString*)
158 {
159     if (enabled())
160         return;
161 
162     enable();
163     m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
164 
165     ASSERT(m_frontend);
166 }
167 
disable(ErrorString *)168 void InspectorDebuggerAgent::disable(ErrorString*)
169 {
170     if (!enabled())
171         return;
172 
173     disable();
174     m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
175 }
176 
compileSkipCallFramePattern(String patternText)177 static PassOwnPtr<ScriptRegexp> compileSkipCallFramePattern(String patternText)
178 {
179     if (patternText.isEmpty())
180         return nullptr;
181     OwnPtr<ScriptRegexp> result = adoptPtr(new ScriptRegexp(patternText, TextCaseSensitive));
182     if (!result->isValid())
183         result.clear();
184     return result.release();
185 }
186 
restore()187 void InspectorDebuggerAgent::restore()
188 {
189     if (enabled()) {
190         m_frontend->globalObjectCleared();
191         enable();
192         long pauseState = m_state->getLong(DebuggerAgentState::pauseOnExceptionsState);
193         String error;
194         setPauseOnExceptionsImpl(&error, pauseState);
195         m_cachedSkipStackRegExp = compileSkipCallFramePattern(m_state->getString(DebuggerAgentState::skipStackPattern));
196         m_skipAllPauses = m_state->getBoolean(DebuggerAgentState::skipAllPauses);
197         if (m_skipAllPauses && m_state->getBoolean(DebuggerAgentState::skipAllPausesExpiresOnReload)) {
198             m_skipAllPauses = false;
199             m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
200         }
201         m_asyncCallStackTracker.setAsyncCallStackDepth(m_state->getLong(DebuggerAgentState::asyncCallStackDepth));
202     }
203 }
204 
setFrontend(InspectorFrontend * frontend)205 void InspectorDebuggerAgent::setFrontend(InspectorFrontend* frontend)
206 {
207     m_frontend = frontend->debugger();
208 }
209 
clearFrontend()210 void InspectorDebuggerAgent::clearFrontend()
211 {
212     m_frontend = 0;
213 
214     if (!enabled())
215         return;
216 
217     disable();
218 
219     // FIXME: due to m_state->mute() hack in InspectorController, debuggerEnabled is actually set to false only
220     // in InspectorState, but not in cookie. That's why after navigation debuggerEnabled will be true,
221     // but after front-end re-open it will still be false.
222     m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
223 }
224 
setBreakpointsActive(ErrorString *,bool active)225 void InspectorDebuggerAgent::setBreakpointsActive(ErrorString*, bool active)
226 {
227     scriptDebugServer().setBreakpointsActivated(active);
228 }
229 
setSkipAllPauses(ErrorString *,bool skipped,const bool * untilReload)230 void InspectorDebuggerAgent::setSkipAllPauses(ErrorString*, bool skipped, const bool* untilReload)
231 {
232     m_skipAllPauses = skipped;
233     bool untilReloadValue = untilReload && *untilReload;
234     m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses);
235     m_state->setBoolean(DebuggerAgentState::skipAllPausesExpiresOnReload, untilReloadValue);
236 }
237 
pageDidCommitLoad()238 void InspectorDebuggerAgent::pageDidCommitLoad()
239 {
240     if (m_state->getBoolean(DebuggerAgentState::skipAllPausesExpiresOnReload)) {
241         m_skipAllPauses = false;
242         m_state->setBoolean(DebuggerAgentState::skipAllPauses, m_skipAllPauses);
243     }
244 }
245 
isPaused()246 bool InspectorDebuggerAgent::isPaused()
247 {
248     return scriptDebugServer().isPaused();
249 }
250 
runningNestedMessageLoop()251 bool InspectorDebuggerAgent::runningNestedMessageLoop()
252 {
253     return scriptDebugServer().runningNestedMessageLoop();
254 }
255 
addMessageToConsole(MessageSource source,MessageType type)256 void InspectorDebuggerAgent::addMessageToConsole(MessageSource source, MessageType type)
257 {
258     if (source == ConsoleAPIMessageSource && type == AssertMessageType && scriptDebugServer().pauseOnExceptionsState() != ScriptDebugServer::DontPauseOnExceptions)
259         breakProgram(InspectorFrontend::Debugger::Reason::Assert, 0);
260 }
261 
addMessageToConsole(MessageSource source,MessageType type,MessageLevel,const String &,PassRefPtr<ScriptCallStack>,unsigned long)262 void InspectorDebuggerAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel, const String&, PassRefPtr<ScriptCallStack>, unsigned long)
263 {
264     addMessageToConsole(source, type);
265 }
266 
addMessageToConsole(MessageSource source,MessageType type,MessageLevel,const String &,ScriptState *,PassRefPtr<ScriptArguments>,unsigned long)267 void InspectorDebuggerAgent::addMessageToConsole(MessageSource source, MessageType type, MessageLevel, const String&, ScriptState*, PassRefPtr<ScriptArguments>, unsigned long)
268 {
269     addMessageToConsole(source, type);
270 }
271 
preprocessEventListener(Frame * frame,const String & source,const String & url,const String & functionName)272 String InspectorDebuggerAgent::preprocessEventListener(Frame* frame, const String& source, const String& url, const String& functionName)
273 {
274     return scriptDebugServer().preprocessEventListener(frame, source, url, functionName);
275 }
276 
preprocess(Frame * frame,const ScriptSourceCode & sourceCode)277 PassOwnPtr<ScriptSourceCode> InspectorDebuggerAgent::preprocess(Frame* frame, const ScriptSourceCode& sourceCode)
278 {
279     return scriptDebugServer().preprocess(frame, sourceCode);
280 }
281 
buildObjectForBreakpointCookie(const String & url,int lineNumber,int columnNumber,const String & condition,bool isRegex,bool isAnti)282 static PassRefPtr<JSONObject> buildObjectForBreakpointCookie(const String& url, int lineNumber, int columnNumber, const String& condition, bool isRegex, bool isAnti)
283 {
284     RefPtr<JSONObject> breakpointObject = JSONObject::create();
285     breakpointObject->setString(DebuggerAgentState::url, url);
286     breakpointObject->setNumber(DebuggerAgentState::lineNumber, lineNumber);
287     breakpointObject->setNumber(DebuggerAgentState::columnNumber, columnNumber);
288     breakpointObject->setString(DebuggerAgentState::condition, condition);
289     breakpointObject->setBoolean(DebuggerAgentState::isRegex, isRegex);
290     breakpointObject->setBoolean(DebuggerAgentState::isAnti, isAnti);
291     return breakpointObject;
292 }
293 
matches(const String & url,const String & pattern,bool isRegex)294 static bool matches(const String& url, const String& pattern, bool isRegex)
295 {
296     if (isRegex) {
297         ScriptRegexp regex(pattern, TextCaseSensitive);
298         return regex.match(url) != -1;
299     }
300     return url == pattern;
301 }
302 
setBreakpointByUrl(ErrorString * errorString,int lineNumber,const String * const optionalURL,const String * const optionalURLRegex,const int * const optionalColumnNumber,const String * const optionalCondition,const bool * isAntiBreakpoint,BreakpointId * outBreakpointId,RefPtr<Array<TypeBuilder::Debugger::Location>> & locations)303 void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString* errorString, int lineNumber, const String* const optionalURL, const String* const optionalURLRegex, const int* const optionalColumnNumber, const String* const optionalCondition, const bool* isAntiBreakpoint, BreakpointId* outBreakpointId, RefPtr<Array<TypeBuilder::Debugger::Location> >& locations)
304 {
305     locations = Array<TypeBuilder::Debugger::Location>::create();
306     if (!optionalURL == !optionalURLRegex) {
307         *errorString = "Either url or urlRegex must be specified.";
308         return;
309     }
310 
311     bool isAntiBreakpointValue = isAntiBreakpoint && *isAntiBreakpoint;
312 
313     String url = optionalURL ? *optionalURL : *optionalURLRegex;
314     int columnNumber;
315     if (optionalColumnNumber) {
316         columnNumber = *optionalColumnNumber;
317         if (columnNumber < 0) {
318             *errorString = "Incorrect column number";
319             return;
320         }
321     } else {
322         columnNumber = isAntiBreakpointValue ? -1 : 0;
323     }
324     String condition = optionalCondition ? *optionalCondition : "";
325     bool isRegex = optionalURLRegex;
326 
327     String breakpointId = (isRegex ? "/" + url + "/" : url) + ':' + String::number(lineNumber) + ':' + String::number(columnNumber);
328     RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
329     if (breakpointsCookie->find(breakpointId) != breakpointsCookie->end()) {
330         *errorString = "Breakpoint at specified location already exists.";
331         return;
332     }
333 
334     breakpointsCookie->setObject(breakpointId, buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, isRegex, isAntiBreakpointValue));
335     m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCookie);
336 
337     if (!isAntiBreakpointValue) {
338         ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
339         for (ScriptsMap::iterator it = m_scripts.begin(); it != m_scripts.end(); ++it) {
340             if (!matches(it->value.url, url, isRegex))
341                 continue;
342             RefPtr<TypeBuilder::Debugger::Location> location = resolveBreakpoint(breakpointId, it->key, breakpoint, UserBreakpointSource);
343             if (location)
344                 locations->addItem(location);
345         }
346     }
347     *outBreakpointId = breakpointId;
348 }
349 
parseLocation(ErrorString * errorString,PassRefPtr<JSONObject> location,String * scriptId,int * lineNumber,int * columnNumber)350 static bool parseLocation(ErrorString* errorString, PassRefPtr<JSONObject> location, String* scriptId, int* lineNumber, int* columnNumber)
351 {
352     if (!location->getString("scriptId", scriptId) || !location->getNumber("lineNumber", lineNumber)) {
353         // FIXME: replace with input validation.
354         *errorString = "scriptId and lineNumber are required.";
355         return false;
356     }
357     *columnNumber = 0;
358     location->getNumber("columnNumber", columnNumber);
359     return true;
360 }
361 
setBreakpoint(ErrorString * errorString,const RefPtr<JSONObject> & location,const String * const optionalCondition,BreakpointId * outBreakpointId,RefPtr<TypeBuilder::Debugger::Location> & actualLocation)362 void InspectorDebuggerAgent::setBreakpoint(ErrorString* errorString, const RefPtr<JSONObject>& location, const String* const optionalCondition, BreakpointId* outBreakpointId, RefPtr<TypeBuilder::Debugger::Location>& actualLocation)
363 {
364     String scriptId;
365     int lineNumber;
366     int columnNumber;
367 
368     if (!parseLocation(errorString, location, &scriptId, &lineNumber, &columnNumber))
369         return;
370 
371     String condition = optionalCondition ? *optionalCondition : emptyString();
372 
373     String breakpointId = generateBreakpointId(scriptId, lineNumber, columnNumber, UserBreakpointSource);
374     if (m_breakpointIdToDebugServerBreakpointIds.find(breakpointId) != m_breakpointIdToDebugServerBreakpointIds.end()) {
375         *errorString = "Breakpoint at specified location already exists.";
376         return;
377     }
378     ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
379     actualLocation = resolveBreakpoint(breakpointId, scriptId, breakpoint, UserBreakpointSource);
380     if (actualLocation)
381         *outBreakpointId = breakpointId;
382     else
383         *errorString = "Could not resolve breakpoint";
384 }
385 
removeBreakpoint(ErrorString *,const String & breakpointId)386 void InspectorDebuggerAgent::removeBreakpoint(ErrorString*, const String& breakpointId)
387 {
388     RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
389     JSONObject::iterator it = breakpointsCookie->find(breakpointId);
390     bool isAntibreakpoint = false;
391     if (it != breakpointsCookie->end()) {
392         RefPtr<JSONObject> breakpointObject = it->value->asObject();
393         breakpointObject->getBoolean(DebuggerAgentState::isAnti, &isAntibreakpoint);
394         breakpointsCookie->remove(breakpointId);
395         m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCookie);
396     }
397 
398     if (!isAntibreakpoint)
399         removeBreakpoint(breakpointId);
400 }
401 
removeBreakpoint(const String & breakpointId)402 void InspectorDebuggerAgent::removeBreakpoint(const String& breakpointId)
403 {
404     BreakpointIdToDebugServerBreakpointIdsMap::iterator debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.find(breakpointId);
405     if (debugServerBreakpointIdsIterator == m_breakpointIdToDebugServerBreakpointIds.end())
406         return;
407     for (size_t i = 0; i < debugServerBreakpointIdsIterator->value.size(); ++i) {
408         const String& debugServerBreakpointId = debugServerBreakpointIdsIterator->value[i];
409         scriptDebugServer().removeBreakpoint(debugServerBreakpointId);
410         m_serverBreakpoints.remove(debugServerBreakpointId);
411     }
412     m_breakpointIdToDebugServerBreakpointIds.remove(debugServerBreakpointIdsIterator);
413 }
414 
continueToLocation(ErrorString * errorString,const RefPtr<JSONObject> & location,const bool * interstateLocationOpt)415 void InspectorDebuggerAgent::continueToLocation(ErrorString* errorString, const RefPtr<JSONObject>& location, const bool* interstateLocationOpt)
416 {
417     bool interstateLocation = interstateLocationOpt ? *interstateLocationOpt : false;
418     if (!m_continueToLocationBreakpointId.isEmpty()) {
419         scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointId);
420         m_continueToLocationBreakpointId = "";
421     }
422 
423     String scriptId;
424     int lineNumber;
425     int columnNumber;
426 
427     if (!parseLocation(errorString, location, &scriptId, &lineNumber, &columnNumber))
428         return;
429 
430     ScriptBreakpoint breakpoint(lineNumber, columnNumber, "");
431     m_continueToLocationBreakpointId = scriptDebugServer().setBreakpoint(scriptId, breakpoint, &lineNumber, &columnNumber, interstateLocation);
432     resume(errorString);
433 }
434 
getStepInPositions(ErrorString * errorString,const String & callFrameId,RefPtr<Array<TypeBuilder::Debugger::Location>> & positions)435 void InspectorDebuggerAgent::getStepInPositions(ErrorString* errorString, const String& callFrameId, RefPtr<Array<TypeBuilder::Debugger::Location> >& positions)
436 {
437     if (!isPaused() || m_currentCallStack.hasNoValue()) {
438         *errorString = "Attempt to access callframe when debugger is not on pause";
439         return;
440     }
441     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId);
442     if (injectedScript.hasNoValue()) {
443         *errorString = "Inspected frame has gone";
444         return;
445     }
446 
447     injectedScript.getStepInPositions(errorString, m_currentCallStack, callFrameId, positions);
448 }
449 
getBacktrace(ErrorString * errorString,RefPtr<Array<CallFrame>> & callFrames,RefPtr<StackTrace> & asyncStackTrace)450 void InspectorDebuggerAgent::getBacktrace(ErrorString* errorString, RefPtr<Array<CallFrame> >& callFrames, RefPtr<StackTrace>& asyncStackTrace)
451 {
452     if (!assertPaused(errorString))
453         return;
454     m_currentCallStack = scriptDebugServer().currentCallFrames();
455     callFrames = currentCallFrames();
456     asyncStackTrace = currentAsyncStackTrace();
457 }
458 
scriptURL(JavaScriptCallFrame * frame)459 String InspectorDebuggerAgent::scriptURL(JavaScriptCallFrame* frame)
460 {
461     String scriptIdString = String::number(frame->sourceID());
462     ScriptsMap::iterator it = m_scripts.find(scriptIdString);
463     if (it == m_scripts.end())
464         return String();
465     return it->value.url;
466 }
467 
shouldSkipExceptionPause(RefPtr<JavaScriptCallFrame> & topFrame)468 ScriptDebugListener::SkipPauseRequest InspectorDebuggerAgent::shouldSkipExceptionPause(RefPtr<JavaScriptCallFrame>& topFrame)
469 {
470     if (m_skipAllPauses)
471         return ScriptDebugListener::Continue;
472     if (!topFrame)
473         return ScriptDebugListener::NoSkip;
474 
475     String topFrameScriptUrl = scriptURL(topFrame.get());
476     if (m_cachedSkipStackRegExp && !topFrameScriptUrl.isEmpty() && m_cachedSkipStackRegExp->match(topFrameScriptUrl) != -1)
477         return ScriptDebugListener::Continue;
478 
479     // Match against breakpoints.
480     if (topFrameScriptUrl.isEmpty())
481         return ScriptDebugListener::NoSkip;
482 
483     // Prepare top frame parameters.
484     int topFrameLineNumber = topFrame->line();
485     int topFrameColumnNumber = topFrame->column();
486 
487     RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
488     for (JSONObject::iterator it = breakpointsCookie->begin(); it != breakpointsCookie->end(); ++it) {
489         RefPtr<JSONObject> breakpointObject = it->value->asObject();
490         bool isAntibreakpoint;
491         breakpointObject->getBoolean(DebuggerAgentState::isAnti, &isAntibreakpoint);
492         if (!isAntibreakpoint)
493             continue;
494 
495         int breakLineNumber;
496         breakpointObject->getNumber(DebuggerAgentState::lineNumber, &breakLineNumber);
497         int breakColumnNumber;
498         breakpointObject->getNumber(DebuggerAgentState::columnNumber, &breakColumnNumber);
499 
500         if (breakLineNumber != topFrameLineNumber)
501             continue;
502 
503         if (breakColumnNumber != -1 && breakColumnNumber != topFrameColumnNumber)
504             continue;
505 
506         bool isRegex;
507         breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex);
508         String url;
509         breakpointObject->getString(DebuggerAgentState::url, &url);
510         if (!matches(topFrameScriptUrl, url, isRegex))
511             continue;
512 
513         return ScriptDebugListener::Continue;
514     }
515 
516     return ScriptDebugListener::NoSkip;
517 }
518 
shouldSkipBreakpointPause(RefPtr<JavaScriptCallFrame> & topFrame)519 ScriptDebugListener::SkipPauseRequest InspectorDebuggerAgent::shouldSkipBreakpointPause(RefPtr<JavaScriptCallFrame>& topFrame)
520 {
521     if (m_skipAllPauses)
522         return ScriptDebugListener::Continue;
523     if (!topFrame)
524         return ScriptDebugListener::NoSkip;
525     return ScriptDebugListener::NoSkip;
526 }
527 
shouldSkipStepPause(RefPtr<JavaScriptCallFrame> & topFrame)528 ScriptDebugListener::SkipPauseRequest InspectorDebuggerAgent::shouldSkipStepPause(RefPtr<JavaScriptCallFrame>& topFrame)
529 {
530     if (m_skipAllPauses)
531         return ScriptDebugListener::Continue;
532     if (!topFrame)
533         return ScriptDebugListener::NoSkip;
534 
535     if (m_cachedSkipStackRegExp) {
536         String scriptUrl = scriptURL(topFrame.get());
537         if (!scriptUrl.isEmpty() && m_cachedSkipStackRegExp->match(scriptUrl) != -1) {
538             if (m_skipStepInCount > 0) {
539                 --m_skipStepInCount;
540                 return ScriptDebugListener::StepInto;
541             }
542             return ScriptDebugListener::StepOut;
543         }
544     }
545     return ScriptDebugListener::NoSkip;
546 }
547 
resolveBreakpoint(const String & breakpointId,const String & scriptId,const ScriptBreakpoint & breakpoint,BreakpointSource source)548 PassRefPtr<TypeBuilder::Debugger::Location> InspectorDebuggerAgent::resolveBreakpoint(const String& breakpointId, const String& scriptId, const ScriptBreakpoint& breakpoint, BreakpointSource source)
549 {
550     ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
551     if (scriptIterator == m_scripts.end())
552         return 0;
553     Script& script = scriptIterator->value;
554     if (breakpoint.lineNumber < script.startLine || script.endLine < breakpoint.lineNumber)
555         return 0;
556 
557     int actualLineNumber;
558     int actualColumnNumber;
559     String debugServerBreakpointId = scriptDebugServer().setBreakpoint(scriptId, breakpoint, &actualLineNumber, &actualColumnNumber, false);
560     if (debugServerBreakpointId.isEmpty())
561         return 0;
562 
563     m_serverBreakpoints.set(debugServerBreakpointId, std::make_pair(breakpointId, source));
564 
565     BreakpointIdToDebugServerBreakpointIdsMap::iterator debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.find(breakpointId);
566     if (debugServerBreakpointIdsIterator == m_breakpointIdToDebugServerBreakpointIds.end())
567         debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.set(breakpointId, Vector<String>()).iterator;
568     debugServerBreakpointIdsIterator->value.append(debugServerBreakpointId);
569 
570     RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create()
571         .setScriptId(scriptId)
572         .setLineNumber(actualLineNumber);
573     location->setColumnNumber(actualColumnNumber);
574     return location;
575 }
576 
scriptToInspectorObject(ScriptObject scriptObject)577 static PassRefPtr<JSONObject> scriptToInspectorObject(ScriptObject scriptObject)
578 {
579     if (scriptObject.hasNoValue())
580         return 0;
581     RefPtr<JSONValue> value = scriptObject.toJSONValue(scriptObject.scriptState());
582     if (!value)
583         return 0;
584     return value->asObject();
585 }
586 
searchInContent(ErrorString * error,const String & scriptId,const String & query,const bool * const optionalCaseSensitive,const bool * const optionalIsRegex,RefPtr<Array<WebCore::TypeBuilder::Page::SearchMatch>> & results)587 void InspectorDebuggerAgent::searchInContent(ErrorString* error, const String& scriptId, const String& query, const bool* const optionalCaseSensitive, const bool* const optionalIsRegex, RefPtr<Array<WebCore::TypeBuilder::Page::SearchMatch> >& results)
588 {
589     bool isRegex = optionalIsRegex ? *optionalIsRegex : false;
590     bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false;
591 
592     ScriptsMap::iterator it = m_scripts.find(scriptId);
593     if (it != m_scripts.end())
594         results = ContentSearchUtils::searchInTextByLines(it->value.source, query, caseSensitive, isRegex);
595     else
596         *error = "No script for id: " + scriptId;
597 }
598 
setScriptSource(ErrorString * error,RefPtr<TypeBuilder::Debugger::SetScriptSourceError> & errorData,const String & scriptId,const String & newContent,const bool * const preview,RefPtr<Array<CallFrame>> & newCallFrames,RefPtr<JSONObject> & result,RefPtr<StackTrace> & asyncStackTrace)599 void InspectorDebuggerAgent::setScriptSource(ErrorString* error, RefPtr<TypeBuilder::Debugger::SetScriptSourceError>& errorData, const String& scriptId, const String& newContent, const bool* const preview, RefPtr<Array<CallFrame> >& newCallFrames, RefPtr<JSONObject>& result, RefPtr<StackTrace>& asyncStackTrace)
600 {
601     bool previewOnly = preview && *preview;
602     ScriptObject resultObject;
603     if (!scriptDebugServer().setScriptSource(scriptId, newContent, previewOnly, error, errorData, &m_currentCallStack, &resultObject))
604         return;
605     newCallFrames = currentCallFrames();
606     asyncStackTrace = currentAsyncStackTrace();
607     RefPtr<JSONObject> object = scriptToInspectorObject(resultObject);
608     if (object)
609         result = object;
610 }
611 
restartFrame(ErrorString * errorString,const String & callFrameId,RefPtr<Array<CallFrame>> & newCallFrames,RefPtr<JSONObject> & result,RefPtr<StackTrace> & asyncStackTrace)612 void InspectorDebuggerAgent::restartFrame(ErrorString* errorString, const String& callFrameId, RefPtr<Array<CallFrame> >& newCallFrames, RefPtr<JSONObject>& result, RefPtr<StackTrace>& asyncStackTrace)
613 {
614     if (!isPaused() || m_currentCallStack.hasNoValue()) {
615         *errorString = "Attempt to access callframe when debugger is not on pause";
616         return;
617     }
618     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId);
619     if (injectedScript.hasNoValue()) {
620         *errorString = "Inspected frame has gone";
621         return;
622     }
623 
624     injectedScript.restartFrame(errorString, m_currentCallStack, callFrameId, &result);
625     m_currentCallStack = scriptDebugServer().currentCallFrames();
626     newCallFrames = currentCallFrames();
627     asyncStackTrace = currentAsyncStackTrace();
628 }
629 
getScriptSource(ErrorString * error,const String & scriptId,String * scriptSource)630 void InspectorDebuggerAgent::getScriptSource(ErrorString* error, const String& scriptId, String* scriptSource)
631 {
632     ScriptsMap::iterator it = m_scripts.find(scriptId);
633     if (it != m_scripts.end())
634         *scriptSource = it->value.source;
635     else
636         *error = "No script for id: " + scriptId;
637 }
638 
getFunctionDetails(ErrorString * errorString,const String & functionId,RefPtr<FunctionDetails> & details)639 void InspectorDebuggerAgent::getFunctionDetails(ErrorString* errorString, const String& functionId, RefPtr<FunctionDetails>& details)
640 {
641     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(functionId);
642     if (injectedScript.hasNoValue()) {
643         *errorString = "Function object id is obsolete";
644         return;
645     }
646     injectedScript.getFunctionDetails(errorString, functionId, &details);
647 }
648 
schedulePauseOnNextStatement(InspectorFrontend::Debugger::Reason::Enum breakReason,PassRefPtr<JSONObject> data)649 void InspectorDebuggerAgent::schedulePauseOnNextStatement(InspectorFrontend::Debugger::Reason::Enum breakReason, PassRefPtr<JSONObject> data)
650 {
651     if (m_javaScriptPauseScheduled)
652         return;
653     m_breakReason = breakReason;
654     m_breakAuxData = data;
655     scriptDebugServer().setPauseOnNextStatement(true);
656 }
657 
cancelPauseOnNextStatement()658 void InspectorDebuggerAgent::cancelPauseOnNextStatement()
659 {
660     if (m_javaScriptPauseScheduled)
661         return;
662     clearBreakDetails();
663     scriptDebugServer().setPauseOnNextStatement(false);
664 }
665 
didInstallTimer(ExecutionContext * context,int timerId,int timeout,bool singleShot)666 void InspectorDebuggerAgent::didInstallTimer(ExecutionContext* context, int timerId, int timeout, bool singleShot)
667 {
668     if (m_asyncCallStackTracker.isEnabled())
669         m_asyncCallStackTracker.didInstallTimer(context, timerId, singleShot, scriptDebugServer().currentCallFrames());
670 }
671 
didRemoveTimer(ExecutionContext * context,int timerId)672 void InspectorDebuggerAgent::didRemoveTimer(ExecutionContext* context, int timerId)
673 {
674     m_asyncCallStackTracker.didRemoveTimer(context, timerId);
675 }
676 
willFireTimer(ExecutionContext * context,int timerId)677 bool InspectorDebuggerAgent::willFireTimer(ExecutionContext* context, int timerId)
678 {
679     m_asyncCallStackTracker.willFireTimer(context, timerId);
680     return true;
681 }
682 
didFireTimer()683 void InspectorDebuggerAgent::didFireTimer()
684 {
685     m_asyncCallStackTracker.didFireAsyncCall();
686     cancelPauseOnNextStatement();
687 }
688 
didRequestAnimationFrame(Document * document,int callbackId)689 void InspectorDebuggerAgent::didRequestAnimationFrame(Document* document, int callbackId)
690 {
691     if (m_asyncCallStackTracker.isEnabled())
692         m_asyncCallStackTracker.didRequestAnimationFrame(document, callbackId, scriptDebugServer().currentCallFrames());
693 }
694 
didCancelAnimationFrame(Document * document,int callbackId)695 void InspectorDebuggerAgent::didCancelAnimationFrame(Document* document, int callbackId)
696 {
697     m_asyncCallStackTracker.didCancelAnimationFrame(document, callbackId);
698 }
699 
willFireAnimationFrame(Document * document,int callbackId)700 bool InspectorDebuggerAgent::willFireAnimationFrame(Document* document, int callbackId)
701 {
702     m_asyncCallStackTracker.willFireAnimationFrame(document, callbackId);
703     return true;
704 }
705 
didFireAnimationFrame()706 void InspectorDebuggerAgent::didFireAnimationFrame()
707 {
708     m_asyncCallStackTracker.didFireAsyncCall();
709 }
710 
didHandleEvent()711 void InspectorDebuggerAgent::didHandleEvent()
712 {
713     cancelPauseOnNextStatement();
714 }
715 
pause(ErrorString *)716 void InspectorDebuggerAgent::pause(ErrorString*)
717 {
718     if (m_javaScriptPauseScheduled)
719         return;
720     clearBreakDetails();
721     scriptDebugServer().setPauseOnNextStatement(true);
722     m_javaScriptPauseScheduled = true;
723 }
724 
resume(ErrorString * errorString)725 void InspectorDebuggerAgent::resume(ErrorString* errorString)
726 {
727     if (!assertPaused(errorString))
728         return;
729     m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
730     scriptDebugServer().continueProgram();
731 }
732 
resolveCallFrame(ErrorString * errorString,const String * callFrameId)733 ScriptValue InspectorDebuggerAgent::resolveCallFrame(ErrorString* errorString, const String* callFrameId)
734 {
735     if (!callFrameId)
736         return ScriptValue();
737     if (!isPaused() || m_currentCallStack.hasNoValue()) {
738         *errorString = "Attempt to access callframe when debugger is not on pause";
739         return ScriptValue();
740     }
741     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*callFrameId);
742     if (injectedScript.hasNoValue()) {
743         *errorString = "Inspected frame has gone";
744         return ScriptValue();
745     }
746     return injectedScript.findCallFrameById(errorString, m_currentCallStack, *callFrameId);
747 }
748 
stepOver(ErrorString * errorString,const String * callFrameId)749 void InspectorDebuggerAgent::stepOver(ErrorString* errorString, const String* callFrameId)
750 {
751     if (!assertPaused(errorString))
752         return;
753     ScriptValue frame = resolveCallFrame(errorString, callFrameId);
754     if (!errorString->isEmpty())
755         return;
756     m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
757     scriptDebugServer().stepOverStatement(frame);
758 }
759 
stepInto(ErrorString * errorString)760 void InspectorDebuggerAgent::stepInto(ErrorString* errorString)
761 {
762     if (!assertPaused(errorString))
763         return;
764     m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
765     scriptDebugServer().stepIntoStatement();
766     if (m_listener)
767         m_listener->stepInto();
768 }
769 
stepOut(ErrorString * errorString,const String * callFrameId)770 void InspectorDebuggerAgent::stepOut(ErrorString* errorString, const String* callFrameId)
771 {
772     if (!assertPaused(errorString))
773         return;
774     ScriptValue frame = resolveCallFrame(errorString, callFrameId);
775     if (!errorString->isEmpty())
776         return;
777     m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
778     scriptDebugServer().stepOutOfFunction(frame);
779 }
780 
setPauseOnExceptions(ErrorString * errorString,const String & stringPauseState)781 void InspectorDebuggerAgent::setPauseOnExceptions(ErrorString* errorString, const String& stringPauseState)
782 {
783     ScriptDebugServer::PauseOnExceptionsState pauseState;
784     if (stringPauseState == "none")
785         pauseState = ScriptDebugServer::DontPauseOnExceptions;
786     else if (stringPauseState == "all")
787         pauseState = ScriptDebugServer::PauseOnAllExceptions;
788     else if (stringPauseState == "uncaught")
789         pauseState = ScriptDebugServer::PauseOnUncaughtExceptions;
790     else {
791         *errorString = "Unknown pause on exceptions mode: " + stringPauseState;
792         return;
793     }
794     setPauseOnExceptionsImpl(errorString, pauseState);
795 }
796 
setPauseOnExceptionsImpl(ErrorString * errorString,int pauseState)797 void InspectorDebuggerAgent::setPauseOnExceptionsImpl(ErrorString* errorString, int pauseState)
798 {
799     scriptDebugServer().setPauseOnExceptionsState(static_cast<ScriptDebugServer::PauseOnExceptionsState>(pauseState));
800     if (scriptDebugServer().pauseOnExceptionsState() != pauseState)
801         *errorString = "Internal error. Could not change pause on exceptions state";
802     else
803         m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, pauseState);
804 }
805 
evaluateOnCallFrame(ErrorString * errorString,const String & callFrameId,const String & expression,const String * const objectGroup,const bool * const includeCommandLineAPI,const bool * const doNotPauseOnExceptionsAndMuteConsole,const bool * const returnByValue,const bool * generatePreview,RefPtr<RemoteObject> & result,TypeBuilder::OptOutput<bool> * wasThrown)806 void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString* errorString, const String& callFrameId, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* generatePreview, RefPtr<RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown)
807 {
808     if (!isPaused() || m_currentCallStack.hasNoValue()) {
809         *errorString = "Attempt to access callframe when debugger is not on pause";
810         return;
811     }
812     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId);
813     if (injectedScript.hasNoValue()) {
814         *errorString = "Inspected frame has gone";
815         return;
816     }
817 
818     ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = scriptDebugServer().pauseOnExceptionsState();
819     if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) {
820         if (previousPauseOnExceptionsState != ScriptDebugServer::DontPauseOnExceptions)
821             scriptDebugServer().setPauseOnExceptionsState(ScriptDebugServer::DontPauseOnExceptions);
822         muteConsole();
823     }
824 
825     injectedScript.evaluateOnCallFrame(errorString, m_currentCallStack, callFrameId, expression, objectGroup ? *objectGroup : "", includeCommandLineAPI ? *includeCommandLineAPI : false, returnByValue ? *returnByValue : false, generatePreview ? *generatePreview : false, &result, wasThrown);
826 
827     if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) {
828         unmuteConsole();
829         if (scriptDebugServer().pauseOnExceptionsState() != previousPauseOnExceptionsState)
830             scriptDebugServer().setPauseOnExceptionsState(previousPauseOnExceptionsState);
831     }
832 }
833 
compileScript(ErrorString * errorString,const String & expression,const String & sourceURL,TypeBuilder::OptOutput<ScriptId> * scriptId,TypeBuilder::OptOutput<String> * syntaxErrorMessage)834 void InspectorDebuggerAgent::compileScript(ErrorString* errorString, const String& expression, const String& sourceURL, TypeBuilder::OptOutput<ScriptId>* scriptId, TypeBuilder::OptOutput<String>* syntaxErrorMessage)
835 {
836     InjectedScript injectedScript = injectedScriptForEval(errorString, 0);
837     if (injectedScript.hasNoValue()) {
838         *errorString = "Inspected frame has gone";
839         return;
840     }
841 
842     String scriptIdValue;
843     String exceptionMessage;
844     scriptDebugServer().compileScript(injectedScript.scriptState(), expression, sourceURL, &scriptIdValue, &exceptionMessage);
845     if (!scriptIdValue && !exceptionMessage) {
846         *errorString = "Script compilation failed";
847         return;
848     }
849     *syntaxErrorMessage = exceptionMessage;
850     *scriptId = scriptIdValue;
851 }
852 
runScript(ErrorString * errorString,const ScriptId & scriptId,const int * executionContextId,const String * const objectGroup,const bool * const doNotPauseOnExceptionsAndMuteConsole,RefPtr<RemoteObject> & result,TypeBuilder::OptOutput<bool> * wasThrown)853 void InspectorDebuggerAgent::runScript(ErrorString* errorString, const ScriptId& scriptId, const int* executionContextId, const String* const objectGroup, const bool* const doNotPauseOnExceptionsAndMuteConsole, RefPtr<RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown)
854 {
855     InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId);
856     if (injectedScript.hasNoValue()) {
857         *errorString = "Inspected frame has gone";
858         return;
859     }
860 
861     ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = scriptDebugServer().pauseOnExceptionsState();
862     if (doNotPauseOnExceptionsAndMuteConsole && *doNotPauseOnExceptionsAndMuteConsole) {
863         if (previousPauseOnExceptionsState != ScriptDebugServer::DontPauseOnExceptions)
864             scriptDebugServer().setPauseOnExceptionsState(ScriptDebugServer::DontPauseOnExceptions);
865         muteConsole();
866     }
867 
868     ScriptValue value;
869     bool wasThrownValue;
870     String exceptionMessage;
871     scriptDebugServer().runScript(injectedScript.scriptState(), scriptId, &value, &wasThrownValue, &exceptionMessage);
872     *wasThrown = wasThrownValue;
873     if (value.hasNoValue()) {
874         *errorString = "Script execution failed";
875         return;
876     }
877     result = injectedScript.wrapObject(value, objectGroup ? *objectGroup : "");
878     if (wasThrownValue)
879         result->setDescription(exceptionMessage);
880 
881     if (doNotPauseOnExceptionsAndMuteConsole && *doNotPauseOnExceptionsAndMuteConsole) {
882         unmuteConsole();
883         if (scriptDebugServer().pauseOnExceptionsState() != previousPauseOnExceptionsState)
884             scriptDebugServer().setPauseOnExceptionsState(previousPauseOnExceptionsState);
885     }
886 }
887 
setOverlayMessage(ErrorString *,const String *)888 void InspectorDebuggerAgent::setOverlayMessage(ErrorString*, const String*)
889 {
890 }
891 
setVariableValue(ErrorString * errorString,int scopeNumber,const String & variableName,const RefPtr<JSONObject> & newValue,const String * callFrameId,const String * functionObjectId)892 void InspectorDebuggerAgent::setVariableValue(ErrorString* errorString, int scopeNumber, const String& variableName, const RefPtr<JSONObject>& newValue, const String* callFrameId, const String* functionObjectId)
893 {
894     InjectedScript injectedScript;
895     if (callFrameId) {
896         if (!isPaused() || m_currentCallStack.hasNoValue()) {
897             *errorString = "Attempt to access callframe when debugger is not on pause";
898             return;
899         }
900         injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*callFrameId);
901         if (injectedScript.hasNoValue()) {
902             *errorString = "Inspected frame has gone";
903             return;
904         }
905     } else if (functionObjectId) {
906         injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*functionObjectId);
907         if (injectedScript.hasNoValue()) {
908             *errorString = "Function object id cannot be resolved";
909             return;
910         }
911     } else {
912         *errorString = "Either call frame or function object must be specified";
913         return;
914     }
915     String newValueString = newValue->toJSONString();
916 
917     injectedScript.setVariableValue(errorString, m_currentCallStack, callFrameId, functionObjectId, scopeNumber, variableName, newValueString);
918 }
919 
skipStackFrames(ErrorString * errorString,const String * pattern)920 void InspectorDebuggerAgent::skipStackFrames(ErrorString* errorString, const String* pattern)
921 {
922     OwnPtr<ScriptRegexp> compiled;
923     String patternValue = pattern ? *pattern : "";
924     if (!patternValue.isEmpty()) {
925         compiled = compileSkipCallFramePattern(patternValue);
926         if (!compiled) {
927             *errorString = "Invalid regular expression";
928             return;
929         }
930     }
931     m_state->setString(DebuggerAgentState::skipStackPattern, patternValue);
932     m_cachedSkipStackRegExp = compiled.release();
933 }
934 
setAsyncCallStackDepth(ErrorString *,int depth)935 void InspectorDebuggerAgent::setAsyncCallStackDepth(ErrorString*, int depth)
936 {
937     m_state->setLong(DebuggerAgentState::asyncCallStackDepth, depth);
938     m_asyncCallStackTracker.setAsyncCallStackDepth(depth);
939 }
940 
scriptExecutionBlockedByCSP(const String & directiveText)941 void InspectorDebuggerAgent::scriptExecutionBlockedByCSP(const String& directiveText)
942 {
943     if (scriptDebugServer().pauseOnExceptionsState() != ScriptDebugServer::DontPauseOnExceptions) {
944         RefPtr<JSONObject> directive = JSONObject::create();
945         directive->setString("directiveText", directiveText);
946         breakProgram(InspectorFrontend::Debugger::Reason::CSPViolation, directive.release());
947     }
948 }
949 
currentCallFrames()950 PassRefPtr<Array<CallFrame> > InspectorDebuggerAgent::currentCallFrames()
951 {
952     if (!m_pausedScriptState || m_currentCallStack.hasNoValue())
953         return Array<CallFrame>::create();
954     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(m_pausedScriptState);
955     if (injectedScript.hasNoValue()) {
956         ASSERT_NOT_REACHED();
957         return Array<CallFrame>::create();
958     }
959     return injectedScript.wrapCallFrames(m_currentCallStack);
960 }
961 
currentAsyncStackTrace()962 PassRefPtr<StackTrace> InspectorDebuggerAgent::currentAsyncStackTrace()
963 {
964     if (!m_pausedScriptState || !m_asyncCallStackTracker.isEnabled())
965         return 0;
966     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(m_pausedScriptState);
967     if (injectedScript.hasNoValue()) {
968         ASSERT_NOT_REACHED();
969         return 0;
970     }
971     const AsyncCallStackTracker::AsyncCallChain* chain = m_asyncCallStackTracker.currentAsyncCallChain();
972     if (!chain)
973         return 0;
974     const AsyncCallStackTracker::AsyncCallStackVector& callStacks = chain->callStacks();
975     if (callStacks.isEmpty())
976         return 0;
977     RefPtr<StackTrace> result;
978     for (AsyncCallStackTracker::AsyncCallStackVector::const_reverse_iterator it = callStacks.rbegin(); it != callStacks.rend(); ++it) {
979         RefPtr<StackTrace> next = StackTrace::create()
980             .setCallFrames(injectedScript.wrapCallFrames((*it)->callFrames()))
981             .release();
982         next->setDescription((*it)->description());
983         if (result)
984             next->setAsyncStackTrace(result.release());
985         result.swap(next);
986     }
987     return result.release();
988 }
989 
sourceMapURLForScript(const Script & script)990 String InspectorDebuggerAgent::sourceMapURLForScript(const Script& script)
991 {
992     bool deprecated;
993     String sourceMapURL = ContentSearchUtils::findSourceMapURL(script.source, ContentSearchUtils::JavaScriptMagicComment, &deprecated);
994     if (!sourceMapURL.isEmpty()) {
995         // FIXME: add deprecated console message here.
996         return sourceMapURL;
997     }
998 
999     if (script.url.isEmpty())
1000         return String();
1001 
1002     InspectorPageAgent* pageAgent = m_instrumentingAgents->inspectorPageAgent();
1003     if (!pageAgent)
1004         return String();
1005     return pageAgent->resourceSourceMapURL(script.url);
1006 }
1007 
1008 // JavaScriptDebugListener functions
1009 
didParseSource(const String & scriptId,const Script & script)1010 void InspectorDebuggerAgent::didParseSource(const String& scriptId, const Script& script)
1011 {
1012     // Don't send script content to the front end until it's really needed.
1013     const bool* isContentScript = script.isContentScript ? &script.isContentScript : 0;
1014     String sourceMapURL = sourceMapURLForScript(script);
1015     String* sourceMapURLParam = sourceMapURL.isNull() ? 0 : &sourceMapURL;
1016     String sourceURL;
1017     if (!script.startLine && !script.startColumn) {
1018         bool deprecated;
1019         sourceURL = ContentSearchUtils::findSourceURL(script.source, ContentSearchUtils::JavaScriptMagicComment, &deprecated);
1020         // FIXME: add deprecated console message here.
1021     }
1022     bool hasSourceURL = !sourceURL.isEmpty();
1023     String scriptURL = hasSourceURL ? sourceURL : script.url;
1024     bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : 0;
1025     m_frontend->scriptParsed(scriptId, scriptURL, script.startLine, script.startColumn, script.endLine, script.endColumn, isContentScript, sourceMapURLParam, hasSourceURLParam);
1026 
1027     m_scripts.set(scriptId, script);
1028 
1029     if (scriptURL.isEmpty())
1030         return;
1031 
1032     RefPtr<JSONObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
1033     for (JSONObject::iterator it = breakpointsCookie->begin(); it != breakpointsCookie->end(); ++it) {
1034         RefPtr<JSONObject> breakpointObject = it->value->asObject();
1035         bool isAntibreakpoint;
1036         breakpointObject->getBoolean(DebuggerAgentState::isAnti, &isAntibreakpoint);
1037         if (isAntibreakpoint)
1038             continue;
1039         bool isRegex;
1040         breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex);
1041         String url;
1042         breakpointObject->getString(DebuggerAgentState::url, &url);
1043         if (!matches(scriptURL, url, isRegex))
1044             continue;
1045         ScriptBreakpoint breakpoint;
1046         breakpointObject->getNumber(DebuggerAgentState::lineNumber, &breakpoint.lineNumber);
1047         breakpointObject->getNumber(DebuggerAgentState::columnNumber, &breakpoint.columnNumber);
1048         breakpointObject->getString(DebuggerAgentState::condition, &breakpoint.condition);
1049         RefPtr<TypeBuilder::Debugger::Location> location = resolveBreakpoint(it->key, scriptId, breakpoint, UserBreakpointSource);
1050         if (location)
1051             m_frontend->breakpointResolved(it->key, location);
1052     }
1053 }
1054 
failedToParseSource(const String & url,const String & data,int firstLine,int errorLine,const String & errorMessage)1055 void InspectorDebuggerAgent::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage)
1056 {
1057     m_frontend->scriptFailedToParse(url, data, firstLine, errorLine, errorMessage);
1058 }
1059 
didPause(ScriptState * scriptState,const ScriptValue & callFrames,const ScriptValue & exception,const Vector<String> & hitBreakpoints)1060 void InspectorDebuggerAgent::didPause(ScriptState* scriptState, const ScriptValue& callFrames, const ScriptValue& exception, const Vector<String>& hitBreakpoints)
1061 {
1062     ASSERT(scriptState && !m_pausedScriptState);
1063     m_pausedScriptState = scriptState;
1064     m_currentCallStack = callFrames;
1065 
1066     m_skipStepInCount = numberOfStepsBeforeStepOut;
1067 
1068     if (!exception.hasNoValue()) {
1069         InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
1070         if (!injectedScript.hasNoValue()) {
1071             m_breakReason = InspectorFrontend::Debugger::Reason::Exception;
1072             m_breakAuxData = injectedScript.wrapObject(exception, InspectorDebuggerAgent::backtraceObjectGroup)->openAccessors();
1073             // m_breakAuxData might be null after this.
1074         }
1075     }
1076 
1077     RefPtr<Array<String> > hitBreakpointIds = Array<String>::create();
1078 
1079     for (Vector<String>::const_iterator i = hitBreakpoints.begin(); i != hitBreakpoints.end(); ++i) {
1080         DebugServerBreakpointToBreakpointIdAndSourceMap::iterator breakpointIterator = m_serverBreakpoints.find(*i);
1081         if (breakpointIterator != m_serverBreakpoints.end()) {
1082             const String& localId = breakpointIterator->value.first;
1083             hitBreakpointIds->addItem(localId);
1084 
1085             BreakpointSource source = breakpointIterator->value.second;
1086             if (m_breakReason == InspectorFrontend::Debugger::Reason::Other && source == DebugCommandBreakpointSource)
1087                 m_breakReason = InspectorFrontend::Debugger::Reason::DebugCommand;
1088         }
1089     }
1090 
1091     m_frontend->paused(currentCallFrames(), m_breakReason, m_breakAuxData, hitBreakpointIds, currentAsyncStackTrace());
1092     m_javaScriptPauseScheduled = false;
1093 
1094     if (!m_continueToLocationBreakpointId.isEmpty()) {
1095         scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointId);
1096         m_continueToLocationBreakpointId = "";
1097     }
1098     if (m_listener)
1099         m_listener->didPause();
1100 }
1101 
didContinue()1102 void InspectorDebuggerAgent::didContinue()
1103 {
1104     m_pausedScriptState = 0;
1105     m_currentCallStack = ScriptValue();
1106     clearBreakDetails();
1107     m_frontend->resumed();
1108 }
1109 
canBreakProgram()1110 bool InspectorDebuggerAgent::canBreakProgram()
1111 {
1112     return scriptDebugServer().canBreakProgram();
1113 }
1114 
breakProgram(InspectorFrontend::Debugger::Reason::Enum breakReason,PassRefPtr<JSONObject> data)1115 void InspectorDebuggerAgent::breakProgram(InspectorFrontend::Debugger::Reason::Enum breakReason, PassRefPtr<JSONObject> data)
1116 {
1117     if (m_skipAllPauses)
1118         return;
1119     m_breakReason = breakReason;
1120     m_breakAuxData = data;
1121     scriptDebugServer().breakProgram();
1122 }
1123 
clear()1124 void InspectorDebuggerAgent::clear()
1125 {
1126     m_pausedScriptState = 0;
1127     m_currentCallStack = ScriptValue();
1128     m_scripts.clear();
1129     m_breakpointIdToDebugServerBreakpointIds.clear();
1130     m_asyncCallStackTracker.clear();
1131     m_continueToLocationBreakpointId = String();
1132     clearBreakDetails();
1133     m_javaScriptPauseScheduled = false;
1134     ErrorString error;
1135     setOverlayMessage(&error, 0);
1136 }
1137 
assertPaused(ErrorString * errorString)1138 bool InspectorDebuggerAgent::assertPaused(ErrorString* errorString)
1139 {
1140     if (!m_pausedScriptState) {
1141         *errorString = "Can only perform operation while paused.";
1142         return false;
1143     }
1144     return true;
1145 }
1146 
clearBreakDetails()1147 void InspectorDebuggerAgent::clearBreakDetails()
1148 {
1149     m_breakReason = InspectorFrontend::Debugger::Reason::Other;
1150     m_breakAuxData = 0;
1151 }
1152 
setBreakpoint(const String & scriptId,int lineNumber,int columnNumber,BreakpointSource source,const String & condition)1153 void InspectorDebuggerAgent::setBreakpoint(const String& scriptId, int lineNumber, int columnNumber, BreakpointSource source, const String& condition)
1154 {
1155     String breakpointId = generateBreakpointId(scriptId, lineNumber, columnNumber, source);
1156     ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
1157     resolveBreakpoint(breakpointId, scriptId, breakpoint, source);
1158 }
1159 
removeBreakpoint(const String & scriptId,int lineNumber,int columnNumber,BreakpointSource source)1160 void InspectorDebuggerAgent::removeBreakpoint(const String& scriptId, int lineNumber, int columnNumber, BreakpointSource source)
1161 {
1162     removeBreakpoint(generateBreakpointId(scriptId, lineNumber, columnNumber, source));
1163 }
1164 
reset()1165 void InspectorDebuggerAgent::reset()
1166 {
1167     m_scripts.clear();
1168     m_breakpointIdToDebugServerBreakpointIds.clear();
1169     m_asyncCallStackTracker.clear();
1170     if (m_frontend)
1171         m_frontend->globalObjectCleared();
1172 }
1173 
1174 } // namespace WebCore
1175 
1176