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