3c629816ac51a24c8e4b32026fce05da40f4c9e7
[WebKit-https.git] / Source / WebCore / inspector / InspectorDebuggerAgent.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2010 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 "InspectorDebuggerAgent.h"
32
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
42 namespace WebCore {
43
44 PassOwnPtr<InspectorDebuggerAgent> InspectorDebuggerAgent::create(InspectorAgent* inspectorAgent, InspectorFrontend* frontend)
45 {
46     OwnPtr<InspectorDebuggerAgent> agent = adoptPtr(new InspectorDebuggerAgent(inspectorAgent, frontend));
47     ScriptDebugServer::shared().clearBreakpoints();
48     // FIXME(WK44513): breakpoints activated flag should be synchronized between all front-ends
49     ScriptDebugServer::shared().setBreakpointsActivated(true);
50     ScriptDebugServer::shared().addListener(agent.get(), inspectorAgent->inspectedPage());
51     return agent.release();
52 }
53
54 InspectorDebuggerAgent::InspectorDebuggerAgent(InspectorAgent* inspectorAgent, InspectorFrontend* frontend)
55     : m_inspectorAgent(inspectorAgent)
56     , m_frontend(frontend)
57     , m_pausedScriptState(0)
58     , m_javaScriptPauseScheduled(false)
59     , m_breakpointsRestored(false)
60 {
61 }
62
63 InspectorDebuggerAgent::~InspectorDebuggerAgent()
64 {
65     ScriptDebugServer::shared().removeListener(this, m_inspectorAgent->inspectedPage());
66     m_pausedScriptState = 0;
67 }
68
69 void InspectorDebuggerAgent::activateBreakpoints()
70 {
71     ScriptDebugServer::shared().activateBreakpoints();
72 }
73
74 void InspectorDebuggerAgent::deactivateBreakpoints()
75 {
76     ScriptDebugServer::shared().deactivateBreakpoints();
77 }
78
79 void InspectorDebuggerAgent::setAllJavaScriptBreakpoints(PassRefPtr<InspectorObject> breakpoints)
80 {
81     m_inspectorAgent->state()->setObject(InspectorState::javaScriptBreakpoints, breakpoints);
82     if (!m_breakpointsRestored) {
83         restoreBreakpoints(m_inspectorAgent->inspectedURLWithoutFragment());
84         m_breakpointsRestored = true;
85     }
86 }
87
88 void InspectorDebuggerAgent::inspectedURLChanged(const String& url)
89 {
90     m_scriptIDToContent.clear();
91     m_urlToSourceIDs.clear();
92     restoreBreakpoints(url);
93 }
94
95 void InspectorDebuggerAgent::restoreBreakpoints(const String& inspectedURL)
96 {
97     m_stickyBreakpoints.clear();
98
99     RefPtr<InspectorObject> allBreakpoints = m_inspectorAgent->state()->getObject(InspectorState::javaScriptBreakpoints);
100     RefPtr<InspectorArray> breakpoints = allBreakpoints->getArray(inspectedURL);
101     if (!breakpoints)
102         return;
103     for (unsigned i = 0; i < breakpoints->length(); ++i) {
104         RefPtr<InspectorObject> breakpoint = breakpoints->get(i)->asObject();
105         if (!breakpoint)
106             continue;
107         String url;
108         if (!breakpoint->getString("url", &url))
109             continue;
110         double lineNumber;
111         if (!breakpoint->getNumber("lineNumber", &lineNumber))
112             continue;
113         double columnNumber;
114         if (!breakpoint->getNumber("columnNumber", &columnNumber))
115             return;
116         String condition;
117         if (!breakpoint->getString("condition", &condition))
118             continue;
119         bool enabled;
120         if (!breakpoint->getBoolean("enabled", &enabled))
121             continue;
122         ScriptBreakpoint scriptBreakpoint((long) lineNumber, (long) columnNumber, condition, enabled);
123         setStickyBreakpoint(url, scriptBreakpoint);
124     }
125 }
126
127 void InspectorDebuggerAgent::setStickyBreakpoint(const String& url, const ScriptBreakpoint& breakpoint)
128 {
129     InspectedURLToBreakpointsMap::iterator it = m_stickyBreakpoints.find(url);
130     if (it == m_stickyBreakpoints.end())
131         it = m_stickyBreakpoints.set(url, LocationToBreakpointMap()).first;
132     it->second.set(Location(breakpoint.lineNumber, breakpoint.columnNumber), breakpoint);
133
134     URLToSourceIDsMap::iterator urlToSourceIDsIterator = m_urlToSourceIDs.find(url);
135     if (urlToSourceIDsIterator == m_urlToSourceIDs.end())
136         return;
137     const Vector<String>& sourceIDs = urlToSourceIDsIterator->second;
138     for (size_t i = 0; i < sourceIDs.size(); ++i)
139         restoreBreakpoint(sourceIDs[i], breakpoint);
140 }
141
142 void InspectorDebuggerAgent::setBreakpoint(PassRefPtr<InspectorObject> breakpoint, String* breakpointId, long* actualLineNumber, long* actualColumnNumber)
143 {
144     String sourceID;
145     if (!breakpoint->getString("sourceID", &sourceID))
146         return;
147     double lineNumber;
148     if (!breakpoint->getNumber("lineNumber", &lineNumber))
149         return;
150     double columnNumber;
151     if (!breakpoint->getNumber("columnNumber", &columnNumber))
152         return;
153     String condition;
154     if (!breakpoint->getString("condition", &condition))
155         return;
156     bool enabled;
157     if (!breakpoint->getBoolean("enabled", &enabled))
158         return;
159     ScriptBreakpoint scriptBreakpoint((long) lineNumber, (long) columnNumber, condition, enabled);
160     *breakpointId = ScriptDebugServer::shared().setBreakpoint(sourceID, scriptBreakpoint, actualLineNumber, actualColumnNumber);
161 }
162
163 void InspectorDebuggerAgent::removeBreakpoint(const String& breakpointId)
164 {
165     ScriptDebugServer::shared().removeBreakpoint(breakpointId);
166 }
167
168 void InspectorDebuggerAgent::restoreBreakpoint(const String& sourceID, const ScriptBreakpoint& breakpoint)
169 {
170     long actualLineNumber = 0, actualColumnNumber = 0;
171     String breakpointId = ScriptDebugServer::shared().setBreakpoint(sourceID, breakpoint, &actualLineNumber, &actualColumnNumber);
172     if (breakpointId.isEmpty())
173         return;
174     RefPtr<InspectorObject> breakpointData = InspectorObject::create();
175     breakpointData->setString("id", breakpointId);
176     breakpointData->setString("sourceID", sourceID);
177     breakpointData->setNumber("lineNumber", actualLineNumber);
178     breakpointData->setNumber("columnNumber", actualColumnNumber);
179     breakpointData->setString("condition", breakpoint.condition);
180     breakpointData->setBoolean("enabled", breakpoint.enabled);
181     breakpointData->setNumber("originalLineNumber", breakpoint.lineNumber);
182     breakpointData->setNumber("originalColumnNumber", breakpoint.columnNumber);
183     m_frontend->breakpointResolved(breakpointId, breakpointData);
184 }
185
186 void InspectorDebuggerAgent::editScriptSource(const String& sourceID, const String& newContent, bool* success, String* result, RefPtr<InspectorValue>* newCallFrames)
187 {
188     if ((*success = ScriptDebugServer::shared().editScriptSource(sourceID, newContent, *result)))
189         *newCallFrames = currentCallFrames();
190 }
191
192 void InspectorDebuggerAgent::getScriptSource(const String& sourceID, String* scriptSource)
193 {
194     *scriptSource = m_scriptIDToContent.get(sourceID);
195 }
196
197 void InspectorDebuggerAgent::schedulePauseOnNextStatement(DebuggerEventType type, PassRefPtr<InspectorValue> data)
198 {
199     if (m_javaScriptPauseScheduled)
200         return;
201     m_breakProgramDetails = InspectorObject::create();
202     m_breakProgramDetails->setNumber("eventType", type);
203     m_breakProgramDetails->setValue("eventData", data);
204     ScriptDebugServer::shared().setPauseOnNextStatement(true);
205 }
206
207 void InspectorDebuggerAgent::cancelPauseOnNextStatement()
208 {
209     if (m_javaScriptPauseScheduled)
210         return;
211     m_breakProgramDetails = 0;
212     ScriptDebugServer::shared().setPauseOnNextStatement(false);
213 }
214
215 void InspectorDebuggerAgent::pause()
216 {
217     schedulePauseOnNextStatement(JavaScriptPauseEventType, InspectorObject::create());
218     m_javaScriptPauseScheduled = true;
219 }
220
221 void InspectorDebuggerAgent::resume()
222 {
223     ScriptDebugServer::shared().continueProgram();
224 }
225
226 void InspectorDebuggerAgent::stepOver()
227 {
228     ScriptDebugServer::shared().stepOverStatement();
229 }
230
231 void InspectorDebuggerAgent::stepInto()
232 {
233     ScriptDebugServer::shared().stepIntoStatement();
234 }
235
236 void InspectorDebuggerAgent::stepOut()
237 {
238     ScriptDebugServer::shared().stepOutOfFunction();
239 }
240
241 void InspectorDebuggerAgent::setPauseOnExceptionsState(long pauseState, long* newState)
242 {
243     ScriptDebugServer::shared().setPauseOnExceptionsState(static_cast<ScriptDebugServer::PauseOnExceptionsState>(pauseState));
244     *newState = ScriptDebugServer::shared().pauseOnExceptionsState();
245 }
246
247 void InspectorDebuggerAgent::evaluateOnCallFrame(PassRefPtr<InspectorObject> callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, RefPtr<InspectorValue>* result)
248 {
249     InjectedScript injectedScript = m_inspectorAgent->injectedScriptHost()->injectedScriptForObjectId(callFrameId.get());
250     if (!injectedScript.hasNoValue())
251         injectedScript.evaluateOnCallFrame(callFrameId, expression, objectGroup, includeCommandLineAPI, result);
252 }
253
254 void InspectorDebuggerAgent::getCompletionsOnCallFrame(PassRefPtr<InspectorObject> callFrameId, const String& expression, bool includeCommandLineAPI, RefPtr<InspectorValue>* result)
255 {
256     InjectedScript injectedScript = m_inspectorAgent->injectedScriptHost()->injectedScriptForObjectId(callFrameId.get());
257     if (!injectedScript.hasNoValue())
258         injectedScript.getCompletionsOnCallFrame(callFrameId, expression, includeCommandLineAPI, result);
259 }
260
261 PassRefPtr<InspectorValue> InspectorDebuggerAgent::currentCallFrames()
262 {
263     if (!m_pausedScriptState)
264         return InspectorValue::null();
265     InjectedScript injectedScript = m_inspectorAgent->injectedScriptHost()->injectedScriptFor(m_pausedScriptState);
266     if (injectedScript.hasNoValue()) {
267         ASSERT_NOT_REACHED();
268         return InspectorValue::null();
269     }
270     return injectedScript.callFrames();
271 }
272
273 // JavaScriptDebugListener functions
274
275 void InspectorDebuggerAgent::didParseSource(const String& sourceID, const String& url, const String& data, int lineOffset, int columnOffset, ScriptWorldType worldType)
276 {
277     // Don't send script content to the front end until it's really needed.
278     m_frontend->parsedScriptSource(sourceID, url, lineOffset, columnOffset, data.length(), worldType);
279
280     m_scriptIDToContent.set(sourceID, data);
281
282     if (url.isEmpty())
283         return;
284
285     URLToSourceIDsMap::iterator urlToSourceIDsIterator = m_urlToSourceIDs.find(url);
286     if (urlToSourceIDsIterator == m_urlToSourceIDs.end())
287         urlToSourceIDsIterator = m_urlToSourceIDs.set(url, Vector<String>()).first;
288     urlToSourceIDsIterator->second.append(sourceID);
289
290     InspectedURLToBreakpointsMap::iterator stickyBreakpointsIterator = m_stickyBreakpoints.find(url);
291     if (stickyBreakpointsIterator == m_stickyBreakpoints.end())
292         return;
293
294     const LocationToBreakpointMap& breakpoints = stickyBreakpointsIterator->second;
295     for (LocationToBreakpointMap::const_iterator it = breakpoints.begin(); it != breakpoints.end(); ++it)
296         restoreBreakpoint(sourceID, it->second);
297 }
298
299 void InspectorDebuggerAgent::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage)
300 {
301     m_frontend->failedToParseScriptSource(url, data, firstLine, errorLine, errorMessage);
302 }
303
304 void InspectorDebuggerAgent::didPause(ScriptState* scriptState)
305 {
306     ASSERT(scriptState && !m_pausedScriptState);
307     m_pausedScriptState = scriptState;
308
309     if (!m_breakProgramDetails)
310         m_breakProgramDetails = InspectorObject::create();
311     m_breakProgramDetails->setValue("callFrames", currentCallFrames());
312
313     m_frontend->pausedScript(m_breakProgramDetails);
314     m_javaScriptPauseScheduled = false;
315 }
316
317 void InspectorDebuggerAgent::didContinue()
318 {
319     m_pausedScriptState = 0;
320     m_breakProgramDetails = 0;
321     m_frontend->resumedScript();
322 }
323
324 void InspectorDebuggerAgent::breakProgram(DebuggerEventType type, PassRefPtr<InspectorValue> data)
325 {
326     m_breakProgramDetails = InspectorObject::create();
327     m_breakProgramDetails->setNumber("eventType", type);
328     m_breakProgramDetails->setValue("eventData", data);
329     ScriptDebugServer::shared().breakProgram();
330 }
331
332 } // namespace WebCore
333
334 #endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)