2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
31 #include "InspectorDebuggerAgent.h"
33 #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)
34 #include "InjectedScript.h"
35 #include "InjectedScriptHost.h"
36 #include "InspectorFrontend.h"
37 #include "InspectorState.h"
38 #include "InspectorValues.h"
39 #include "PlatformString.h"
40 #include "ScriptDebugServer.h"
41 #include <wtf/text/StringConcatenate.h>
45 namespace DebuggerAgentState {
46 static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
49 PassOwnPtr<InspectorDebuggerAgent> InspectorDebuggerAgent::create(InspectorAgent* inspectorAgent, InspectorFrontend* frontend, bool eraseStickyBreakpoints)
51 OwnPtr<InspectorDebuggerAgent> agent = adoptPtr(new InspectorDebuggerAgent(inspectorAgent, frontend, eraseStickyBreakpoints));
52 ScriptDebugServer::shared().clearBreakpoints();
53 // FIXME(WK44513): breakpoints activated flag should be synchronized between all front-ends
54 ScriptDebugServer::shared().setBreakpointsActivated(true);
55 ScriptDebugServer::shared().addListener(agent.get(), inspectorAgent->inspectedPage());
56 return agent.release();
59 InspectorDebuggerAgent::InspectorDebuggerAgent(InspectorAgent* inspectorAgent, InspectorFrontend* frontend, bool eraseStickyBreakpoints)
60 : m_inspectorAgent(inspectorAgent)
61 , m_frontend(frontend)
62 , m_pausedScriptState(0)
63 , m_javaScriptPauseScheduled(false)
65 if (eraseStickyBreakpoints)
66 inspectorAgent->state()->setObject(DebuggerAgentState::javaScriptBreakpoints, InspectorObject::create());
69 InspectorDebuggerAgent::~InspectorDebuggerAgent()
71 ScriptDebugServer::shared().removeListener(this, m_inspectorAgent->inspectedPage());
72 m_pausedScriptState = 0;
75 void InspectorDebuggerAgent::activateBreakpoints()
77 ScriptDebugServer::shared().activateBreakpoints();
80 void InspectorDebuggerAgent::deactivateBreakpoints()
82 ScriptDebugServer::shared().deactivateBreakpoints();
85 void InspectorDebuggerAgent::inspectedURLChanged(const String&)
88 m_breakpointIdToDebugServerBreakpointIds.clear();
91 void InspectorDebuggerAgent::setJavaScriptBreakpoint(const String& url, int lineNumber, int columnNumber, const String& condition, bool enabled, String* outBreakpointId, RefPtr<InspectorArray>* locations)
93 String breakpointId = makeString(url, ":", String::number(lineNumber), ":", String::number(columnNumber));
94 RefPtr<InspectorObject> breakpointsCookie = m_inspectorAgent->state()->getObject(DebuggerAgentState::javaScriptBreakpoints);
95 if (breakpointsCookie->find(breakpointId) != breakpointsCookie->end())
97 RefPtr<InspectorObject> breakpointObject = InspectorObject::create();
98 breakpointObject->setString("url", url);
99 breakpointObject->setNumber("lineNumber", lineNumber);
100 breakpointObject->setNumber("columnNumber", columnNumber);
101 breakpointObject->setString("condition", condition);
102 breakpointObject->setBoolean("enabled", enabled);
103 breakpointsCookie->setObject(breakpointId, breakpointObject);
104 m_inspectorAgent->state()->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCookie);
106 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition, enabled);
107 for (ScriptsMap::iterator it = m_scripts.begin(); it != m_scripts.end(); ++it) {
108 if (it->second.url != url)
110 int actualLineNumber = 0, actualColumnNumber = 0;
111 if (!resolveBreakpoint(breakpointId, it->first, breakpoint, &actualLineNumber, &actualColumnNumber))
113 RefPtr<InspectorObject> location = InspectorObject::create();
114 location->setString("sourceID", it->first);
115 location->setNumber("lineNumber", actualLineNumber);
116 location->setNumber("columnNumber", actualColumnNumber);
117 locations->get()->pushObject(location);
119 *outBreakpointId = breakpointId;
122 void InspectorDebuggerAgent::setJavaScriptBreakpointBySourceId(const String& sourceId, int lineNumber, int columnNumber, const String& condition, bool enabled, String* outBreakpointId, int* actualLineNumber, int* actualColumnNumber)
124 String breakpointId = makeString(sourceId, ":", String::number(lineNumber), ":", String::number(columnNumber));
125 if (m_breakpointIdToDebugServerBreakpointIds.find(breakpointId) != m_breakpointIdToDebugServerBreakpointIds.end())
127 ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition, enabled);
128 if (!resolveBreakpoint(breakpointId, sourceId, breakpoint, actualLineNumber, actualColumnNumber))
130 *outBreakpointId = breakpointId;
133 void InspectorDebuggerAgent::removeJavaScriptBreakpoint(const String& breakpointId)
135 RefPtr<InspectorObject> breakpointsCookie = m_inspectorAgent->state()->getObject(DebuggerAgentState::javaScriptBreakpoints);
136 breakpointsCookie->remove(breakpointId);
137 m_inspectorAgent->state()->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCookie);
139 BreakpointIdToDebugServerBreakpointIdsMap::iterator debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.find(breakpointId);
140 if (debugServerBreakpointIdsIterator == m_breakpointIdToDebugServerBreakpointIds.end())
142 for (size_t i = 0; i < debugServerBreakpointIdsIterator->second.size(); ++i)
143 ScriptDebugServer::shared().removeBreakpoint(debugServerBreakpointIdsIterator->second[i]);
144 m_breakpointIdToDebugServerBreakpointIds.remove(debugServerBreakpointIdsIterator);
147 void InspectorDebuggerAgent::continueToLocation(const String& sourceId, int lineNumber, int columnNumber)
149 if (!m_continueToLocationBreakpointId.isEmpty()) {
150 ScriptDebugServer::shared().removeBreakpoint(m_continueToLocationBreakpointId);
151 m_continueToLocationBreakpointId = "";
153 ScriptBreakpoint breakpoint(lineNumber, columnNumber, "", true);
154 m_continueToLocationBreakpointId = ScriptDebugServer::shared().setBreakpoint(sourceId, breakpoint, &lineNumber, &columnNumber);
158 bool InspectorDebuggerAgent::resolveBreakpoint(const String& breakpointId, const String& sourceId, const ScriptBreakpoint& breakpoint, int* actualLineNumber, int* actualColumnNumber)
160 ScriptsMap::iterator scriptIterator = m_scripts.find(sourceId);
161 if (scriptIterator == m_scripts.end())
163 Script& script = scriptIterator->second;
164 if (breakpoint.lineNumber < script.lineOffset)
166 if (!script.linesCount) {
167 script.linesCount = 1;
168 for (size_t i = 0; i < script.data.length(); ++i) {
169 if (script.data[i] == '\n')
170 script.linesCount += 1;
173 if (breakpoint.lineNumber >= script.lineOffset + script.linesCount)
176 String debugServerBreakpointId = ScriptDebugServer::shared().setBreakpoint(sourceId, breakpoint, actualLineNumber, actualColumnNumber);
177 if (debugServerBreakpointId.isEmpty())
180 BreakpointIdToDebugServerBreakpointIdsMap::iterator debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.find(breakpointId);
181 if (debugServerBreakpointIdsIterator == m_breakpointIdToDebugServerBreakpointIds.end())
182 debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.set(breakpointId, Vector<String>()).first;
183 debugServerBreakpointIdsIterator->second.append(debugServerBreakpointId);
188 void InspectorDebuggerAgent::editScriptSource(const String& sourceID, const String& newContent, bool* success, String* result, RefPtr<InspectorValue>* newCallFrames)
190 if ((*success = ScriptDebugServer::shared().editScriptSource(sourceID, newContent, *result)))
191 *newCallFrames = currentCallFrames();
194 void InspectorDebuggerAgent::getScriptSource(const String& sourceID, String* scriptSource)
196 *scriptSource = m_scripts.get(sourceID).data;
199 void InspectorDebuggerAgent::schedulePauseOnNextStatement(DebuggerEventType type, PassRefPtr<InspectorValue> data)
201 if (m_javaScriptPauseScheduled)
203 m_breakProgramDetails = InspectorObject::create();
204 m_breakProgramDetails->setNumber("eventType", type);
205 m_breakProgramDetails->setValue("eventData", data);
206 ScriptDebugServer::shared().setPauseOnNextStatement(true);
209 void InspectorDebuggerAgent::cancelPauseOnNextStatement()
211 if (m_javaScriptPauseScheduled)
213 m_breakProgramDetails = 0;
214 ScriptDebugServer::shared().setPauseOnNextStatement(false);
217 void InspectorDebuggerAgent::pause()
219 schedulePauseOnNextStatement(JavaScriptPauseEventType, InspectorObject::create());
220 m_javaScriptPauseScheduled = true;
223 void InspectorDebuggerAgent::resume()
225 ScriptDebugServer::shared().continueProgram();
228 void InspectorDebuggerAgent::stepOver()
230 ScriptDebugServer::shared().stepOverStatement();
233 void InspectorDebuggerAgent::stepInto()
235 ScriptDebugServer::shared().stepIntoStatement();
238 void InspectorDebuggerAgent::stepOut()
240 ScriptDebugServer::shared().stepOutOfFunction();
243 void InspectorDebuggerAgent::setPauseOnExceptionsState(long pauseState, long* newState)
245 ScriptDebugServer::shared().setPauseOnExceptionsState(static_cast<ScriptDebugServer::PauseOnExceptionsState>(pauseState));
246 *newState = ScriptDebugServer::shared().pauseOnExceptionsState();
249 void InspectorDebuggerAgent::evaluateOnCallFrame(PassRefPtr<InspectorObject> callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, RefPtr<InspectorValue>* result)
251 InjectedScript injectedScript = m_inspectorAgent->injectedScriptHost()->injectedScriptForObjectId(callFrameId.get());
252 if (!injectedScript.hasNoValue())
253 injectedScript.evaluateOnCallFrame(callFrameId, expression, objectGroup, includeCommandLineAPI, result);
256 void InspectorDebuggerAgent::getCompletionsOnCallFrame(PassRefPtr<InspectorObject> callFrameId, const String& expression, bool includeCommandLineAPI, RefPtr<InspectorValue>* result)
258 InjectedScript injectedScript = m_inspectorAgent->injectedScriptHost()->injectedScriptForObjectId(callFrameId.get());
259 if (!injectedScript.hasNoValue())
260 injectedScript.getCompletionsOnCallFrame(callFrameId, expression, includeCommandLineAPI, result);
263 PassRefPtr<InspectorValue> InspectorDebuggerAgent::currentCallFrames()
265 if (!m_pausedScriptState)
266 return InspectorValue::null();
267 InjectedScript injectedScript = m_inspectorAgent->injectedScriptHost()->injectedScriptFor(m_pausedScriptState);
268 if (injectedScript.hasNoValue()) {
269 ASSERT_NOT_REACHED();
270 return InspectorValue::null();
272 return injectedScript.callFrames();
275 // JavaScriptDebugListener functions
277 void InspectorDebuggerAgent::didParseSource(const String& sourceID, const String& url, const String& data, int lineOffset, int columnOffset, ScriptWorldType worldType)
279 // Don't send script content to the front end until it's really needed.
280 m_frontend->parsedScriptSource(sourceID, url, lineOffset, columnOffset, data.length(), worldType);
282 m_scripts.set(sourceID, Script(url, data, lineOffset, columnOffset));
287 RefPtr<InspectorObject> breakpointsCookie = m_inspectorAgent->state()->getObject(DebuggerAgentState::javaScriptBreakpoints);
288 for (InspectorObject::iterator it = breakpointsCookie->begin(); it != breakpointsCookie->end(); ++it) {
289 RefPtr<InspectorObject> breakpointObject = it->second->asObject();
290 String breakpointURL;
291 breakpointObject->getString("url", &breakpointURL);
292 if (breakpointURL != url)
294 ScriptBreakpoint breakpoint;
295 breakpointObject->getNumber("lineNumber", &breakpoint.lineNumber);
296 breakpointObject->getNumber("columnNumber", &breakpoint.columnNumber);
297 breakpointObject->getString("condition", &breakpoint.condition);
298 breakpointObject->getBoolean("enabled", &breakpoint.enabled);
299 int actualLineNumber = 0, actualColumnNumber = 0;
300 if (resolveBreakpoint(it->first, sourceID, breakpoint, &actualLineNumber, &actualColumnNumber))
301 m_frontend->breakpointResolved(it->first, sourceID, actualLineNumber, actualColumnNumber);
305 void InspectorDebuggerAgent::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage)
307 m_frontend->failedToParseScriptSource(url, data, firstLine, errorLine, errorMessage);
310 void InspectorDebuggerAgent::didPause(ScriptState* scriptState)
312 ASSERT(scriptState && !m_pausedScriptState);
313 m_pausedScriptState = scriptState;
315 if (!m_breakProgramDetails)
316 m_breakProgramDetails = InspectorObject::create();
317 m_breakProgramDetails->setValue("callFrames", currentCallFrames());
319 m_frontend->pausedScript(m_breakProgramDetails);
320 m_javaScriptPauseScheduled = false;
322 if (!m_continueToLocationBreakpointId.isEmpty()) {
323 ScriptDebugServer::shared().removeBreakpoint(m_continueToLocationBreakpointId);
324 m_continueToLocationBreakpointId = "";
328 void InspectorDebuggerAgent::didContinue()
330 m_pausedScriptState = 0;
331 m_breakProgramDetails = 0;
332 m_frontend->resumedScript();
335 void InspectorDebuggerAgent::breakProgram(DebuggerEventType type, PassRefPtr<InspectorValue> data)
337 m_breakProgramDetails = InspectorObject::create();
338 m_breakProgramDetails->setNumber("eventType", type);
339 m_breakProgramDetails->setValue("eventData", data);
340 ScriptDebugServer::shared().breakProgram();
343 } // namespace WebCore
345 #endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)