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