0046989bb5d275615147ccd9a39c1dbb46f49075
[WebKit-https.git] / 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 "InspectorValues.h"
38 #include "PlatformString.h"
39 #include "ScriptDebugServer.h"
40
41 namespace WebCore {
42
43 PassOwnPtr<InspectorDebuggerAgent> InspectorDebuggerAgent::create(InspectorController* inspectorController, InspectorFrontend* frontend)
44 {
45     OwnPtr<InspectorDebuggerAgent> agent = adoptPtr(new InspectorDebuggerAgent(inspectorController, frontend));
46     ScriptDebugServer::shared().clearBreakpoints();
47     // FIXME(WK44513): breakpoints activated flag should be synchronized between all front-ends
48     ScriptDebugServer::shared().setBreakpointsActivated(true);
49     ScriptDebugServer::shared().addListener(agent.get(), inspectorController->inspectedPage());
50     return agent.release();
51 }
52
53 InspectorDebuggerAgent::InspectorDebuggerAgent(InspectorController* inspectorController, InspectorFrontend* frontend)
54     : m_inspectorController(inspectorController)
55     , m_frontend(frontend)
56     , m_pausedScriptState(0)
57     , m_javaScriptPauseScheduled(false)
58 {
59 }
60
61 InspectorDebuggerAgent::~InspectorDebuggerAgent()
62 {
63     ScriptDebugServer::shared().removeListener(this, m_inspectorController->inspectedPage());
64     m_pausedScriptState = 0;
65 }
66
67 bool InspectorDebuggerAgent::isDebuggerAlwaysEnabled()
68 {
69     return ScriptDebugServer::shared().isDebuggerAlwaysEnabled();
70 }
71
72 void InspectorDebuggerAgent::activateBreakpoints()
73 {
74     ScriptDebugServer::shared().activateBreakpoints();
75 }
76
77 void InspectorDebuggerAgent::deactivateBreakpoints()
78 {
79     ScriptDebugServer::shared().deactivateBreakpoints();
80 }
81
82 void InspectorDebuggerAgent::setStickyBreakpoint(const String& url, unsigned lineNumber, const String& condition, bool enabled)
83 {
84     HashMap<String, ScriptBreakpoints>::iterator it = m_stickyBreakpoints.find(url);
85     if (it == m_stickyBreakpoints.end())
86         it = m_stickyBreakpoints.set(url, ScriptBreakpoints()).first;
87     it->second.set(lineNumber, Breakpoint(condition, enabled));
88
89     URLToSourceIDsMap::iterator urlToSourceIDsIterator = m_urlToSourceIDs.find(url);
90     if (urlToSourceIDsIterator == m_urlToSourceIDs.end())
91         return;
92     const Vector<String>& sourceIDs = urlToSourceIDsIterator->second;
93     for (size_t i = 0; i < sourceIDs.size(); ++i)
94         restoreBreakpoint(sourceIDs[i], lineNumber, condition, enabled);
95 }
96
97 void InspectorDebuggerAgent::setBreakpoint(const String& sourceID, unsigned lineNumber, const String& condition, bool enabled, String* breakpointId, unsigned int* actualLineNumber)
98 {
99     *breakpointId = ScriptDebugServer::shared().setBreakpoint(sourceID, lineNumber, condition, enabled, actualLineNumber);
100 }
101
102 void InspectorDebuggerAgent::removeBreakpoint(const String& breakpointId)
103 {
104     ScriptDebugServer::shared().removeBreakpoint(breakpointId);
105 }
106
107 void InspectorDebuggerAgent::restoreBreakpoint(const String& sourceID, unsigned lineNumber, const String& condition, bool enabled)
108 {
109     unsigned actualLineNumber = 0;
110     String breakpointId = ScriptDebugServer::shared().setBreakpoint(sourceID, lineNumber, condition, enabled, &actualLineNumber);
111     if (!breakpointId.isEmpty())
112         m_frontend->breakpointResolved(breakpointId, sourceID, actualLineNumber, condition, enabled, lineNumber);
113 }
114
115 void InspectorDebuggerAgent::editScriptSource(const String& sourceID, const String& newContent, bool* success, String* result, RefPtr<InspectorValue>* newCallFrames)
116 {
117     if ((*success = ScriptDebugServer::shared().editScriptSource(sourceID, newContent, *result)))
118         *newCallFrames = currentCallFrames();
119 }
120
121 void InspectorDebuggerAgent::getScriptSource(const String& sourceID, String* scriptSource)
122 {
123     *scriptSource = m_scriptIDToContent.get(sourceID);
124 }
125
126 void InspectorDebuggerAgent::schedulePauseOnNextStatement(DebuggerEventType type, PassRefPtr<InspectorValue> data)
127 {
128     if (m_javaScriptPauseScheduled)
129         return;
130     m_breakProgramDetails = InspectorObject::create();
131     m_breakProgramDetails->setNumber("eventType", type);
132     m_breakProgramDetails->setValue("eventData", data);
133     ScriptDebugServer::shared().setPauseOnNextStatement(true);
134 }
135
136 void InspectorDebuggerAgent::cancelPauseOnNextStatement()
137 {
138     if (m_javaScriptPauseScheduled)
139         return;
140     m_breakProgramDetails = 0;
141     ScriptDebugServer::shared().setPauseOnNextStatement(false);
142 }
143
144 void InspectorDebuggerAgent::pause()
145 {
146     schedulePauseOnNextStatement(JavaScriptPauseEventType, InspectorObject::create());
147     m_javaScriptPauseScheduled = true;
148 }
149
150 void InspectorDebuggerAgent::resume()
151 {
152     ScriptDebugServer::shared().continueProgram();
153 }
154
155 void InspectorDebuggerAgent::stepOverStatement()
156 {
157     ScriptDebugServer::shared().stepOverStatement();
158 }
159
160 void InspectorDebuggerAgent::stepIntoStatement()
161 {
162     ScriptDebugServer::shared().stepIntoStatement();
163 }
164
165 void InspectorDebuggerAgent::stepOutOfFunction()
166 {
167     ScriptDebugServer::shared().stepOutOfFunction();
168 }
169
170 void InspectorDebuggerAgent::setPauseOnExceptionsState(long pauseState, long* newState)
171 {
172     ScriptDebugServer::shared().setPauseOnExceptionsState(static_cast<ScriptDebugServer::PauseOnExceptionsState>(pauseState));
173     *newState = ScriptDebugServer::shared().pauseOnExceptionsState();
174 }
175
176 long InspectorDebuggerAgent::pauseOnExceptionsState()
177 {
178     return ScriptDebugServer::shared().pauseOnExceptionsState();
179 }
180
181 void InspectorDebuggerAgent::clearForPageNavigation()
182 {
183     m_scriptIDToContent.clear();
184     m_urlToSourceIDs.clear();
185     m_stickyBreakpoints.clear();
186 }
187
188 PassRefPtr<InspectorValue> InspectorDebuggerAgent::currentCallFrames()
189 {
190     if (!m_pausedScriptState)
191         return InspectorValue::null();
192     InjectedScript injectedScript = m_inspectorController->injectedScriptHost()->injectedScriptFor(m_pausedScriptState);
193     if (injectedScript.hasNoValue()) {
194         ASSERT_NOT_REACHED();
195         return InspectorValue::null();
196     }
197     return injectedScript.callFrames();
198 }
199
200 // JavaScriptDebugListener functions
201
202 void InspectorDebuggerAgent::didParseSource(const String& sourceID, const String& url, const String& data, int firstLine, ScriptWorldType worldType)
203 {
204     // Don't send script content to the front end until it's really needed.
205     m_frontend->parsedScriptSource(sourceID, url, "", firstLine, worldType);
206
207     m_scriptIDToContent.set(sourceID, data);
208
209     if (url.isEmpty())
210         return;
211
212     URLToSourceIDsMap::iterator urlToSourceIDsIterator = m_urlToSourceIDs.find(url);
213     if (urlToSourceIDsIterator == m_urlToSourceIDs.end())
214         urlToSourceIDsIterator = m_urlToSourceIDs.set(url, Vector<String>()).first;
215     urlToSourceIDsIterator->second.append(sourceID);
216
217     HashMap<String, ScriptBreakpoints>::iterator stickyBreakpointsIterator = m_stickyBreakpoints.find(url);
218     if (stickyBreakpointsIterator == m_stickyBreakpoints.end())
219         return;
220
221     const ScriptBreakpoints& breakpoints = stickyBreakpointsIterator->second;
222     for (ScriptBreakpoints::const_iterator it = breakpoints.begin(); it != breakpoints.end(); ++it) {
223         const Breakpoint& breakpoint = it->second;
224         restoreBreakpoint(sourceID, it->first, breakpoint.first, breakpoint.second);
225     }
226 }
227
228 void InspectorDebuggerAgent::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage)
229 {
230     m_frontend->failedToParseScriptSource(url, data, firstLine, errorLine, errorMessage);
231 }
232
233 void InspectorDebuggerAgent::didPause(ScriptState* scriptState)
234 {
235     ASSERT(scriptState && !m_pausedScriptState);
236     m_pausedScriptState = scriptState;
237
238     if (!m_breakProgramDetails)
239         m_breakProgramDetails = InspectorObject::create();
240     m_breakProgramDetails->setValue("callFrames", currentCallFrames());
241
242     m_frontend->pausedScript(m_breakProgramDetails);
243     m_javaScriptPauseScheduled = false;
244 }
245
246 void InspectorDebuggerAgent::didContinue()
247 {
248     m_pausedScriptState = 0;
249     m_breakProgramDetails = 0;
250     m_frontend->resumedScript();
251 }
252
253 void InspectorDebuggerAgent::breakProgram(DebuggerEventType type, PassRefPtr<InspectorValue> data)
254 {
255     m_breakProgramDetails = InspectorObject::create();
256     m_breakProgramDetails->setNumber("eventType", type);
257     m_breakProgramDetails->setValue("eventData", data);
258     ScriptDebugServer::shared().breakProgram();
259 }
260
261 } // namespace WebCore
262
263 #endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)