InspectorDebuggerAgent.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. /*
  2. * Copyright (C) 2010 Apple Inc. All rights reserved.
  3. * Copyright (C) 2010-2011 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. #include "config.h"
  30. #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)
  31. #include "InspectorDebuggerAgent.h"
  32. #include "CachedResource.h"
  33. #include "ContentSearchUtils.h"
  34. #include "InjectedScript.h"
  35. #include "InjectedScriptManager.h"
  36. #include "InspectorFrontend.h"
  37. #include "InspectorPageAgent.h"
  38. #include "InspectorState.h"
  39. #include "InspectorValues.h"
  40. #include "InstrumentingAgents.h"
  41. #include "RegularExpression.h"
  42. #include "ScriptDebugServer.h"
  43. #include "ScriptObject.h"
  44. #include <wtf/text/WTFString.h>
  45. using WebCore::TypeBuilder::Array;
  46. using WebCore::TypeBuilder::Debugger::FunctionDetails;
  47. using WebCore::TypeBuilder::Debugger::ScriptId;
  48. using WebCore::TypeBuilder::Runtime::RemoteObject;
  49. namespace WebCore {
  50. namespace DebuggerAgentState {
  51. static const char debuggerEnabled[] = "debuggerEnabled";
  52. static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
  53. static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
  54. };
  55. const char* InspectorDebuggerAgent::backtraceObjectGroup = "backtrace";
  56. InspectorDebuggerAgent::InspectorDebuggerAgent(InstrumentingAgents* instrumentingAgents, InspectorCompositeState* inspectorState, InjectedScriptManager* injectedScriptManager)
  57. : InspectorBaseAgent<InspectorDebuggerAgent>("Debugger", instrumentingAgents, inspectorState)
  58. , m_injectedScriptManager(injectedScriptManager)
  59. , m_frontend(0)
  60. , m_pausedScriptState(0)
  61. , m_javaScriptPauseScheduled(false)
  62. , m_listener(0)
  63. {
  64. // FIXME: make breakReason optional so that there was no need to init it with "other".
  65. clearBreakDetails();
  66. m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, ScriptDebugServer::DontPauseOnExceptions);
  67. }
  68. InspectorDebuggerAgent::~InspectorDebuggerAgent()
  69. {
  70. ASSERT(!m_instrumentingAgents->inspectorDebuggerAgent());
  71. }
  72. void InspectorDebuggerAgent::enable()
  73. {
  74. m_instrumentingAgents->setInspectorDebuggerAgent(this);
  75. // FIXME(WK44513): breakpoints activated flag should be synchronized between all front-ends
  76. scriptDebugServer().setBreakpointsActivated(true);
  77. startListeningScriptDebugServer();
  78. if (m_listener)
  79. m_listener->debuggerWasEnabled();
  80. }
  81. void InspectorDebuggerAgent::disable()
  82. {
  83. m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, InspectorObject::create());
  84. m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, ScriptDebugServer::DontPauseOnExceptions);
  85. m_instrumentingAgents->setInspectorDebuggerAgent(0);
  86. stopListeningScriptDebugServer();
  87. scriptDebugServer().clearBreakpoints();
  88. scriptDebugServer().clearCompiledScripts();
  89. scriptDebugServer().continueProgram();
  90. clear();
  91. if (m_listener)
  92. m_listener->debuggerWasDisabled();
  93. }
  94. bool InspectorDebuggerAgent::enabled()
  95. {
  96. return m_state->getBoolean(DebuggerAgentState::debuggerEnabled);
  97. }
  98. void InspectorDebuggerAgent::causesRecompilation(ErrorString*, bool* result)
  99. {
  100. *result = scriptDebugServer().causesRecompilation();
  101. }
  102. void InspectorDebuggerAgent::canSetScriptSource(ErrorString*, bool* result)
  103. {
  104. *result = scriptDebugServer().canSetScriptSource();
  105. }
  106. void InspectorDebuggerAgent::supportsSeparateScriptCompilationAndExecution(ErrorString*, bool* result)
  107. {
  108. *result = scriptDebugServer().supportsSeparateScriptCompilationAndExecution();
  109. }
  110. void InspectorDebuggerAgent::enable(ErrorString*)
  111. {
  112. if (enabled())
  113. return;
  114. enable();
  115. m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
  116. ASSERT(m_frontend);
  117. }
  118. void InspectorDebuggerAgent::disable(ErrorString*)
  119. {
  120. if (!enabled())
  121. return;
  122. disable();
  123. m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
  124. }
  125. void InspectorDebuggerAgent::restore()
  126. {
  127. if (enabled()) {
  128. m_frontend->globalObjectCleared();
  129. enable();
  130. long pauseState = m_state->getLong(DebuggerAgentState::pauseOnExceptionsState);
  131. String error;
  132. setPauseOnExceptionsImpl(&error, pauseState);
  133. }
  134. }
  135. void InspectorDebuggerAgent::setFrontend(InspectorFrontend* frontend)
  136. {
  137. m_frontend = frontend->debugger();
  138. }
  139. void InspectorDebuggerAgent::clearFrontend()
  140. {
  141. m_frontend = 0;
  142. if (!enabled())
  143. return;
  144. disable();
  145. // FIXME: due to m_state->mute() hack in InspectorController, debuggerEnabled is actually set to false only
  146. // in InspectorState, but not in cookie. That's why after navigation debuggerEnabled will be true,
  147. // but after front-end re-open it will still be false.
  148. m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
  149. }
  150. void InspectorDebuggerAgent::setBreakpointsActive(ErrorString*, bool active)
  151. {
  152. if (active)
  153. scriptDebugServer().activateBreakpoints();
  154. else
  155. scriptDebugServer().deactivateBreakpoints();
  156. }
  157. bool InspectorDebuggerAgent::isPaused()
  158. {
  159. return scriptDebugServer().isPaused();
  160. }
  161. bool InspectorDebuggerAgent::runningNestedMessageLoop()
  162. {
  163. return scriptDebugServer().runningNestedMessageLoop();
  164. }
  165. void InspectorDebuggerAgent::addMessageToConsole(MessageSource source, MessageType type)
  166. {
  167. if (scriptDebugServer().pauseOnExceptionsState() != ScriptDebugServer::DontPauseOnExceptions && source == ConsoleAPIMessageSource && type == AssertMessageType)
  168. breakProgram(InspectorFrontend::Debugger::Reason::Assert, 0);
  169. }
  170. static PassRefPtr<InspectorObject> buildObjectForBreakpointCookie(const String& url, int lineNumber, int columnNumber, const String& condition, bool isRegex)
  171. {
  172. RefPtr<InspectorObject> breakpointObject = InspectorObject::create();
  173. breakpointObject->setString("url", url);
  174. breakpointObject->setNumber("lineNumber", lineNumber);
  175. breakpointObject->setNumber("columnNumber", columnNumber);
  176. breakpointObject->setString("condition", condition);
  177. breakpointObject->setBoolean("isRegex", isRegex);
  178. return breakpointObject;
  179. }
  180. static bool matches(const String& url, const String& pattern, bool isRegex)
  181. {
  182. if (isRegex) {
  183. RegularExpression regex(pattern, TextCaseSensitive);
  184. return regex.match(url) != -1;
  185. }
  186. return url == pattern;
  187. }
  188. void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString* errorString, int lineNumber, const String* const optionalURL, const String* const optionalURLRegex, const int* const optionalColumnNumber, const String* const optionalCondition, TypeBuilder::Debugger::BreakpointId* outBreakpointId, RefPtr<TypeBuilder::Array<TypeBuilder::Debugger::Location> >& locations)
  189. {
  190. locations = Array<TypeBuilder::Debugger::Location>::create();
  191. if (!optionalURL == !optionalURLRegex) {
  192. *errorString = "Either url or urlRegex must be specified.";
  193. return;
  194. }
  195. String url = optionalURL ? *optionalURL : *optionalURLRegex;
  196. int columnNumber = optionalColumnNumber ? *optionalColumnNumber : 0;
  197. String condition = optionalCondition ? *optionalCondition : "";
  198. bool isRegex = optionalURLRegex;
  199. String breakpointId = (isRegex ? "/" + url + "/" : url) + ':' + String::number(lineNumber) + ':' + String::number(columnNumber);
  200. RefPtr<InspectorObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
  201. if (breakpointsCookie->find(breakpointId) != breakpointsCookie->end()) {
  202. *errorString = "Breakpoint at specified location already exists.";
  203. return;
  204. }
  205. breakpointsCookie->setObject(breakpointId, buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, isRegex));
  206. m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCookie);
  207. ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
  208. for (ScriptsMap::iterator it = m_scripts.begin(); it != m_scripts.end(); ++it) {
  209. if (!matches(it->value.url, url, isRegex))
  210. continue;
  211. RefPtr<TypeBuilder::Debugger::Location> location = resolveBreakpoint(breakpointId, it->key, breakpoint);
  212. if (location)
  213. locations->addItem(location);
  214. }
  215. *outBreakpointId = breakpointId;
  216. }
  217. static bool parseLocation(ErrorString* errorString, RefPtr<InspectorObject> location, String* scriptId, int* lineNumber, int* columnNumber)
  218. {
  219. if (!location->getString("scriptId", scriptId) || !location->getNumber("lineNumber", lineNumber)) {
  220. // FIXME: replace with input validation.
  221. *errorString = "scriptId and lineNumber are required.";
  222. return false;
  223. }
  224. *columnNumber = 0;
  225. location->getNumber("columnNumber", columnNumber);
  226. return true;
  227. }
  228. void InspectorDebuggerAgent::setBreakpoint(ErrorString* errorString, const RefPtr<InspectorObject>& location, const String* const optionalCondition, TypeBuilder::Debugger::BreakpointId* outBreakpointId, RefPtr<TypeBuilder::Debugger::Location>& actualLocation)
  229. {
  230. String scriptId;
  231. int lineNumber;
  232. int columnNumber;
  233. if (!parseLocation(errorString, location, &scriptId, &lineNumber, &columnNumber))
  234. return;
  235. String condition = optionalCondition ? *optionalCondition : emptyString();
  236. String breakpointId = scriptId + ':' + String::number(lineNumber) + ':' + String::number(columnNumber);
  237. if (m_breakpointIdToDebugServerBreakpointIds.find(breakpointId) != m_breakpointIdToDebugServerBreakpointIds.end()) {
  238. *errorString = "Breakpoint at specified location already exists.";
  239. return;
  240. }
  241. ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition);
  242. actualLocation = resolveBreakpoint(breakpointId, scriptId, breakpoint);
  243. if (actualLocation)
  244. *outBreakpointId = breakpointId;
  245. else
  246. *errorString = "Could not resolve breakpoint";
  247. }
  248. void InspectorDebuggerAgent::removeBreakpoint(ErrorString*, const String& breakpointId)
  249. {
  250. RefPtr<InspectorObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
  251. breakpointsCookie->remove(breakpointId);
  252. m_state->setObject(DebuggerAgentState::javaScriptBreakpoints, breakpointsCookie);
  253. BreakpointIdToDebugServerBreakpointIdsMap::iterator debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.find(breakpointId);
  254. if (debugServerBreakpointIdsIterator == m_breakpointIdToDebugServerBreakpointIds.end())
  255. return;
  256. for (size_t i = 0; i < debugServerBreakpointIdsIterator->value.size(); ++i)
  257. scriptDebugServer().removeBreakpoint(debugServerBreakpointIdsIterator->value[i]);
  258. m_breakpointIdToDebugServerBreakpointIds.remove(debugServerBreakpointIdsIterator);
  259. }
  260. void InspectorDebuggerAgent::continueToLocation(ErrorString* errorString, const RefPtr<InspectorObject>& location)
  261. {
  262. if (!m_continueToLocationBreakpointId.isEmpty()) {
  263. scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointId);
  264. m_continueToLocationBreakpointId = "";
  265. }
  266. String scriptId;
  267. int lineNumber;
  268. int columnNumber;
  269. if (!parseLocation(errorString, location, &scriptId, &lineNumber, &columnNumber))
  270. return;
  271. ScriptBreakpoint breakpoint(lineNumber, columnNumber, "");
  272. m_continueToLocationBreakpointId = scriptDebugServer().setBreakpoint(scriptId, breakpoint, &lineNumber, &columnNumber);
  273. resume(errorString);
  274. }
  275. PassRefPtr<TypeBuilder::Debugger::Location> InspectorDebuggerAgent::resolveBreakpoint(const String& breakpointId, const String& scriptId, const ScriptBreakpoint& breakpoint)
  276. {
  277. ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
  278. if (scriptIterator == m_scripts.end())
  279. return 0;
  280. Script& script = scriptIterator->value;
  281. if (breakpoint.lineNumber < script.startLine || script.endLine < breakpoint.lineNumber)
  282. return 0;
  283. int actualLineNumber;
  284. int actualColumnNumber;
  285. String debugServerBreakpointId = scriptDebugServer().setBreakpoint(scriptId, breakpoint, &actualLineNumber, &actualColumnNumber);
  286. if (debugServerBreakpointId.isEmpty())
  287. return 0;
  288. BreakpointIdToDebugServerBreakpointIdsMap::iterator debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.find(breakpointId);
  289. if (debugServerBreakpointIdsIterator == m_breakpointIdToDebugServerBreakpointIds.end())
  290. debugServerBreakpointIdsIterator = m_breakpointIdToDebugServerBreakpointIds.set(breakpointId, Vector<String>()).iterator;
  291. debugServerBreakpointIdsIterator->value.append(debugServerBreakpointId);
  292. RefPtr<TypeBuilder::Debugger::Location> location = TypeBuilder::Debugger::Location::create()
  293. .setScriptId(scriptId)
  294. .setLineNumber(actualLineNumber);
  295. location->setColumnNumber(actualColumnNumber);
  296. return location;
  297. }
  298. static PassRefPtr<InspectorObject> scriptToInspectorObject(ScriptObject scriptObject)
  299. {
  300. if (scriptObject.hasNoValue())
  301. return 0;
  302. RefPtr<InspectorValue> value = scriptObject.toInspectorValue(scriptObject.scriptState());
  303. if (!value)
  304. return 0;
  305. return value->asObject();
  306. }
  307. 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)
  308. {
  309. bool isRegex = optionalIsRegex ? *optionalIsRegex : false;
  310. bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false;
  311. ScriptsMap::iterator it = m_scripts.find(scriptId);
  312. if (it != m_scripts.end())
  313. results = ContentSearchUtils::searchInTextByLines(it->value.source, query, caseSensitive, isRegex);
  314. else
  315. *error = "No script for id: " + scriptId;
  316. }
  317. void InspectorDebuggerAgent::setScriptSource(ErrorString* error, const String& scriptId, const String& newContent, const bool* const preview, RefPtr<Array<TypeBuilder::Debugger::CallFrame> >& newCallFrames, RefPtr<InspectorObject>& result)
  318. {
  319. bool previewOnly = preview && *preview;
  320. ScriptObject resultObject;
  321. if (!scriptDebugServer().setScriptSource(scriptId, newContent, previewOnly, error, &m_currentCallStack, &resultObject))
  322. return;
  323. newCallFrames = currentCallFrames();
  324. RefPtr<InspectorObject> object = scriptToInspectorObject(resultObject);
  325. if (object)
  326. result = object;
  327. }
  328. void InspectorDebuggerAgent::restartFrame(ErrorString* errorString, const String& callFrameId, RefPtr<Array<TypeBuilder::Debugger::CallFrame> >& newCallFrames, RefPtr<InspectorObject>& result)
  329. {
  330. InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId);
  331. if (injectedScript.hasNoValue()) {
  332. *errorString = "Inspected frame has gone";
  333. return;
  334. }
  335. injectedScript.restartFrame(errorString, m_currentCallStack, callFrameId, &result);
  336. scriptDebugServer().updateCallStack(&m_currentCallStack);
  337. newCallFrames = currentCallFrames();
  338. }
  339. void InspectorDebuggerAgent::getScriptSource(ErrorString* error, const String& scriptId, String* scriptSource)
  340. {
  341. ScriptsMap::iterator it = m_scripts.find(scriptId);
  342. if (it != m_scripts.end())
  343. *scriptSource = it->value.source;
  344. else
  345. *error = "No script for id: " + scriptId;
  346. }
  347. void InspectorDebuggerAgent::getFunctionDetails(ErrorString* errorString, const String& functionId, RefPtr<TypeBuilder::Debugger::FunctionDetails>& details)
  348. {
  349. InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(functionId);
  350. if (injectedScript.hasNoValue()) {
  351. *errorString = "Function object id is obsolete";
  352. return;
  353. }
  354. injectedScript.getFunctionDetails(errorString, functionId, &details);
  355. }
  356. void InspectorDebuggerAgent::schedulePauseOnNextStatement(InspectorFrontend::Debugger::Reason::Enum breakReason, PassRefPtr<InspectorObject> data)
  357. {
  358. if (m_javaScriptPauseScheduled)
  359. return;
  360. m_breakReason = breakReason;
  361. m_breakAuxData = data;
  362. scriptDebugServer().setPauseOnNextStatement(true);
  363. }
  364. void InspectorDebuggerAgent::cancelPauseOnNextStatement()
  365. {
  366. if (m_javaScriptPauseScheduled)
  367. return;
  368. clearBreakDetails();
  369. scriptDebugServer().setPauseOnNextStatement(false);
  370. }
  371. void InspectorDebuggerAgent::pause(ErrorString*)
  372. {
  373. if (m_javaScriptPauseScheduled)
  374. return;
  375. clearBreakDetails();
  376. scriptDebugServer().setPauseOnNextStatement(true);
  377. m_javaScriptPauseScheduled = true;
  378. }
  379. void InspectorDebuggerAgent::resume(ErrorString* errorString)
  380. {
  381. if (!assertPaused(errorString))
  382. return;
  383. m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
  384. scriptDebugServer().continueProgram();
  385. }
  386. void InspectorDebuggerAgent::stepOver(ErrorString* errorString)
  387. {
  388. if (!assertPaused(errorString))
  389. return;
  390. m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
  391. scriptDebugServer().stepOverStatement();
  392. }
  393. void InspectorDebuggerAgent::stepInto(ErrorString* errorString)
  394. {
  395. if (!assertPaused(errorString))
  396. return;
  397. m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
  398. scriptDebugServer().stepIntoStatement();
  399. m_listener->stepInto();
  400. }
  401. void InspectorDebuggerAgent::stepOut(ErrorString* errorString)
  402. {
  403. if (!assertPaused(errorString))
  404. return;
  405. m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup);
  406. scriptDebugServer().stepOutOfFunction();
  407. }
  408. void InspectorDebuggerAgent::setPauseOnExceptions(ErrorString* errorString, const String& stringPauseState)
  409. {
  410. ScriptDebugServer::PauseOnExceptionsState pauseState;
  411. if (stringPauseState == "none")
  412. pauseState = ScriptDebugServer::DontPauseOnExceptions;
  413. else if (stringPauseState == "all")
  414. pauseState = ScriptDebugServer::PauseOnAllExceptions;
  415. else if (stringPauseState == "uncaught")
  416. pauseState = ScriptDebugServer::PauseOnUncaughtExceptions;
  417. else {
  418. *errorString = "Unknown pause on exceptions mode: " + stringPauseState;
  419. return;
  420. }
  421. setPauseOnExceptionsImpl(errorString, pauseState);
  422. }
  423. void InspectorDebuggerAgent::setPauseOnExceptionsImpl(ErrorString* errorString, int pauseState)
  424. {
  425. scriptDebugServer().setPauseOnExceptionsState(static_cast<ScriptDebugServer::PauseOnExceptionsState>(pauseState));
  426. if (scriptDebugServer().pauseOnExceptionsState() != pauseState)
  427. *errorString = "Internal error. Could not change pause on exceptions state";
  428. else
  429. m_state->setLong(DebuggerAgentState::pauseOnExceptionsState, pauseState);
  430. }
  431. 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<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown)
  432. {
  433. InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId);
  434. if (injectedScript.hasNoValue()) {
  435. *errorString = "Inspected frame has gone";
  436. return;
  437. }
  438. ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = scriptDebugServer().pauseOnExceptionsState();
  439. if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) {
  440. if (previousPauseOnExceptionsState != ScriptDebugServer::DontPauseOnExceptions)
  441. scriptDebugServer().setPauseOnExceptionsState(ScriptDebugServer::DontPauseOnExceptions);
  442. muteConsole();
  443. }
  444. injectedScript.evaluateOnCallFrame(errorString, m_currentCallStack, callFrameId, expression, objectGroup ? *objectGroup : "", includeCommandLineAPI ? *includeCommandLineAPI : false, returnByValue ? *returnByValue : false, generatePreview ? *generatePreview : false, &result, wasThrown);
  445. if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) {
  446. unmuteConsole();
  447. if (scriptDebugServer().pauseOnExceptionsState() != previousPauseOnExceptionsState)
  448. scriptDebugServer().setPauseOnExceptionsState(previousPauseOnExceptionsState);
  449. }
  450. }
  451. void InspectorDebuggerAgent::compileScript(ErrorString* errorString, const String& expression, const String& sourceURL, TypeBuilder::OptOutput<ScriptId>* scriptId, TypeBuilder::OptOutput<String>* syntaxErrorMessage)
  452. {
  453. InjectedScript injectedScript = injectedScriptForEval(errorString, 0);
  454. if (injectedScript.hasNoValue()) {
  455. *errorString = "Inspected frame has gone";
  456. return;
  457. }
  458. String scriptIdValue;
  459. String exceptionMessage;
  460. scriptDebugServer().compileScript(injectedScript.scriptState(), expression, sourceURL, &scriptIdValue, &exceptionMessage);
  461. if (!scriptIdValue && !exceptionMessage) {
  462. *errorString = "Script compilation failed";
  463. return;
  464. }
  465. *syntaxErrorMessage = exceptionMessage;
  466. *scriptId = scriptIdValue;
  467. }
  468. void InspectorDebuggerAgent::runScript(ErrorString* errorString, const ScriptId& scriptId, const int* executionContextId, const String* const objectGroup, const bool* const doNotPauseOnExceptionsAndMuteConsole, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown)
  469. {
  470. InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId);
  471. if (injectedScript.hasNoValue()) {
  472. *errorString = "Inspected frame has gone";
  473. return;
  474. }
  475. ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = scriptDebugServer().pauseOnExceptionsState();
  476. if (doNotPauseOnExceptionsAndMuteConsole && *doNotPauseOnExceptionsAndMuteConsole) {
  477. if (previousPauseOnExceptionsState != ScriptDebugServer::DontPauseOnExceptions)
  478. scriptDebugServer().setPauseOnExceptionsState(ScriptDebugServer::DontPauseOnExceptions);
  479. muteConsole();
  480. }
  481. ScriptValue value;
  482. bool wasThrownValue;
  483. String exceptionMessage;
  484. scriptDebugServer().runScript(injectedScript.scriptState(), scriptId, &value, &wasThrownValue, &exceptionMessage);
  485. *wasThrown = wasThrownValue;
  486. if (value.hasNoValue()) {
  487. *errorString = "Script execution failed";
  488. return;
  489. }
  490. result = injectedScript.wrapObject(value, objectGroup ? *objectGroup : "");
  491. if (wasThrownValue)
  492. result->setDescription(exceptionMessage);
  493. if (doNotPauseOnExceptionsAndMuteConsole && *doNotPauseOnExceptionsAndMuteConsole) {
  494. unmuteConsole();
  495. if (scriptDebugServer().pauseOnExceptionsState() != previousPauseOnExceptionsState)
  496. scriptDebugServer().setPauseOnExceptionsState(previousPauseOnExceptionsState);
  497. }
  498. }
  499. void InspectorDebuggerAgent::setOverlayMessage(ErrorString*, const String*)
  500. {
  501. }
  502. void InspectorDebuggerAgent::setVariableValue(ErrorString* errorString, int scopeNumber, const String& variableName, const RefPtr<InspectorObject>& newValue, const String* callFrameId, const String* functionObjectId)
  503. {
  504. InjectedScript injectedScript;
  505. if (callFrameId) {
  506. injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*callFrameId);
  507. if (injectedScript.hasNoValue()) {
  508. *errorString = "Inspected frame has gone";
  509. return;
  510. }
  511. } else if (functionObjectId) {
  512. injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*functionObjectId);
  513. if (injectedScript.hasNoValue()) {
  514. *errorString = "Function object id cannot be resolved";
  515. return;
  516. }
  517. } else {
  518. *errorString = "Either call frame or function object must be specified";
  519. return;
  520. }
  521. String newValueString = newValue->toJSONString();
  522. injectedScript.setVariableValue(errorString, m_currentCallStack, callFrameId, functionObjectId, scopeNumber, variableName, newValueString);
  523. }
  524. void InspectorDebuggerAgent::scriptExecutionBlockedByCSP(const String& directiveText)
  525. {
  526. if (scriptDebugServer().pauseOnExceptionsState() != ScriptDebugServer::DontPauseOnExceptions) {
  527. RefPtr<InspectorObject> directive = InspectorObject::create();
  528. directive->setString("directiveText", directiveText);
  529. breakProgram(InspectorFrontend::Debugger::Reason::CSPViolation, directive.release());
  530. }
  531. }
  532. PassRefPtr<Array<TypeBuilder::Debugger::CallFrame> > InspectorDebuggerAgent::currentCallFrames()
  533. {
  534. if (!m_pausedScriptState)
  535. return Array<TypeBuilder::Debugger::CallFrame>::create();
  536. InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(m_pausedScriptState);
  537. if (injectedScript.hasNoValue()) {
  538. ASSERT_NOT_REACHED();
  539. return Array<TypeBuilder::Debugger::CallFrame>::create();
  540. }
  541. return injectedScript.wrapCallFrames(m_currentCallStack);
  542. }
  543. String InspectorDebuggerAgent::sourceMapURLForScript(const Script& script)
  544. {
  545. DEFINE_STATIC_LOCAL(String, sourceMapHTTPHeader, (ASCIILiteral("SourceMap")));
  546. DEFINE_STATIC_LOCAL(String, sourceMapHTTPHeaderDeprecated, (ASCIILiteral("X-SourceMap")));
  547. if (!script.url.isEmpty()) {
  548. if (InspectorPageAgent* pageAgent = m_instrumentingAgents->inspectorPageAgent()) {
  549. CachedResource* resource = pageAgent->cachedResource(pageAgent->mainFrame(), KURL(ParsedURLString, script.url));
  550. if (resource) {
  551. String sourceMapHeader = resource->response().httpHeaderField(sourceMapHTTPHeader);
  552. if (!sourceMapHeader.isEmpty())
  553. return sourceMapHeader;
  554. sourceMapHeader = resource->response().httpHeaderField(sourceMapHTTPHeaderDeprecated);
  555. if (!sourceMapHeader.isEmpty())
  556. return sourceMapHeader;
  557. }
  558. }
  559. }
  560. return ContentSearchUtils::findScriptSourceMapURL(script.source);
  561. }
  562. // JavaScriptDebugListener functions
  563. void InspectorDebuggerAgent::didParseSource(const String& scriptId, const Script& script)
  564. {
  565. // Don't send script content to the front end until it's really needed.
  566. const bool* isContentScript = script.isContentScript ? &script.isContentScript : 0;
  567. String sourceMapURL = sourceMapURLForScript(script);
  568. String* sourceMapURLParam = sourceMapURL.isNull() ? 0 : &sourceMapURL;
  569. String sourceURL;
  570. if (!script.startLine && !script.startColumn)
  571. sourceURL = ContentSearchUtils::findScriptSourceURL(script.source);
  572. bool hasSourceURL = !sourceURL.isEmpty();
  573. String scriptURL = hasSourceURL ? sourceURL : script.url;
  574. bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : 0;
  575. m_frontend->scriptParsed(scriptId, scriptURL, script.startLine, script.startColumn, script.endLine, script.endColumn, isContentScript, sourceMapURLParam, hasSourceURLParam);
  576. m_scripts.set(scriptId, script);
  577. if (scriptURL.isEmpty())
  578. return;
  579. RefPtr<InspectorObject> breakpointsCookie = m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
  580. for (InspectorObject::iterator it = breakpointsCookie->begin(); it != breakpointsCookie->end(); ++it) {
  581. RefPtr<InspectorObject> breakpointObject = it->value->asObject();
  582. bool isRegex;
  583. breakpointObject->getBoolean("isRegex", &isRegex);
  584. String url;
  585. breakpointObject->getString("url", &url);
  586. if (!matches(scriptURL, url, isRegex))
  587. continue;
  588. ScriptBreakpoint breakpoint;
  589. breakpointObject->getNumber("lineNumber", &breakpoint.lineNumber);
  590. breakpointObject->getNumber("columnNumber", &breakpoint.columnNumber);
  591. breakpointObject->getString("condition", &breakpoint.condition);
  592. RefPtr<TypeBuilder::Debugger::Location> location = resolveBreakpoint(it->key, scriptId, breakpoint);
  593. if (location)
  594. m_frontend->breakpointResolved(it->key, location);
  595. }
  596. }
  597. void InspectorDebuggerAgent::failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage)
  598. {
  599. m_frontend->scriptFailedToParse(url, data, firstLine, errorLine, errorMessage);
  600. }
  601. void InspectorDebuggerAgent::didPause(ScriptState* scriptState, const ScriptValue& callFrames, const ScriptValue& exception)
  602. {
  603. ASSERT(scriptState && !m_pausedScriptState);
  604. m_pausedScriptState = scriptState;
  605. m_currentCallStack = callFrames;
  606. if (!exception.hasNoValue()) {
  607. InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState);
  608. if (!injectedScript.hasNoValue()) {
  609. m_breakReason = InspectorFrontend::Debugger::Reason::Exception;
  610. m_breakAuxData = injectedScript.wrapObject(exception, "backtrace")->openAccessors();
  611. // m_breakAuxData might be null after this.
  612. }
  613. }
  614. m_frontend->paused(currentCallFrames(), m_breakReason, m_breakAuxData);
  615. m_javaScriptPauseScheduled = false;
  616. if (!m_continueToLocationBreakpointId.isEmpty()) {
  617. scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointId);
  618. m_continueToLocationBreakpointId = "";
  619. }
  620. if (m_listener)
  621. m_listener->didPause();
  622. }
  623. void InspectorDebuggerAgent::didContinue()
  624. {
  625. m_pausedScriptState = 0;
  626. m_currentCallStack = ScriptValue();
  627. clearBreakDetails();
  628. m_frontend->resumed();
  629. }
  630. void InspectorDebuggerAgent::breakProgram(InspectorFrontend::Debugger::Reason::Enum breakReason, PassRefPtr<InspectorObject> data)
  631. {
  632. m_breakReason = breakReason;
  633. m_breakAuxData = data;
  634. scriptDebugServer().breakProgram();
  635. }
  636. void InspectorDebuggerAgent::clear()
  637. {
  638. m_pausedScriptState = 0;
  639. m_currentCallStack = ScriptValue();
  640. m_scripts.clear();
  641. m_breakpointIdToDebugServerBreakpointIds.clear();
  642. m_continueToLocationBreakpointId = String();
  643. clearBreakDetails();
  644. m_javaScriptPauseScheduled = false;
  645. ErrorString error;
  646. setOverlayMessage(&error, 0);
  647. }
  648. bool InspectorDebuggerAgent::assertPaused(ErrorString* errorString)
  649. {
  650. if (!m_pausedScriptState) {
  651. *errorString = "Can only perform operation while paused.";
  652. return false;
  653. }
  654. return true;
  655. }
  656. void InspectorDebuggerAgent::clearBreakDetails()
  657. {
  658. m_breakReason = InspectorFrontend::Debugger::Reason::Other;
  659. m_breakAuxData = 0;
  660. }
  661. void InspectorDebuggerAgent::reset()
  662. {
  663. m_scripts.clear();
  664. m_breakpointIdToDebugServerBreakpointIds.clear();
  665. if (m_frontend)
  666. m_frontend->globalObjectCleared();
  667. }
  668. } // namespace WebCore
  669. #endif // ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR)