• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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