Index: build/premake/premake4.lua
===================================================================
--- build/premake/premake4.lua	(Revision 13210)
+++ build/premake/premake4.lua	(Arbeitskopie)
@@ -545,6 +545,7 @@
 		"boost",
 		"spidermonkey",
 		"valgrind",
+		"sdl",
 	}
 	setup_static_lib_project("scriptinterface", source_dirs, extern_libs, {})
 
Index: source/graphics/MapGenerator.cpp
===================================================================
--- source/graphics/MapGenerator.cpp	(Revision 13210)
+++ source/graphics/MapGenerator.cpp	(Arbeitskopie)
@@ -63,6 +63,7 @@
 void* CMapGeneratorWorker::RunThread(void *data)
 {
 	debug_SetThreadName("MapGenerator");
+	g_Profiler2.RegisterCurrentThread("MapGenerator");
 
 	CMapGeneratorWorker* self = static_cast<CMapGeneratorWorker*>(data);
 
Index: source/gui/scripting/JSInterface_IGUIObject.cpp
===================================================================
--- source/gui/scripting/JSInterface_IGUIObject.cpp	(Revision 13210)
+++ source/gui/scripting/JSInterface_IGUIObject.cpp	(Arbeitskopie)
@@ -75,6 +75,7 @@
 	if (propName == "constructor" ||
 		propName == "prototype"   ||
 		propName == "toString"    ||
+		propName == "toJSON"      ||
 		propName == "focus"       ||
 		propName == "blur"        ||
 		propName == "getComputedSize"
Index: source/ps/ConfigDB.h
===================================================================
--- source/ps/ConfigDB.h	(Revision 13210)
+++ source/ps/ConfigDB.h	(Arbeitskopie)
@@ -80,9 +80,12 @@
 	static VfsPath m_ConfigFile[];
 
 public:
+	CConfigDB();
+	
 	// NOTE: Construct the Singleton Object *after* JavaScript init, so that
-	// the JS interface can be registered.
-	CConfigDB();
+	// the JS interface can be registered. ConfigDB (C++) needs to be initialized before
+	// The ScriptInterface because the ScriptInterface requires some configuration information too.
+	void RegisterJSConfigDB();
 
 	/**
 	 * Attempt to find a config variable with the given name; will search
Index: source/ps/ConfigDB.cpp
===================================================================
--- source/ps/ConfigDB.cpp	(Revision 13210)
+++ source/ps/ConfigDB.cpp	(Arbeitskopie)
@@ -210,6 +210,10 @@
 
 CConfigDB::CConfigDB()
 {
+}
+
+void CConfigDB::RegisterJSConfigDB()
+{
 	g_ScriptingHost.DefineCustomObjectType(&ConfigDB_JS::Class, ConfigDB_JS::Construct, 0, ConfigDB_JS::Props, ConfigDB_JS::Funcs, NULL, NULL);
 	g_ScriptingHost.DefineCustomObjectType(&ConfigNamespace_JS::Class, ConfigNamespace_JS::Construct, 0, NULL, ConfigNamespace_JS::Funcs, NULL, NULL);
 	JSObject *js_ConfigDB = g_ScriptingHost.CreateCustomObject("ConfigDB");
Index: source/ps/GameSetup/GameSetup.cpp
===================================================================
--- source/ps/GameSetup/GameSetup.cpp	(Revision 13210)
+++ source/ps/GameSetup/GameSetup.cpp	(Arbeitskopie)
@@ -84,6 +84,7 @@
 #include "renderer/ModelRenderer.h"
 #include "scripting/ScriptingHost.h"
 #include "scripting/ScriptGlue.h"
+#include "scriptinterface/DebuggingServer.h"
 #include "scriptinterface/ScriptInterface.h"
 #include "scriptinterface/ScriptStats.h"
 #include "simulation2/Simulation2.h"
@@ -692,6 +693,7 @@
 
 	TIMER_BEGIN(L"shutdown ScriptingHost");
 	delete &g_ScriptingHost;
+	delete g_DebuggingServer;
 	TIMER_END(L"shutdown ScriptingHost");
 
 	TIMER_BEGIN(L"shutdown ConfigDB");
@@ -886,11 +888,17 @@
 	CSoundManager::CreateSoundManager();
 #endif
 
-	InitScripting();	// before GUI
-
 	// g_ConfigDB, command line args, globals
 	CONFIG_Init(args);
+	
+	// before scripting 
+	if (g_JSDebuggerEnabled)
+		g_DebuggingServer = new CDebuggingServer();
 
+	InitScripting();	// before GUI
+	
+	g_ConfigDB.RegisterJSConfigDB(); 	// after scripting 
+
 	// Optionally start profiler HTTP output automatically
 	// (By default it's only enabled by a hotkey, for security/performance)
 	bool profilerHTTPEnable = false;
Index: source/ps/GameSetup/Config.cpp
===================================================================
--- source/ps/GameSetup/Config.cpp	(Revision 13210)
+++ source/ps/GameSetup/Config.cpp	(Arbeitskopie)
@@ -21,6 +21,7 @@
 
 #include "ps/ConfigDB.h"
 #include "ps/CConsole.h"
+#include "ps/CLogger.h"
 #include "ps/GameSetup/CmdLineArgs.h"
 #include "lib/timer.h"
 #include "soundmanager/SoundManager.h"
@@ -60,6 +61,9 @@
 bool g_Quickstart = false;
 bool g_DisableAudio = false;
 
+bool g_JSDebuggerEnabled = false;
+bool g_ScriptProfilingEnabled = false;
+
 // flag to switch on drawing terrain overlays
 bool g_ShowPathfindingOverlay = false;
 
@@ -127,6 +131,14 @@
 		g_SoundManager->SetMemoryUsage(bufferSize, bufferCount);
 	}
 #endif // CONFIG2_AUDIO
+
+	CFG_GET_VAL("jsdebugger.enable", Bool, g_JSDebuggerEnabled);
+	CFG_GET_VAL("profiler2.script.enable", Bool, g_ScriptProfilingEnabled);
+
+	// Script Debugging and profiling does not make sense together because of the hooks
+	// that reduce performance a lot - and it wasn't tested if it even works together.
+	if (g_JSDebuggerEnabled && g_ScriptProfilingEnabled)
+		LOGERROR(L"Enabling both script profiling and script debugging is not supported!");
 }
 
 
Index: source/ps/GameSetup/Config.h
===================================================================
--- source/ps/GameSetup/Config.h	(Revision 13210)
+++ source/ps/GameSetup/Config.h	(Arbeitskopie)
@@ -82,6 +82,9 @@
 extern bool g_Quickstart;
 extern bool g_DisableAudio;
 
+extern bool g_JSDebuggerEnabled;
+extern bool g_ScriptProfilingEnabled;
+
 extern CStrW g_CursorName;
 
 class CmdLineArgs;
Index: source/scriptinterface/ScriptInterface.h
===================================================================
--- source/scriptinterface/ScriptInterface.h	(Revision 13210)
+++ source/scriptinterface/ScriptInterface.h	(Arbeitskopie)
@@ -43,17 +43,12 @@
 // TODO: what's a good default?
 #define DEFAULT_RUNTIME_SIZE 16 * 1024 * 1024
 
-
-#ifdef NDEBUG
-#define ENABLE_SCRIPT_PROFILING 0
-#else
-#define ENABLE_SCRIPT_PROFILING 1
-#endif
-
 struct ScriptInterface_impl;
 
 class ScriptRuntime;
 
+class CDebuggingServer;
+
 /**
  * Abstraction around a SpiderMonkey JSContext.
  *
@@ -244,7 +239,7 @@
 	 * Stringify to a JSON string, UTF-8 encoded. Returns an empty string on error.
 	 */
 	std::string StringifyJSON(jsval obj, bool indent = true);
-
+	
 	/**
 	 * Report the given error message through the JS error reporting mechanism,
 	 * and throw a JS exception. (Callers can check IsPendingException, and must
Index: source/scriptinterface/NativeWrapperDefns.h
===================================================================
--- source/scriptinterface/NativeWrapperDefns.h	(Revision 13210)
+++ source/scriptinterface/NativeWrapperDefns.h	(Arbeitskopie)
@@ -14,10 +14,10 @@
  * You should have received a copy of the GNU General Public License
  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
  */
+ #include "ps/GameSetup/Config.h"
 
 // (NativeWrapperDecls.h set up a lot of the macros we use here)
 
-
 // ScriptInterface_NativeWrapper<T>::call(cx, rval, fptr, args...) will call fptr(cbdata, args...),
 // and if T != void then it will store the result in rval:
 
@@ -76,18 +76,17 @@
 // ScriptInterface_impl::Register stores the name in a reserved slot.
 // (TODO: this doesn't work for functions registered via InterfaceScripted.h.
 // Maybe we should do some interned JS_GetFunctionId thing.)
-#if ENABLE_SCRIPT_PROFILING
 #define SCRIPT_PROFILE \
-	ENSURE(JSVAL_IS_OBJECT(JS_CALLEE(cx, vp)) && JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)))); \
-	const char* name = "(unknown)"; \
-	jsval nameval; \
-	if (JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &nameval) \
-		&& !JSVAL_IS_VOID(nameval)) \
-		name = static_cast<const char*>(JSVAL_TO_PRIVATE(nameval)); \
-	CProfileSampleScript profile(name);
-#else
-#define SCRIPT_PROFILE
-#endif
+	if (g_ScriptProfilingEnabled) \
+	{ \
+		ENSURE(JSVAL_IS_OBJECT(JS_CALLEE(cx, vp)) && JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)))); \
+		const char* name = "(unknown)"; \
+		jsval nameval; \
+		if (JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &nameval) \
+			&& !JSVAL_IS_VOID(nameval)) \
+			name = static_cast<const char*>(JSVAL_TO_PRIVATE(nameval)); \
+		CProfileSampleScript profile(name); \
+	}
 
 // JSFastNative-compatible function that wraps the function identified in the template argument list
 #define OVERLOADS(z, i, data) \
Index: source/scriptinterface/ThreadDebugger.h
===================================================================
--- source/scriptinterface/ThreadDebugger.h	(Revision 0)
+++ source/scriptinterface/ThreadDebugger.h	(Arbeitskopie)
@@ -0,0 +1,233 @@
+/* Copyright (C) 2013 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+#ifndef INCLUDED_THREADDEBUGGER
+#define INCLUDED_THREADDEBUGGER
+
+#include "DebuggingServer.h"
+#include "ScriptInterface.h"
+#include "scriptinterface/ScriptExtraHeaders.h"
+
+
+// These Breakpoint classes are not implemented threadsafe. The class using the Breakpoints is responsible to make sure that 
+// only one thread accesses the Breakpoint at a time
+class CBreakPoint
+{
+public:
+	CBreakPoint() { m_UserLine = 0; m_Filename = ""; }
+	uint m_UserLine;
+	std::string m_Filename;
+};
+
+// Only use this with one ScriptInterface/CThreadDebugger!
+class CActiveBreakPoint : public CBreakPoint
+{
+public:
+	CActiveBreakPoint() : 
+		CBreakPoint()
+	{
+		Initialize();
+	}
+
+	CActiveBreakPoint(CBreakPoint breakPoint)
+	{
+		m_Filename = breakPoint.m_Filename;
+		m_UserLine = breakPoint.m_UserLine;
+		Initialize();
+	}
+
+	void Initialize()
+	{
+		m_ToRemove = false;
+		m_Script = NULL;
+		m_Pc = NULL;
+		m_ActualLine = m_UserLine;
+	}
+	
+	uint m_ActualLine;
+	JSScript* m_Script;
+	jsbytecode* m_Pc;
+	bool m_ToRemove;
+};
+
+enum BREAK_SRC { BREAK_SRC_TRAP, BREAK_SRC_INTERRUP, BREAK_SRC_EXCEPTION };
+
+
+class CThreadDebugger
+{
+public:
+	CThreadDebugger();
+	~CThreadDebugger();
+
+	/** @brief Initialize the object (required before using the object!).
+	 *
+	 * @param	id A unique identifier greater than 0 for the object inside its CDebuggingServer object.
+	 * @param 	name A name that will be can be displayed by the UI to identify the thread.
+	 * @param	pScriptInterface Pointer to a scriptinterface. All Hooks, breakpoint traps etc. will be registered in this 
+	 *          scriptinterface and will be called by the thread this scriptinterface is running in.
+	 * @param	pDebuggingServer Pointer to the DebuggingServer object this Object should belong to.
+	 *
+	 * @return	Return value.
+	 */
+	void Initialize(uint id, std::string name, ScriptInterface* pScriptInterface, CDebuggingServer* pDebuggingServer);
+	
+	
+	// A bunch of hooks used to get information from spidermonkey.
+	// These hooks are used internally only but have to be public because they need to be accessible from the global hook functions.
+	// Spidermonkey requires function pointers as hooks, which only works if the functions are global or static (not part of an object).
+	// These global functions in ThreadDebugger.cpp are just wrappers for the following member functions.
+	
+	/** Simply calls BreakHandler with BREAK_SRC_TRAP */
+	JSTrapStatus TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, jsval closure);
+	/** Hook to capture exceptions and breakpoints in code (throw "Breakpoint";) */
+	JSTrapStatus ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval);
+	/** All other hooks call this one if the execution should be paused. It puts the program in a wait-loop and 
+	 *  waits for weak-up events (continue, step etc.). */
+	JSTrapStatus BreakHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, jsval closure, BREAK_SRC breakSrc);
+	JSTrapStatus StepHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure);
+	JSTrapStatus StepIntoHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure);
+	JSTrapStatus StepOutHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure);
+	/** This is an interrup-hook that can be called multiple times per line of code and is used to break into the execution
+	  * without previously setting a breakpoint and to break other threads when one thread triggers a breakpoint */
+	JSTrapStatus CheckForBreakRequestHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure);
+	/** The callback function which gets executed for each new script that gets loaded and each function inside a script.
+	 *   We use it for "Execute-Hooks" and "Call-Hooks" in terms of Spidermonkey.
+	 *   This hook actually sets the traps (Breakpoints) "on the fly" that have been defined by the user previously.
+	 */
+	void ExecuteHook(JSContext *cx, const char *filename, unsigned lineno, JSScript *script, JSFunction *fun, void *callerdata);
+	/** This hook is used to update the mapping between filename plus line-numbers and jsbytecode pointers */
+	void NewScriptHook(JSContext *cx, const char *filename, unsigned lineno, JSScript *script, JSFunction *fun, void *callerdata);
+	/** This hook makes sure that invalid mappings between filename plus line-number and jsbytecode points get deleted */
+	void DestroyScriptHook(JSContext *cx, JSScript *script);
+	
+
+	void ClearTrap(CActiveBreakPoint* activeBreakPoint);
+	
+	/** @brief Checks if a mapping for the specified filename and line number exists in this CThreadDebugger's context
+	 */	
+	bool CheckIfMappingPresent(std::string filename, uint line);
+	
+	/** @brief Checks if a mapping exists for each breakpoint in the list of breakpoints that aren't set yet.
+	 *         If there is a mapping, it removes the breakpoint from the list of unset breakpoints (from CDebuggingServer),
+	 *         adds it to the list of active breakpoints (CThreadDebugger) and sets a trap.
+	 *         Threading: m_Mutex is locked in this call
+	 */	
+	void SetAllNewTraps();
+	
+	/** @brief Sets a new trap and stores the information in the CActiveBreakPoint pointer
+	 *         Make sure that a mapping exists before calling this function
+	 *         Threading: Locking m_Mutex is required by the callee
+	 */	
+	void SetNewTrap(CActiveBreakPoint* activeBreakPoint, std::string filename, uint line);
+
+	/** @brief Toggle a breakpoint if it's active in this threadDebugger object.
+	 *         Threading: Locking m_Mutex is required by the callee
+	 *
+	 * @param	filename full vfs path to the script filename
+	 * @param 	userLine linenumber where the USER set the breakpoint (UserLine)
+	 *
+	 * @return	true if the breakpoint's state was changed
+	 */
+	bool ToggleBreakPoint(std::string filename, uint userLine);
+	
+	
+	void GetCallstack(std::stringstream& response);
+	void GetStackFrameData(std::stringstream& response, uint nestingLevel, STACK_INFO stackInfoKind);
+	
+	/** @brief Compares the object's associated scriptinterface with the pointer passed as parameter.
+	 * @return true if equal
+	 */
+	bool CompareScriptInterfacePtr(ScriptInterface* pScriptInterface);
+	
+	// Getter/Setters for members that need to be threadsafe
+	std::string GetBreakFileName();
+	bool GetIsInBreak();
+	uint GetLastBreakLine();
+	std::string GetName();
+	uint GetID();
+	void ContinueExecution();
+	void SetNextDbgCmd(DBGCMD dbgCmd);
+	DBGCMD GetNextDbgCmd();
+	// The callee is responsible for locking m_Mutex
+	void AddStackInfoRequest(STACK_INFO requestType, uint nestingLevel, SDL_sem* semaphore);
+	
+	
+private:
+	// Getters/Setters for members that need to be threadsafe
+	void SetBreakFileName(std::string breakFileName);
+	void SetLastBreakLine(uint breakLine);
+	void SetIsInBreak(bool isInBreak);
+	
+	// Other threadsafe functions
+	void SaveCallstack();
+	
+	CMutex m_Mutex;
+	CMutex m_ActiveBreakpointsMutex;
+	CMutex m_NextDbgCmdMutex;
+	CMutex m_IsInBreakMutex;
+	
+	/// Used only in the scriptinterface's thread.
+	void ClearTrapsToRemove();
+	bool CurrentFrameIsChildOf(JSStackFrame* pParentFrame);
+	void ReturnActiveBreakPoints(jsbytecode* pBytecode);
+	void SaveStackFrameData(STACK_INFO stackInfo, uint nestingLevel);
+	std::string StringifyCyclicJSON(jsval obj, bool indent);
+	// This member could actually be used by other threads via CompareScriptInterfacePtr(), but that should be safe
+	ScriptInterface* m_pScriptInterface; 
+	CDebuggingServer* m_pDebuggingServer;
+	// We store the pointer on the heap because the stack frame becomes invalid in certain cases
+	// and spidermonkey throws errors if it detects a pointer on the stack.
+	// We only use the pointer for comparing it with the current stack pointer and we don't try to access it, so it
+	// shouldn't be a problem.
+	JSStackFrame** m_pLastBreakFrame;
+	uint m_ObjectReferenceID;
+	
+	/// shared between multiple mongoose threads and one scriptinterface thread
+	std::string m_BreakFileName;
+	uint m_LastBreakLine;
+	bool m_IsInBreak;
+	DBGCMD m_NextDbgCmd;
+
+	
+	struct StackInfoRequest
+	{
+		STACK_INFO requestType;
+		uint nestingLevel;
+		SDL_sem* semaphore;
+		
+	};
+	std::queue<StackInfoRequest> m_StackInfoRequests;
+	
+	struct trapLocation
+	{
+		jsbytecode* pBytecode;
+		JSScript* pScript;
+		uint firstLineInFunction;
+		uint lastLineInFunction;
+	};
+	std::map<std::string, std::map<uint, trapLocation> > m_LineToPCMap;
+	std::list<CActiveBreakPoint*> m_ActiveBreakPoints;
+	std::map<STACK_INFO, std::map<uint, std::string> > m_StackFrameData;
+	std::string m_Callstack;
+	
+	/// shared between multiple mongoose threads (initialization may be an exception)
+	std::string m_Name;	
+	uint m_ID;
+	
+};
+
+#endif // INCLUDED_THREADDEBUGGER
Index: source/scriptinterface/ScriptInterface.cpp
===================================================================
--- source/scriptinterface/ScriptInterface.cpp	(Revision 13210)
+++ source/scriptinterface/ScriptInterface.cpp	(Arbeitskopie)
@@ -18,6 +18,7 @@
 #include "precompiled.h"
 
 #include "ScriptInterface.h"
+#include "DebuggingServer.h"
 #include "ScriptStats.h"
 #include "AutoRooters.h"
 
@@ -73,17 +74,18 @@
 		m_rt = JS_NewRuntime(runtimeSize);
 		ENSURE(m_rt); // TODO: error handling
 
-#if ENABLE_SCRIPT_PROFILING
-		// Profiler isn't thread-safe, so only enable this on the main thread
-		if (ThreadUtil::IsMainThread())
+		if (g_ScriptProfilingEnabled)
 		{
-			if (CProfileManager::IsInitialised())
+			// Profiler isn't thread-safe, so only enable this on the main thread
+			if (ThreadUtil::IsMainThread())
 			{
-				JS_SetExecuteHook(m_rt, jshook_script, this);
-				JS_SetCallHook(m_rt, jshook_function, this);
+				if (CProfileManager::IsInitialised())
+				{
+					JS_SetExecuteHook(m_rt, jshook_script, this);
+					JS_SetCallHook(m_rt, jshook_function, this);
+				}
 			}
 		}
-#endif
 
 		JS_SetExtraGCRoots(m_rt, jshook_trace, this);
 	}
@@ -100,7 +102,7 @@
 
 private:
 
-#if ENABLE_SCRIPT_PROFILING
+
 	static void* jshook_script(JSContext* UNUSED(cx), JSStackFrame* UNUSED(fp), JSBool before, JSBool* UNUSED(ok), void* closure)
 	{
 		if (before)
@@ -212,7 +214,6 @@
 
 		return closure;
 	}
-#endif
 
 	static void jshook_trace(JSTracer* trc, void* data)
 	{
@@ -493,15 +494,17 @@
 	options |= JSOPTION_XML; // "ECMAScript for XML support: parse <!-- --> as a token"
 	options |= JSOPTION_VAROBJFIX; // "recommended" (fixes variable scoping)
 
-	// Enable method JIT, unless script profiling is enabled (since profiling
+	// Enable method JIT, unless script profiling/debugging is enabled (since profiling/debugging
 	// hooks are incompatible with the JIT)
-#if !ENABLE_SCRIPT_PROFILING
-	options |= JSOPTION_METHODJIT;
+	// TODO: Verify what exactly is incompatible
+	if (!g_ScriptProfilingEnabled && !g_JSDebuggerEnabled)
+	{
+		options |= JSOPTION_METHODJIT;
 
-	// Some other JIT flags to experiment with:
-	options |= JSOPTION_JIT;
-	options |= JSOPTION_PROFILING;
-#endif
+		// Some other JIT flags to experiment with:
+		options |= JSOPTION_JIT;
+		options |= JSOPTION_PROFILING;
+	}
 
 	JS_SetOptions(m_cx, options);
 
@@ -557,20 +560,21 @@
 	if (!func)
 		return;
 
-#if ENABLE_SCRIPT_PROFILING
-	// Store the function name in a slot, so we can pass it to the profiler.
+	if (g_ScriptProfilingEnabled)
+	{
+		// Store the function name in a slot, so we can pass it to the profiler.
 
-	// Use a flyweight std::string because we can't assume the caller has
-	// a correctly-aligned string and that its lifetime is long enough
-	typedef boost::flyweight<
-		std::string,
-		boost::flyweights::no_tracking
-		// can't use no_locking; Register might be called in threads
-	> LockedStringFlyweight;
+		// Use a flyweight std::string because we can't assume the caller has
+		// a correctly-aligned string and that its lifetime is long enough
+		typedef boost::flyweight<
+			std::string,
+			boost::flyweights::no_tracking
+			// can't use no_locking; Register might be called in threads
+		> LockedStringFlyweight;
 
-	LockedStringFlyweight fw(name);
-	JS_SetReservedSlot(m_cx, JS_GetFunctionObject(func), 0, PRIVATE_TO_JSVAL((void*)fw.get().c_str()));
-#endif
+		LockedStringFlyweight fw(name);
+		JS_SetReservedSlot(m_cx, JS_GetFunctionObject(func), 0, PRIVATE_TO_JSVAL((void*)fw.get().c_str()));
+	}
 }
 
 ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugName, const shared_ptr<ScriptRuntime>& runtime) :
@@ -582,6 +586,14 @@
 		if (g_ScriptStatsTable)
 			g_ScriptStatsTable->Add(this, debugName);
 	}
+	
+	if (g_JSDebuggerEnabled && g_DebuggingServer != NULL)
+	{
+		if(!JS_SetDebugMode(GetContext(), true))
+			LOGERROR(L"Failed to set Spidermonkey to debug mode!");
+		else
+			g_DebuggingServer->RegisterScriptinterface(debugName, this);
+	}
 }
 
 ScriptInterface::~ScriptInterface()
@@ -591,6 +603,10 @@
 		if (g_ScriptStatsTable)
 			g_ScriptStatsTable->Remove(this);
 	}
+	
+	// Unregister from the Debugger class
+	if (g_JSDebuggerEnabled && g_DebuggingServer != NULL)
+		g_DebuggingServer->UnRegisterScriptinterface(this);
 }
 
 void ScriptInterface::ShutDown()
@@ -1075,12 +1091,14 @@
 	{
 		JS_ClearPendingException(m->m_cx);
 		LOGERROR(L"StringifyJSON failed");
+		JS_ClearPendingException(m->m_cx);
 		return "";
 	}
 
 	return str.stream.str();
 }
 
+
 std::wstring ScriptInterface::ToString(jsval obj, bool pretty)
 {
 	if (JSVAL_IS_VOID(obj))
Index: source/scriptinterface/DebuggingServer.h
===================================================================
--- source/scriptinterface/DebuggingServer.h	(Revision 0)
+++ source/scriptinterface/DebuggingServer.h	(Arbeitskopie)
@@ -0,0 +1,151 @@
+/* Copyright (C) 2013 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDED_DEBUGGINGSERVER
+#define INCLUDED_DEBUGGINGSERVER
+
+#include "third_party/mongoose/mongoose.h"
+#include "ScriptInterface.h"
+
+#include "lib/external_libraries/libsdl.h"
+
+class CBreakPoint;
+class CThreadDebugger;
+
+enum DBGCMD { DBG_CMD_NONE=0, DBG_CMD_CONTINUE, DBG_CMD_SINGLESTEP, DBG_CMD_STEPINTO, DBG_CMD_STEPOUT };
+enum STACK_INFO { STACK_INFO_LOCALS=0, STACK_INFO_THIS, STACK_INFO_GLOBALOBJECT };
+
+class CDebuggingServer
+{
+public:
+	CDebuggingServer();
+	~CDebuggingServer();
+	
+	/** @brief Register a new ScriptInerface for debugging the scripts it executes
+	 *
+	 * @param name A name for the ScriptInterface (will be sent to the debugging client an probably displayed to the user)
+	 * @param pScriptInterface A pointer to the ScriptInterface. This pointer must stay valid until UnRegisterScriptInterface ist called!
+	 */
+	void RegisterScriptinterface(std::string name, ScriptInterface* pScriptInterface);
+	
+	/** @brief Unregister a ScriptInerface that was previously registered using RegisterScriptinterface. 
+	 *
+	 * @param pScriptInterface A pointer to the ScriptInterface
+	 */
+	void UnRegisterScriptinterface(ScriptInterface* pScriptInterface);
+
+	
+	// Mongoose callback when request comes from a client 
+	void* MgDebuggingServerCallback(mg_event event, struct mg_connection *conn, const struct mg_request_info *request_info);	
+	
+	/** @brief Aquire exclusive read and write access to the list of breakpoints.
+	 *
+	 * @param breakPoints A pointer to the list storing all breakpoints.
+	 *
+	 * @return  A number you need to pass to ReleaseBreakPointAccess().
+	 *
+	 * Make sure to call ReleaseBreakPointAccess after you don't need access any longer.
+	 * Using this function you get exclusive access and other threads won't be able to access the breakpoints until you call ReleaseBreakPointAccess!
+	 */
+	double AquireBreakPointAccess(std::list<CBreakPoint>** breakPoints);
+	
+	/** @brief See AquireBreakPointAccess(). You must not access the pointer returend by AquireBreakPointAccess() any longer after you call this function.
+	 * 	
+	 * @param breakPointsLockID The number you got when aquiring the access. It's used to make sure that this function never gets
+	 *          used by the wrong thread.        
+	 */	
+	void ReleaseBreakPointAccess(double breakPointsLockID);
+	
+	
+	/// Called from multiple Mongoose threads and multiple ScriptInterface threads
+	bool GetBreakRequestedByThread();
+	bool GetBreakRequestedByUser();
+	// Should other threads be stopped as soon as possible after a breakpoint is triggered in a thread
+	bool GetSettingSimultaneousThreadBreak();
+	// Should the debugger break on any JS-Exception? If set to false, it will only break when the exceptions text is "Breakpoint".
+	bool GetSettingBreakOnException();
+	void SetBreakRequestedByThread(bool Enabled);
+	void SetBreakRequestedByUser(bool Enabled);
+	
+private:
+	static const char* header400;
+
+	/// Webserver helper function (can be called by multiple mongooser threads)
+	bool GetWebArgs(struct mg_connection *conn, const struct mg_request_info* request_info, std::string argName, uint& arg);
+	bool GetWebArgs(struct mg_connection *conn, const struct mg_request_info* request_info, std::string argName, std::string& arg);
+	
+	/// Functions that are made available via http (can be called by multiple mongoose threads)
+	void GetThreadDebuggerStatus(std::stringstream& response);
+	void ToggleBreakPoint(std::string filename, uint line);
+	void GetAllCallstacks(std::stringstream& response);
+	void GetStackFrameData(std::stringstream& response, uint nestingLevel, uint threadDebuggerID, STACK_INFO stackInfoKind);
+	bool SetNextDbgCmd(uint threadDebuggerID, DBGCMD dbgCmd);
+	void SetSettingSimultaneousThreadBreak(bool Enabled);
+	void SetSettingBreakOnException(bool Enabled);
+	
+	/** @brief Returns a list of the full vfs paths to all files with the extension .js found in the vfs root
+	 * 
+	 *  @param response This will contain the list as JSON array.
+	 */
+	void EnumVfsJSFiles(std::stringstream& response);
+	
+	/** @brief Get the content of a .js file loaded into vfs
+	 *
+	 * @param filename A full vfs path (as returned by EnumVfsJSFiles).
+	 * @param response This will contain the contents of the requested file.
+	 */
+	void GetFile(std::string filename, std::stringstream& response);
+	
+	/// Shared between multiple mongoose threads
+	
+
+	bool m_SettingSimultaneousThreadBreak;
+	bool m_SettingBreakOnException;
+	
+	/// Shared between multiple scriptinterface threads
+	uint m_LastThreadDebuggerID;
+	
+	/// Shared between multiple scriptinerface threads and multiple mongoose threads
+	std::list<CThreadDebugger*> m_ThreadDebuggers;
+	
+	// The CThreadDebuggers will check this value and break the thread if it's true.
+	// This only works for JS code, so C++ code will not break until it executes JS-code again.
+	bool m_BreakRequestedByThread;
+	bool m_BreakRequestedByUser;
+	
+	// The breakpoint is uniquely identified using filename an line-number.
+	// Since the filename is the whole vfs path it should really be unique.
+	std::list<CBreakPoint> m_BreakPoints;
+	
+	/// Used for controlling access to m_BreakPoints
+	SDL_sem* m_BreakPointsSem;
+	double m_BreakPointsLockID;
+	
+	/// Mutexes used to ensure thread-safety. Currently we just use one Mutex (m_Mutex) and if we detect possible sources
+	/// of deadlocks, we use the second mutex for some members to avoid it.
+	CMutex m_Mutex;
+	CMutex m_Mutex1;
+	
+	/// Not important for this class' thread-safety
+	void EnableHTTP();
+	mg_context* m_MgContext;
+};
+
+extern CDebuggingServer* g_DebuggingServer;
+
+
+#endif // INCLUDED_DEBUGGINGSERVER
Index: source/scriptinterface/ThreadDebugger.cpp
===================================================================
--- source/scriptinterface/ThreadDebugger.cpp	(Revision 0)
+++ source/scriptinterface/ThreadDebugger.cpp	(Arbeitskopie)
@@ -0,0 +1,871 @@
+/* Copyright (C) 2013 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+#include "precompiled.h"
+
+#include "ThreadDebugger.h"
+#include "lib/utf8.h"
+#include "ps/CLogger.h"
+
+// Hooks
+
+CMutex ThrowHandlerMutex;
+static JSTrapStatus ThrowHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval, void* closure)
+{
+	CScopeLock lock(ThrowHandlerMutex);
+	CThreadDebugger* pThreadDebugger = (CThreadDebugger*) closure;
+	return pThreadDebugger->ThrowHandler(cx, script, pc, rval);
+}
+
+CMutex TrapHandlerMutex;
+static JSTrapStatus TrapHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval, jsval closure) 
+{
+	CScopeLock lock(TrapHandlerMutex);
+	CThreadDebugger* pThreadDebugger = (CThreadDebugger*) JSVAL_TO_PRIVATE(closure);
+	jsval val = JSVAL_NULL;
+	return pThreadDebugger->TrapHandler(cx, script, pc, rval, val);
+}
+
+CMutex StepHandlerMutex;
+JSTrapStatus StepHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval, void* closure)
+{
+	CScopeLock lock(StepHandlerMutex);
+	CThreadDebugger* pThreadDebugger = (CThreadDebugger*) closure;
+	jsval val = JSVAL_VOID;
+	return pThreadDebugger->StepHandler(cx, script, pc, rval, &val);
+}
+
+CMutex StepIntoHandlerMutex;
+JSTrapStatus StepIntoHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval, void* closure)
+{
+	CScopeLock lock(StepIntoHandlerMutex);
+	CThreadDebugger* pThreadDebugger = (CThreadDebugger*) closure;
+	return pThreadDebugger->StepIntoHandler(cx, script, pc, rval, NULL);
+}
+
+CMutex NewScriptHookMutex;
+void NewScriptHook_(JSContext* cx, const char* filename, unsigned lineno, JSScript* script, JSFunction* fun, void* callerdata)
+{
+	CScopeLock lock(NewScriptHookMutex);
+	CThreadDebugger* pThreadDebugger = (CThreadDebugger*) callerdata;
+	return pThreadDebugger->NewScriptHook(cx, filename, lineno, script, fun, NULL);
+}
+
+CMutex DestroyScriptHookMutex;
+void DestroyScriptHook_(JSContext* cx, JSScript* script, void* callerdata)
+{
+	CScopeLock lock(DestroyScriptHookMutex);
+	CThreadDebugger* pThreadDebugger = (CThreadDebugger*) callerdata;
+	return pThreadDebugger->DestroyScriptHook(cx, script);
+}
+
+CMutex StepOutHandlerMutex;
+JSTrapStatus StepOutHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval, void* closure)
+{
+	CScopeLock lock(StepOutHandlerMutex);
+	CThreadDebugger* pThreadDebugger = (CThreadDebugger*) closure;
+	return pThreadDebugger->StepOutHandler(cx, script, pc, rval, NULL);
+}
+
+CMutex CheckForBreakRequestHandlerMutex;
+JSTrapStatus CheckForBreakRequestHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval, void* closure)
+{
+	CScopeLock lock(CheckForBreakRequestHandlerMutex);
+	CThreadDebugger* pThreadDebugger = (CThreadDebugger*) closure;
+	return pThreadDebugger->CheckForBreakRequestHandler(cx, script, pc, rval, NULL);
+}
+
+CMutex CallHookMutex;
+static void* CallHook_(JSContext* cx, JSStackFrame* fp, JSBool before, JSBool* UNUSED(ok), void* closure)
+{
+	CScopeLock lock(CallHookMutex);
+	CThreadDebugger* pThreadDebugger = (CThreadDebugger*) closure;
+	if (before)
+	{
+		JSScript* script;
+		script = JS_GetFrameScript(cx, fp);
+		const char* fileName = JS_GetScriptFilename(cx, script);
+		uint lineno = JS_GetScriptBaseLineNumber(cx, script);
+		JSFunction* fun = JS_GetFrameFunction(cx, fp);
+		pThreadDebugger->ExecuteHook(cx, fileName, lineno, script, fun, closure);
+	}
+	
+	return closure;
+}
+
+/// CThreadDebugger
+
+void CThreadDebugger::ClearTrapsToRemove()
+{
+	CScopeLock lock(m_Mutex);
+	std::list<CActiveBreakPoint*>::iterator itr=m_ActiveBreakPoints.begin();
+	while (itr != m_ActiveBreakPoints.end())
+	{
+		if ((*itr)->m_ToRemove)
+		{
+				ClearTrap((*itr));
+				// Remove the breakpoint
+				delete (*itr);
+				itr = m_ActiveBreakPoints.erase(itr);
+
+		}
+		else
+			itr++;
+	}
+}
+
+void CThreadDebugger::ClearTrap(CActiveBreakPoint* activeBreakPoint)
+{
+	ENSURE(activeBreakPoint->m_Script != NULL && activeBreakPoint->m_Pc != NULL);
+	JSTrapHandler prevHandler;
+	jsval prevClosure;	
+	JS_ClearTrap(m_pScriptInterface->GetContext(), activeBreakPoint->m_Script, activeBreakPoint->m_Pc, &prevHandler, &prevClosure);
+	activeBreakPoint->m_Script = NULL;
+	activeBreakPoint->m_Pc = NULL;
+}
+
+void CThreadDebugger::SetAllNewTraps()
+{
+	std::list<CBreakPoint>* pBreakPoints = NULL;
+	double breakPointsLockID;
+	breakPointsLockID = m_pDebuggingServer->AquireBreakPointAccess(&pBreakPoints);
+	std::list<CBreakPoint>::iterator itr = pBreakPoints->begin();
+	while (itr != pBreakPoints->end())
+	{
+		if (CheckIfMappingPresent((*itr).m_Filename, (*itr).m_UserLine))
+		{
+			// We must not set a new trap if we already have set a trap for this line of code.
+			// For lines without source code it's possible to have breakpoints set that actually refer to another line 
+			// that contains code. This situation is possible if the line containing the sourcecode already has a breakpoint
+			// set and the user sets another one by setting a breakpoint on a line directly above without sourcecode.
+			bool trapAlreadySet = false;
+			{
+				CScopeLock lock(m_Mutex);
+				std::list<CActiveBreakPoint*>::iterator itr1;
+				for ( itr1 = m_ActiveBreakPoints.begin(); itr1 != m_ActiveBreakPoints.end(); itr1++)
+				{
+					if ((*itr1)->m_ActualLine == (*itr).m_UserLine)
+						trapAlreadySet = true;
+				}
+			}
+
+			if (!trapAlreadySet)
+			{
+				CActiveBreakPoint* pActiveBreakPoint = new CActiveBreakPoint((*itr));
+				SetNewTrap(pActiveBreakPoint, (*itr).m_Filename, (*itr).m_UserLine);
+				{
+					CScopeLock lock(m_Mutex);
+					m_ActiveBreakPoints.push_back(pActiveBreakPoint);
+				}
+				itr = pBreakPoints->erase(itr);
+				continue;
+			}
+		}
+		itr++;
+	}
+	m_pDebuggingServer->ReleaseBreakPointAccess(breakPointsLockID);
+}
+
+bool CThreadDebugger::CheckIfMappingPresent(std::string filename, uint line)
+{
+	bool isPresent = (m_LineToPCMap.end() != m_LineToPCMap.find(filename) && m_LineToPCMap[filename].end() != m_LineToPCMap[filename].find(line));
+	return isPresent;
+}
+
+void CThreadDebugger::SetNewTrap(CActiveBreakPoint* activeBreakPoint, std::string filename, uint line)
+{
+	ENSURE(activeBreakPoint->m_Script == NULL); // The trap must not be set already!
+	ENSURE(CheckIfMappingPresent(filename, line)); // You have to check if the mapping exists before calling this function!
+
+	jsbytecode* pc = m_LineToPCMap[filename][line].pBytecode;
+	JSScript* script = m_LineToPCMap[filename][line].pScript;
+	activeBreakPoint->m_Script = script;
+	activeBreakPoint->m_Pc = pc;
+	ENSURE(script != NULL && pc != NULL);
+	activeBreakPoint->m_ActualLine = JS_PCToLineNumber(m_pScriptInterface->GetContext(), script, pc);
+	
+	JS_SetTrap(m_pScriptInterface->GetContext(), script, pc, TrapHandler_, PRIVATE_TO_JSVAL(this));
+}
+
+
+CThreadDebugger::CThreadDebugger()
+{
+	m_NextDbgCmd = DBG_CMD_NONE;
+	m_IsInBreak = false;
+	m_pLastBreakFrame = new JSStackFrame*;
+}
+
+CThreadDebugger::~CThreadDebugger()
+{
+	// Clear all Traps and Breakpoints that are marked for removal
+	ClearTrapsToRemove();
+	
+	// Return all breakpoints to the associated CDebuggingServer
+	ReturnActiveBreakPoints(NULL);
+
+	// Remove all the hooks because they store a pointer to this object
+	JS_SetExecuteHook(m_pScriptInterface->GetRuntime(), NULL, NULL);
+	JS_SetCallHook(m_pScriptInterface->GetRuntime(), NULL, NULL);
+	JS_SetNewScriptHook(m_pScriptInterface->GetRuntime(), NULL, NULL);
+	JS_SetDestroyScriptHook(m_pScriptInterface->GetRuntime(), NULL, NULL);
+	
+	delete m_pLastBreakFrame;
+}
+
+void CThreadDebugger::ReturnActiveBreakPoints(jsbytecode* pBytecode)
+{
+	CScopeLock lock(m_ActiveBreakpointsMutex);
+	std::list<CActiveBreakPoint*>::iterator itr;
+	itr = m_ActiveBreakPoints.begin();
+	while (itr != m_ActiveBreakPoints.end())
+	{
+		// Breakpoints marked for removal should be deleted instead of returned!
+		if ( ((*itr)->m_Pc == pBytecode || pBytecode == NULL) && !(*itr)->m_ToRemove )
+		{
+			std::list<CBreakPoint>* pBreakPoints;
+			double breakPointsLockID = m_pDebuggingServer->AquireBreakPointAccess(&pBreakPoints);
+			CBreakPoint breakPoint;
+			breakPoint.m_UserLine = (*itr)->m_UserLine;
+			breakPoint.m_Filename = (*itr)->m_Filename;
+			// All active breakpoints should have a trap set
+			ClearTrap((*itr));
+			pBreakPoints->push_back(breakPoint);
+			delete (*itr);
+			itr=m_ActiveBreakPoints.erase(itr);
+			m_pDebuggingServer->ReleaseBreakPointAccess(breakPointsLockID);
+		}
+		else
+			itr++;
+	}
+}
+
+void CThreadDebugger::Initialize(uint id, std::string name, ScriptInterface* pScriptInterface, CDebuggingServer* pDebuggingServer)
+{
+	ENSURE(id != 0);
+	m_ID = id;
+	m_Name = name;
+	m_pScriptInterface = pScriptInterface;
+	m_pDebuggingServer = pDebuggingServer;
+	JS_SetExecuteHook(m_pScriptInterface->GetRuntime(), CallHook_, (void*)this);
+	JS_SetCallHook(m_pScriptInterface->GetRuntime(), CallHook_, (void*)this);
+	JS_SetNewScriptHook(m_pScriptInterface->GetRuntime(), NewScriptHook_, (void*)this);
+	JS_SetDestroyScriptHook(m_pScriptInterface->GetRuntime(), DestroyScriptHook_, (void*)this);
+	JS_SetThrowHook(m_pScriptInterface->GetRuntime(), ThrowHandler_, (void*)this);
+	
+	if (m_pDebuggingServer->GetSettingSimultaneousThreadBreak())
+	{
+		// Setup a handler to check for break-requests from the DebuggingServer regularly
+		JS_SetInterrupt(m_pScriptInterface->GetRuntime(), CheckForBreakRequestHandler_, (void*)this);
+	}
+}
+
+JSTrapStatus CThreadDebugger::StepHandler(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval, void* UNUSED(closure))
+{
+	// We break in two conditions
+	// 1. We are in the same frame but on a different line
+	//	  Note: On loops for example, we can go a few lines up again without leaving the current stack frame, so it's not necessarily 
+	//          a higher line number.
+	// 2. We are in a different Frame and m_pLastBreakFrame is not a parent of the current frame (because we stepped out of the function)
+	uint line = JS_PCToLineNumber(cx, script, pc);
+	JSStackFrame* iter = NULL;
+	JSStackFrame* pStackFrame;
+	pStackFrame = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+	uint lastBreakLine = GetLastBreakLine() ;
+	jsval val = JSVAL_VOID;
+	if ((*m_pLastBreakFrame == pStackFrame && lastBreakLine != line) || 
+		(*m_pLastBreakFrame != pStackFrame && !CurrentFrameIsChildOf(*m_pLastBreakFrame)))
+		return BreakHandler(cx, script, pc, rval, val, BREAK_SRC_INTERRUP);
+	else
+		return JSTRAP_CONTINUE;
+}
+
+JSTrapStatus CThreadDebugger::StepIntoHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void* UNUSED(closure))
+{
+	// We break when we are on the same stack frame but not on the same line 
+	// or when we are on another stack frame.
+	uint line = JS_PCToLineNumber(cx, script, pc);
+	JSStackFrame* iter = NULL;
+	JSStackFrame* pStackFrame;
+	pStackFrame = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+	uint lastBreakLine = GetLastBreakLine();
+	
+	jsval val = JSVAL_VOID;
+	if ((*m_pLastBreakFrame == pStackFrame && lastBreakLine != line) || *m_pLastBreakFrame != pStackFrame)
+		return BreakHandler(cx, script, pc, rval, val, BREAK_SRC_INTERRUP);
+	else
+		return JSTRAP_CONTINUE;
+}
+
+JSTrapStatus CThreadDebugger::StepOutHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void* UNUSED(closure))
+{
+	// We break when we are in a different Frame and m_pLastBreakFrame is not a parent of the current frame 
+	// (because we stepped out of the function)
+	JSStackFrame* iter = NULL;
+	JSStackFrame* pStackFrame;
+	pStackFrame = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+	if (pStackFrame != *m_pLastBreakFrame && !CurrentFrameIsChildOf(*m_pLastBreakFrame))
+	{
+		jsval val = JSVAL_VOID;
+		return BreakHandler(cx, script, pc, rval, val, BREAK_SRC_INTERRUP);
+	}
+	else
+		return JSTRAP_CONTINUE;
+}
+
+bool CThreadDebugger::CurrentFrameIsChildOf(JSStackFrame* pParentFrame)
+{
+	JSStackFrame* iter = NULL;
+	JSStackFrame* fp = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+	// Get the first parent Frame
+	fp = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+	while (fp)
+	{
+		if (fp == pParentFrame) 
+			return true;
+		fp = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+	}
+	return false;
+}
+
+JSTrapStatus CThreadDebugger::CheckForBreakRequestHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void* UNUSED(closure))
+{
+	jsval val = JSVAL_VOID;
+	if (m_pDebuggingServer->GetBreakRequestedByThread() || m_pDebuggingServer->GetBreakRequestedByUser())
+		return BreakHandler(cx, script, pc, rval, val, BREAK_SRC_INTERRUP);
+	else
+		return JSTRAP_CONTINUE;
+}
+
+JSTrapStatus CThreadDebugger::TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, jsval UNUSED(closure))
+{
+	jsval val = JSVAL_NULL;
+	return BreakHandler(cx, script, pc, rval, val, BREAK_SRC_TRAP);
+}
+
+JSTrapStatus CThreadDebugger::ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
+{
+	jsval jsexception;
+	JS_GetPendingException(cx, &jsexception);
+	if (JSVAL_IS_STRING(jsexception))
+	{
+		std::string str(JS_EncodeString(cx, JSVAL_TO_STRING(jsexception)));
+		if (str == "Breakpoint" || m_pDebuggingServer->GetSettingBreakOnException())
+		{
+			if (str == "Breakpoint")
+				JS_ClearPendingException(cx);
+			jsval val = JSVAL_NULL;
+			return BreakHandler(cx, script, pc, rval, val, BREAK_SRC_EXCEPTION);
+		}
+	}
+	return JSTRAP_CONTINUE;			
+}
+
+JSTrapStatus CThreadDebugger::BreakHandler(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* UNUSED(rval), jsval UNUSED(closure), BREAK_SRC breakSrc) 
+{	
+	uint line = JS_PCToLineNumber(cx, script, pc);
+	std::string filename(JS_GetScriptFilename(cx, script));
+
+	SetIsInBreak(true);
+	SaveCallstack();
+	SetLastBreakLine(line);
+	SetBreakFileName(filename);
+	*m_pLastBreakFrame = NULL;
+	
+	if (breakSrc == BREAK_SRC_INTERRUP)
+	{
+		JS_ClearInterrupt(m_pScriptInterface->GetRuntime(), NULL, NULL);
+		JS_SetSingleStepMode(cx, script, false);
+	}
+	
+	if (m_pDebuggingServer->GetSettingSimultaneousThreadBreak())
+	{
+		m_pDebuggingServer->SetBreakRequestedByThread(true);
+	}
+	
+	// Wait until the user continues the execution
+	while (1)
+	{
+		DBGCMD nextDbgCmd = GetNextDbgCmd();
+		
+		while (!m_StackInfoRequests.empty())
+		{
+			StackInfoRequest request = m_StackInfoRequests.front();
+			SaveStackFrameData(request.requestType, request.nestingLevel);
+			SDL_SemPost(request.semaphore);
+			m_StackInfoRequests.pop();
+		}
+		
+		if (nextDbgCmd == DBG_CMD_NONE)
+		{
+			// Wait a while before checking for new m_NextDbgCmd again.
+			// We don't want this loop to take 100% of a CPU core for each thread that is in break mode.
+			// On the other hande we don't want the debugger to become unresponsive.
+			SDL_Delay(100);
+		}
+		else if (nextDbgCmd == DBG_CMD_SINGLESTEP || nextDbgCmd == DBG_CMD_STEPINTO || nextDbgCmd == DBG_CMD_STEPOUT)
+		{
+			JSStackFrame* iter = NULL;
+			*m_pLastBreakFrame = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+			
+			if (!JS_SetSingleStepMode(cx, script, true))
+				LOGERROR(L"JS_SetSingleStepMode returned false!"); // TODO: When can this happen?
+			else
+			{
+				if (nextDbgCmd == DBG_CMD_SINGLESTEP)
+				{
+					JS_SetInterrupt(m_pScriptInterface->GetRuntime(), StepHandler_, this);
+					break;
+				}
+				else if (nextDbgCmd == DBG_CMD_STEPINTO)
+				{
+					JS_SetInterrupt(m_pScriptInterface->GetRuntime(), StepIntoHandler_, this);
+					break;
+				}
+				else if (nextDbgCmd == DBG_CMD_STEPOUT)
+				{
+					JS_SetInterrupt(m_pScriptInterface->GetRuntime(), StepOutHandler_, this);
+					break;
+				}
+			}
+		}
+		else if (nextDbgCmd == DBG_CMD_CONTINUE)
+		{
+			if (!JS_SetSingleStepMode(cx, script, true))
+				LOGERROR(L"JS_SetSingleStepMode returned false!"); // TODO: When can this happen?
+			else
+			{
+				// Setup a handler to check for break-requests from the DebuggingServer regularly
+				JS_SetInterrupt(m_pScriptInterface->GetRuntime(), CheckForBreakRequestHandler_, this);
+			}
+			break;
+		}
+		else 
+			debug_warn("Invalid DBGCMD found in CThreadDebugger::BreakHandler!");
+	}
+	ClearTrapsToRemove();
+	SetAllNewTraps();
+	SetNextDbgCmd(DBG_CMD_NONE);
+	SetIsInBreak(false);
+	SetBreakFileName("");
+	
+	// All saved stack data becomes invalid
+	{
+		CScopeLock lock(m_Mutex);
+		m_StackFrameData.clear();
+	}
+	
+	return JSTRAP_CONTINUE;
+}
+
+void CThreadDebugger::NewScriptHook(JSContext* cx, const char* filename, unsigned lineno, JSScript* script, JSFunction* UNUSED(fun), void* UNUSED(callerdata))
+{
+	uint scriptExtent = JS_GetScriptLineExtent (cx, script);
+    std::string stringFileName(filename);
+    if (stringFileName == "")
+    	return;
+
+	for (uint line = lineno; line < scriptExtent + lineno; ++line) 
+	{
+		// If we already have a mapping for this line, we check if the current scipt is more deeply nested.
+		// If it isn't more deeply nested, we don't overwrite the previous mapping
+		// The most deeply nested script is always the one that must be used!
+		uint firstLine = 0;
+		uint lastLine = 0;
+		jsbytecode* oldPC = NULL;
+		if (CheckIfMappingPresent(stringFileName, line))
+		{
+			firstLine = m_LineToPCMap[stringFileName][line].firstLineInFunction;
+			lastLine = m_LineToPCMap[stringFileName][line].lastLineInFunction;
+			
+			// If an entry nested equally is present too, we must overwrite it.
+			// The same script(function) can trigger a NewScriptHook multiple times without DestroyScriptHooks between these 
+			// calls. In this case the old script becomes invalid.
+			if (lineno < firstLine || scriptExtent + lineno > lastLine)
+				continue;
+			else
+				oldPC = m_LineToPCMap[stringFileName][line].pBytecode;
+				
+		}
+		jsbytecode* pc = JS_LineNumberToPC (cx, script, line);
+		m_LineToPCMap[stringFileName][line].pBytecode = pc;
+		m_LineToPCMap[stringFileName][line].pScript = script;
+		m_LineToPCMap[stringFileName][line].firstLineInFunction = lineno;
+		m_LineToPCMap[stringFileName][line].lastLineInFunction = lineno + scriptExtent;
+		
+		// If we are replacing a script, the associated traps become invalid
+		if (lineno == firstLine && scriptExtent + lineno == lastLine)
+		{
+			ReturnActiveBreakPoints(oldPC);
+			SetAllNewTraps();
+		}
+	}
+}
+
+void CThreadDebugger::DestroyScriptHook(JSContext* cx, JSScript* script)
+{
+	uint scriptExtent = JS_GetScriptLineExtent (cx, script);
+	uint baseLine = JS_GetScriptBaseLineNumber(cx, script);
+	
+	char* pStr = NULL;
+	pStr = (char*)JS_GetScriptFilename(cx, script);
+	if (pStr != NULL)
+	{
+		std::string fileName(pStr);
+
+		for (uint line = baseLine; line < scriptExtent + baseLine; ++line) 
+		{
+			if (CheckIfMappingPresent(fileName, line))
+			{
+				if (m_LineToPCMap[fileName][line].pScript == script)
+				{
+					ReturnActiveBreakPoints(m_LineToPCMap[fileName][line].pBytecode);
+					m_LineToPCMap[fileName].erase(line);
+					if (m_LineToPCMap[fileName].empty())
+						m_LineToPCMap.erase(fileName);
+				}
+			}
+		}
+	}
+}
+
+void CThreadDebugger::ExecuteHook(JSContext* UNUSED(cx), const char* UNUSED(filename), unsigned UNUSED(lineno), JSScript* UNUSED(script), JSFunction* UNUSED(fun), void* UNUSED(callerdata))
+{
+	// Search all breakpoints that have no trap set yet
+	{
+		PROFILE2("ExecuteHook");
+		SetAllNewTraps();
+	}
+	return;
+}
+
+bool CThreadDebugger::ToggleBreakPoint(std::string filename, uint userLine)
+{
+	CScopeLock lock(m_Mutex);
+	std::list<CActiveBreakPoint*>::iterator itr;
+	for (itr = m_ActiveBreakPoints.begin(); itr != m_ActiveBreakPoints.end(); itr++)
+	{
+		if ((*itr)->m_UserLine == userLine && (*itr)->m_Filename == filename)
+		{
+			(*itr)->m_ToRemove = !(*itr)->m_ToRemove;
+			return true;
+		}
+	}
+	return false;
+}
+
+void CThreadDebugger::GetCallstack(std::stringstream& response)
+{
+	CScopeLock lock(m_Mutex);
+	response << m_Callstack;
+}
+
+void CThreadDebugger::SaveCallstack()
+{
+	ENSURE(GetIsInBreak());
+	
+	CScopeLock lock(m_Mutex);
+	
+	JSStackFrame *fp;	
+	JSStackFrame *iter = 0;
+	std::string functionName;
+	jsint counter = 0;
+	
+	JSObject* jsArray;
+	jsArray = JS_NewArrayObject(m_pScriptInterface->GetContext(), 0, 0);
+	JSString* functionID;
+
+	fp = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+
+	while (fp)
+	{
+		JSFunction* fun = 0;
+		fun = JS_GetFrameFunction(m_pScriptInterface->GetContext(), fp);
+		if (NULL == fun)
+			functionID = JS_NewStringCopyZ(m_pScriptInterface->GetContext(), "null");
+		else
+		{
+			functionID = JS_GetFunctionId(fun);
+			if (NULL == functionID)
+				functionID = JS_NewStringCopyZ(m_pScriptInterface->GetContext(), "anonymous");
+		}
+
+		JSBool ret = JS_DefineElement(m_pScriptInterface->GetContext(), jsArray, counter, STRING_TO_JSVAL(functionID), NULL, NULL, 0);
+		ENSURE(ret);
+		fp = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+		counter++;
+	}
+	
+	m_Callstack = "";
+	m_Callstack = m_pScriptInterface->StringifyJSON(OBJECT_TO_JSVAL(jsArray), false).c_str();
+}
+
+void CThreadDebugger::GetStackFrameData(std::stringstream& response, uint nestingLevel, STACK_INFO stackInfoKind)
+{
+	// If the data is not yet cached, request it and wait until it's ready.
+	bool dataCached = false;
+	{
+		CScopeLock lock(m_Mutex);
+		dataCached = (!m_StackFrameData.empty() && m_StackFrameData[stackInfoKind].end() != m_StackFrameData[stackInfoKind].find(nestingLevel));
+	}
+
+	if (!dataCached)
+	{
+		SDL_sem* semaphore = SDL_CreateSemaphore(0);
+		AddStackInfoRequest(stackInfoKind, nestingLevel, semaphore);
+		SDL_SemWait(semaphore);
+		SDL_DestroySemaphore(semaphore);
+	}
+	
+	CScopeLock lock(m_Mutex);
+	{
+		response.str("");
+		response << m_StackFrameData[stackInfoKind][nestingLevel];
+	}
+}
+
+void CThreadDebugger::AddStackInfoRequest(STACK_INFO requestType, uint nestingLevel, SDL_sem* semaphore)
+{
+	StackInfoRequest request;
+	request.requestType = requestType;
+	request.semaphore = semaphore;
+	request.nestingLevel = nestingLevel;
+	m_StackInfoRequests.push(request);
+}
+
+void CThreadDebugger::SaveStackFrameData(STACK_INFO stackInfo, uint nestingLevel)
+{
+	ENSURE(GetIsInBreak());
+	
+	CScopeLock lock(m_Mutex);
+	JSStackFrame *fp;	
+	JSStackFrame *iter = 0;
+	uint counter = 0;
+	jsval val;
+	
+	if (stackInfo == STACK_INFO_GLOBALOBJECT)
+	{
+		JSObject* obj;
+		obj = JS_GetGlobalForScopeChain(m_pScriptInterface->GetContext());
+		m_StackFrameData[stackInfo][nestingLevel] = StringifyCyclicJSON(OBJECT_TO_JSVAL(obj), false);
+	}
+	else
+	{
+		fp = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+		while (fp)
+		{
+			if (counter == nestingLevel)
+			{
+				if (stackInfo == STACK_INFO_LOCALS)
+				{
+					JSObject* obj;
+					obj = JS_GetFrameCallObject(m_pScriptInterface->GetContext(), fp);
+					//obj = JS_GetFrameScopeChain(m_pScriptInterface->GetContext(), fp);
+					m_StackFrameData[stackInfo][nestingLevel] = StringifyCyclicJSON(OBJECT_TO_JSVAL(obj), false);
+				}
+				else if (stackInfo == STACK_INFO_THIS)
+				{
+					if (JS_GetFrameThis(m_pScriptInterface->GetContext(), fp, &val))
+					{
+						m_StackFrameData[stackInfo][nestingLevel] = StringifyCyclicJSON(val, false);
+					}
+					else
+						m_StackFrameData[stackInfo][nestingLevel] = "";
+				}
+			}
+			
+			counter++;
+			fp = JS_FrameIterator(m_pScriptInterface->GetContext(), &iter);
+		}
+	}
+}
+
+
+/*
+ * TODO: This is very hacky and ugly and should be improved. 
+ * It replaces cyclic references with a notification that cyclic references are not supported. 
+ * It would be better to create a format that supports cyclic references and allows the UI to display them correctly.
+ * Unfortunately this seems to require writing (or embedding) a new serializer to JSON or something similar.
+ * 
+ * Some things about the implementation which aren't optimal:
+ * 1. It uses globabl variables (they are limited to a namespace though)
+ * 2. It has to work around a bug in Spidermonkey.
+ * 3. It copies code from CScriptInterface. I did this to separate it cleanly because the debugger should not affect
+ *    the rest of the game and because this part of code should be replaced anyway in the future.
+ */
+ 
+ namespace CyclicRefWorkaround
+ {
+	std::set<JSObject*> g_ProcessedObjects;
+	jsval g_LastKey;
+	jsval g_LastValue;
+	bool g_RecursionDetectedInPrevReplacer = false;
+	uint g_countSameKeys = 0;
+
+	struct Stringifier
+	{
+		static JSBool callback(const jschar* buf, uint32 len, void* data)
+		{
+			utf16string str(buf, buf+len);
+			std::wstring strw(str.begin(), str.end());
+
+			Status err; // ignore Unicode errors
+			static_cast<Stringifier*>(data)->stream << utf8_from_wstring(strw, &err);
+			return JS_TRUE;
+		}
+
+		std::stringstream stream;
+	};
+	 
+	JSBool replacer(JSContext* cx, uintN UNUSED(argc), jsval* vp)
+	{
+		jsval value = JS_ARGV(cx, vp)[1];
+		jsval key = JS_ARGV(cx, vp)[0];
+		if (g_LastKey == key)
+			g_countSameKeys++;
+		else
+			g_countSameKeys = 0;
+		
+		if (JSVAL_IS_OBJECT(value))
+		{	
+			// Work around a spidermonkey bug that causes replacer to be called twice with the same key:
+			// https://bugzilla.mozilla.org/show_bug.cgi?id=636079
+			// TODO: Remove the workaround as soon as we upgrade to a newer version of Spidermonkey.
+
+			if (g_ProcessedObjects.end() == g_ProcessedObjects.find(JSVAL_TO_OBJECT(value)))
+			{
+					g_ProcessedObjects.insert(JSVAL_TO_OBJECT(value));
+			}
+			else if (g_countSameKeys %2 == 0 || g_RecursionDetectedInPrevReplacer)
+			{
+				g_RecursionDetectedInPrevReplacer = true;
+				jsval ret = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "Debugger: object removed from output because of cyclic reference."));
+				JS_SET_RVAL(cx, vp, ret);
+				g_LastKey = key;
+				g_LastValue = value;
+				return JS_TRUE;
+			}
+		}
+		g_LastKey = key;
+		g_LastValue = value;
+		g_RecursionDetectedInPrevReplacer = false;
+		JS_SET_RVAL(cx, vp, JS_ARGV(cx, vp)[1]);
+		return JS_TRUE;
+	}
+ }
+ 
+std::string CThreadDebugger::StringifyCyclicJSON(jsval obj, bool indent)
+{
+	CyclicRefWorkaround::Stringifier str;
+	CyclicRefWorkaround::g_ProcessedObjects.clear();
+	CyclicRefWorkaround::g_LastKey = JSVAL_VOID;
+	
+	JSObject* pGlob = JSVAL_TO_OBJECT(m_pScriptInterface->GetGlobalObject());
+	JSFunction* fun = JS_DefineFunction(m_pScriptInterface->GetContext(), pGlob, "replacer", CyclicRefWorkaround::replacer,        0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
+	JSObject* replacer = JS_GetFunctionObject(fun);
+	if (!JS_Stringify(m_pScriptInterface->GetContext(), &obj, replacer, indent ? INT_TO_JSVAL(2) : JSVAL_VOID, &CyclicRefWorkaround::Stringifier::callback, &str))
+	{
+		LOGERROR(L"StringifyJSON failed");
+		jsval exec;
+		jsval execString;
+		if (JS_GetPendingException(m_pScriptInterface->GetContext(), &exec))
+		{
+			if (JSVAL_IS_OBJECT(exec))
+			{
+				JS_GetProperty(m_pScriptInterface->GetContext(), JSVAL_TO_OBJECT(exec), "message", &execString);
+			
+				if (JSVAL_IS_STRING(execString))
+				{
+					std::string strExec = JS_EncodeString(m_pScriptInterface->GetContext(), JSVAL_TO_STRING(execString));
+					LOGERROR(L"Error: %hs", strExec.c_str());
+				}
+			}
+			
+		}			
+		JS_ClearPendingException(m_pScriptInterface->GetContext());
+		return "";
+	}
+
+	return str.stream.str();
+}
+
+
+bool CThreadDebugger::CompareScriptInterfacePtr(ScriptInterface* pScriptInterface)
+{
+	return (pScriptInterface == m_pScriptInterface);
+}
+
+
+std::string CThreadDebugger::GetBreakFileName()
+{
+	CScopeLock lock(m_Mutex);
+	return m_BreakFileName;
+}
+
+void CThreadDebugger::SetBreakFileName(std::string breakFileName)
+{
+	CScopeLock lock(m_Mutex);
+	m_BreakFileName = breakFileName;
+}
+
+uint CThreadDebugger::GetLastBreakLine()
+{
+	CScopeLock lock(m_Mutex);
+	return m_LastBreakLine;
+}
+
+void CThreadDebugger::SetLastBreakLine(uint breakLine)
+{
+	CScopeLock lock(m_Mutex);
+	m_LastBreakLine = breakLine;
+}
+
+bool CThreadDebugger::GetIsInBreak()
+{
+	CScopeLock lock(m_IsInBreakMutex);
+	return m_IsInBreak;
+}
+
+void CThreadDebugger::SetIsInBreak(bool isInBreak)
+{
+	CScopeLock lock(m_IsInBreakMutex);
+	m_IsInBreak = isInBreak;
+}
+
+void CThreadDebugger::SetNextDbgCmd(DBGCMD dbgCmd)
+{
+	CScopeLock lock(m_NextDbgCmdMutex);
+		m_NextDbgCmd = dbgCmd;
+}
+
+DBGCMD CThreadDebugger::GetNextDbgCmd()
+{
+	CScopeLock lock(m_NextDbgCmdMutex);
+	return m_NextDbgCmd;
+}
+
+std::string CThreadDebugger::GetName()
+{
+	CScopeLock lock(m_Mutex);
+	return m_Name;
+}
+
+uint CThreadDebugger::GetID()
+{
+	CScopeLock lock(m_Mutex);
+	return m_ID;
+}
+
Index: source/scriptinterface/DebuggingServer.cpp
===================================================================
--- source/scriptinterface/DebuggingServer.cpp	(Revision 0)
+++ source/scriptinterface/DebuggingServer.cpp	(Arbeitskopie)
@@ -0,0 +1,573 @@
+/* Copyright (C) 2013 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "precompiled.h"
+
+#include "DebuggingServer.h"
+#include "ThreadDebugger.h"
+#include "ps/CLogger.h"
+#include "ps/Filesystem.h"
+#include "scripting/JSConversions.h"
+
+CDebuggingServer* g_DebuggingServer = NULL;
+
+const char* CDebuggingServer::header400 =
+		"HTTP/1.1 400 Bad Request\r\n"
+		"Content-Type: text/plain; charset=utf-8\r\n\r\n"
+		"Invalid request";
+
+void CDebuggingServer::GetAllCallstacks(std::stringstream& response)
+{
+	CScopeLock lock(m_Mutex);
+	response.str("");
+	std::stringstream stream;
+	uint nbrCallstacksWritten = 0;
+	std::list<CThreadDebugger*>::iterator itr;
+	if (m_ThreadDebuggers.size() > 0)
+	{
+		response << "[";
+		for (itr = m_ThreadDebuggers.begin(); itr != m_ThreadDebuggers.end(); itr++)
+		{
+			if ((*itr)->GetIsInBreak())
+			{
+				stream.str("");
+				std::string str = stream.str();
+				(*itr)->GetCallstack(stream);
+				str = stream.str();
+				if (stream.str() != "")
+				{
+					if (nbrCallstacksWritten != 0)
+						response << ",";
+					response << "{" << "\"ThreadDebuggerID\" : " << (*itr)->GetID() << ", \"CallStack\" : " <<  stream.str() << "}";
+					nbrCallstacksWritten++;
+				}
+
+			}
+		}
+		response << "]";
+	}
+}
+
+void CDebuggingServer::GetStackFrameData(std::stringstream& response, uint nestingLevel, uint threadDebuggerID, STACK_INFO stackInfoKind)
+{
+	CScopeLock lock(m_Mutex);
+	response.str("");
+	std::stringstream stream;
+	std::list<CThreadDebugger*>::iterator itr;
+	for (itr = m_ThreadDebuggers.begin(); itr != m_ThreadDebuggers.end(); itr++)
+	{
+		if ( (*itr)->GetID() == threadDebuggerID && (*itr)->GetIsInBreak())
+		{
+			(*itr)->GetStackFrameData(stream, nestingLevel, stackInfoKind);
+			if (stream.str() != "")
+			{
+				response <<  stream.str();
+			}
+		}
+	}
+}
+
+
+CDebuggingServer::CDebuggingServer() :
+	m_MgContext(NULL)
+{
+	m_BreakPointsSem = SDL_CreateSemaphore(0);
+	ENSURE(m_BreakPointsSem);
+	SDL_SemPost(m_BreakPointsSem);
+	m_LastThreadDebuggerID = 0; // Next will be 1, 0 is reserved
+	m_BreakRequestedByThread = false;
+	m_BreakRequestedByUser = false;
+	m_SettingSimultaneousThreadBreak = true;
+	m_SettingBreakOnException = true;
+
+	EnableHTTP();
+	LOGWARNING(L"Javascript debugging webserver enabled.");
+}
+
+CDebuggingServer::~CDebuggingServer()
+{
+	SDL_DestroySemaphore(m_BreakPointsSem);
+	if (m_MgContext)
+	{
+		mg_stop(m_MgContext);
+		m_MgContext = NULL;
+	}
+}
+
+bool CDebuggingServer::SetNextDbgCmd(uint threadDebuggerID, DBGCMD dbgCmd)
+{
+	CScopeLock lock(m_Mutex);
+	std::list<CThreadDebugger*>::iterator itr;
+	for (itr = m_ThreadDebuggers.begin(); itr != m_ThreadDebuggers.end(); itr++)
+	{
+		if ( (*itr)->GetID() == threadDebuggerID || threadDebuggerID == 0)
+		{
+			if (DBG_CMD_NONE == (*itr)->GetNextDbgCmd() && (*itr)->GetIsInBreak())
+			{
+				SetBreakRequestedByThread(false);
+				SetBreakRequestedByUser(false);
+				(*itr)->SetNextDbgCmd(dbgCmd);
+			}
+		}
+	}
+	return true;
+}
+
+void CDebuggingServer::SetBreakRequestedByThread(bool Enabled)
+{
+	CScopeLock lock(m_Mutex1);
+	m_BreakRequestedByThread = Enabled;
+}
+
+bool CDebuggingServer::GetBreakRequestedByThread()
+{
+	CScopeLock lock(m_Mutex1);
+	return m_BreakRequestedByThread;
+}
+
+void CDebuggingServer::SetBreakRequestedByUser(bool Enabled)
+{
+	CScopeLock lock(m_Mutex1);
+	m_BreakRequestedByUser = Enabled;
+}
+
+bool CDebuggingServer::GetBreakRequestedByUser()
+{
+	CScopeLock lock(m_Mutex1);
+	return m_BreakRequestedByUser;
+}
+
+void CDebuggingServer::SetSettingBreakOnException(bool Enabled)
+{
+	CScopeLock lock(m_Mutex);
+	m_SettingBreakOnException = Enabled;
+}
+
+void CDebuggingServer::SetSettingSimultaneousThreadBreak(bool Enabled)
+{
+	CScopeLock lock(m_Mutex1);
+	m_SettingSimultaneousThreadBreak = Enabled;
+}
+
+bool CDebuggingServer::GetSettingBreakOnException()
+{
+	CScopeLock lock(m_Mutex);
+	return m_SettingBreakOnException;
+}
+
+bool CDebuggingServer::GetSettingSimultaneousThreadBreak()
+{
+	CScopeLock lock(m_Mutex1);
+	return m_SettingSimultaneousThreadBreak;
+}
+
+
+static Status AddFileResponse(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), const uintptr_t cbData)
+{
+	std::vector<std::string>& templates = *(std::vector<std::string>*)cbData;
+	std::wstring str(pathname.string());
+	templates.push_back(std::string(str.begin(), str.end()));
+	return INFO::OK;
+}
+
+void CDebuggingServer::EnumVfsJSFiles(std::stringstream& response)
+{
+	VfsPath path = L"";
+	VfsPaths pathnames;
+	response.str("");
+	
+	std::vector<std::string> templates;
+	vfs::ForEachFile(g_VFS, "", AddFileResponse, (uintptr_t)&templates, L"*.js", vfs::DIR_RECURSIVE);
+
+	std::vector<std::string>::iterator itr;
+	response << "[";
+	for (itr = templates.begin(); itr != templates.end(); itr++)
+	{
+		if (itr != templates.begin())
+			response << ",";
+		response << "\"" << *itr << "\"";
+	}
+	response << "]";
+}
+
+void CDebuggingServer::GetFile(std::string filename, std::stringstream& response)
+{
+	CVFSFile file;
+	if (file.Load(g_VFS, filename) != PSRETURN_OK)
+	{
+		response << "Failed to load the file contents";
+		return;
+	}
+
+	std::string code = file.DecodeUTF8(); // assume it's UTF-8
+	response << code;
+}
+
+
+static void* MgDebuggingServerCallback_(mg_event event, struct mg_connection *conn, const struct mg_request_info *request_info)
+{
+	CDebuggingServer* debuggingServer = (CDebuggingServer*)request_info->user_data;
+	ENSURE(debuggingServer);
+	return debuggingServer->MgDebuggingServerCallback(event, conn, request_info);
+}
+
+void* CDebuggingServer::MgDebuggingServerCallback(mg_event event, struct mg_connection *conn, const struct mg_request_info *request_info)
+{
+	void* handled = (void*)""; // arbitrary non-NULL pointer to indicate successful handling
+
+	const char* header200 =
+		"HTTP/1.1 200 OK\r\n"
+		"Access-Control-Allow-Origin: *\r\n" // TODO: not great for security
+		"Content-Type: text/plain; charset=utf-8\r\n\r\n";
+
+	const char* header404 =
+		"HTTP/1.1 404 Not Found\r\n"
+		"Content-Type: text/plain; charset=utf-8\r\n\r\n"
+		"Unrecognised URI";
+
+	switch (event)
+	{
+	case MG_NEW_REQUEST:
+	{
+		std::stringstream stream;
+		std::string uri = request_info->uri;
+		
+		if (uri == "/GetThreadDebuggerStatus")
+		{
+			GetThreadDebuggerStatus(stream);
+		}
+		else if (uri == "/EnumVfsJSFiles")
+		{
+			EnumVfsJSFiles(stream);
+		}
+		else if (uri == "/GetAllCallstacks")
+		{
+			GetAllCallstacks(stream);
+		}
+		else if (uri == "/Continue")
+		{
+			uint threadDebuggerID;
+			if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID))
+				return handled;
+			// TODO: handle the return value
+			SetNextDbgCmd(threadDebuggerID, DBG_CMD_CONTINUE);	
+		}
+		else if (uri == "/Break")
+		{
+			SetBreakRequestedByUser(true);
+		}
+		else if (uri == "/SetSettingSimultaneousThreadBreak")
+		{
+			std::string strEnabled;
+			bool bEnabled = false;
+			if (!GetWebArgs(conn, request_info, "enabled", strEnabled))
+				return handled;
+			// TODO: handle the return value
+			if (strEnabled == "true")
+				bEnabled = true;
+			else if (strEnabled == "false")
+				bEnabled = false;
+			else
+				return handled; // TODO: return an error state
+			SetSettingSimultaneousThreadBreak(bEnabled);
+		}
+		else if (uri == "/GetSettingSimultaneousThreadBreak")
+		{
+			stream << "{ \"Enabled\" : " << (GetSettingSimultaneousThreadBreak() ? "true" : "false") << " } ";
+		}
+		else if (uri == "/SetSettingBreakOnException")
+		{
+			std::string strEnabled;
+			bool bEnabled = false;
+			if (!GetWebArgs(conn, request_info, "enabled", strEnabled))
+				return handled;
+			// TODO: handle the return value
+			if (strEnabled == "true")
+				bEnabled = true;
+			else if (strEnabled == "false")
+				bEnabled = false;
+			else
+				return handled; // TODO: return an error state
+			SetSettingBreakOnException(bEnabled);
+		}
+		else if (uri == "/GetSettingBreakOnException")
+		{
+			stream << "{ \"Enabled\" : " << (GetSettingBreakOnException() ? "true" : "false") << " } ";
+		}
+		else if (uri == "/Step")
+		{
+			uint threadDebuggerID;
+			if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID))
+				return handled;
+			// TODO: handle the return value
+			SetNextDbgCmd(threadDebuggerID, DBG_CMD_SINGLESTEP);
+		}
+		else if (uri == "/StepInto")
+		{
+			uint threadDebuggerID;
+			if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID))
+				return handled;
+			// TODO: handle the return value
+			SetNextDbgCmd(threadDebuggerID, DBG_CMD_STEPINTO);
+		}
+		else if (uri == "/StepOut")
+		{
+			uint threadDebuggerID;
+			if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID))
+				return handled;
+			// TODO: handle the return value
+			SetNextDbgCmd(threadDebuggerID, DBG_CMD_STEPOUT);
+		}
+		else if (uri == "/GetStackFrame")
+		{
+			uint nestingLevel;
+			uint threadDebuggerID;
+			if (!GetWebArgs(conn, request_info, "nestingLevel", nestingLevel) || 
+				!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID))
+			{
+				return handled;
+			}
+			GetStackFrameData(stream, nestingLevel, threadDebuggerID, STACK_INFO_LOCALS);
+		}
+		else if (uri == "/GetStackFrameThis")
+		{
+			uint nestingLevel;
+			uint threadDebuggerID;
+			if (!GetWebArgs(conn, request_info, "nestingLevel", nestingLevel) || 
+				!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID))
+			{
+				return handled;
+			}
+			GetStackFrameData(stream, nestingLevel, threadDebuggerID, STACK_INFO_THIS);
+		}
+		else if (uri == "/GetCurrentGlobalObject")
+		{
+			uint threadDebuggerID;
+			if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID))
+			{
+				return handled;
+			}
+			GetStackFrameData(stream, 0, threadDebuggerID, STACK_INFO_GLOBALOBJECT);
+		}
+		else if (uri == "/ToggleBreakpoint")
+		{
+			std::string filename;
+			uint line;
+			if (!GetWebArgs(conn, request_info, "filename", filename) ||
+				!GetWebArgs(conn, request_info, "line", line))
+			{
+				return handled;
+			}
+			ToggleBreakPoint(filename, line);
+		}
+		else if (uri == "/GetFile")
+		{
+			std::string filename;
+			if (!GetWebArgs(conn, request_info, "filename", filename))
+				return handled;
+			GetFile(filename, stream);
+		}
+		else
+		{
+			mg_printf(conn, "%s", header404);
+			return handled;
+		}
+
+		mg_printf(conn, "%s", header200);
+		std::string str = stream.str();
+		mg_write(conn, str.c_str(), str.length());
+		return handled;
+	}
+
+	case MG_HTTP_ERROR:
+		return NULL;
+
+	case MG_EVENT_LOG:
+		// Called by Mongoose's cry()
+		LOGERROR(L"Mongoose error: %hs", request_info->log_message);
+		return NULL;
+
+	case MG_INIT_SSL:
+		return NULL;
+
+	default:
+		debug_warn(L"Invalid Mongoose event type");
+		return NULL;
+	}
+};
+
+void CDebuggingServer::EnableHTTP()
+{
+	// Ignore multiple enablings
+	if (m_MgContext)
+		return;
+
+	const char *options[] = {
+		"listening_ports", "127.0.0.1:9000", // bind to localhost for security
+		"num_threads", "6", // enough for the browser's parallel connection limit
+		NULL
+	};
+	m_MgContext = mg_start(MgDebuggingServerCallback_, this, options);
+	ENSURE(m_MgContext);
+}
+
+bool CDebuggingServer::GetWebArgs(struct mg_connection *conn, const struct mg_request_info* request_info, std::string argName, uint& arg)
+{
+	if (!request_info->query_string)
+	{
+		mg_printf(conn, "%s (no query string)", header400);
+		return false;
+	}
+			
+	char buf[256];
+			
+	int len = mg_get_var(request_info->query_string, strlen(request_info->query_string), argName.c_str(), buf, ARRAY_SIZE(buf));
+	if (len < 0)
+	{
+			mg_printf(conn, "%s (no '%s')", header400, argName.c_str());
+			return false;
+	}
+	arg = atoi(buf);
+	return true;
+}
+
+bool CDebuggingServer::GetWebArgs(struct mg_connection *conn, const struct mg_request_info* request_info, std::string argName, std::string& arg)
+{
+	if (!request_info->query_string)
+	{
+		mg_printf(conn, "%s (no query string)", header400);
+		return false;
+	}
+			
+	char buf[256];
+	int len = mg_get_var(request_info->query_string, strlen(request_info->query_string), argName.c_str(), buf, ARRAY_SIZE(buf));
+	if (len < 0)
+	{
+		mg_printf(conn, "%s (no '%s')", header400, argName.c_str());
+		return false;
+	}
+	arg = buf;
+	return true;
+}
+
+void CDebuggingServer::RegisterScriptinterface(std::string name, ScriptInterface* pScriptInterface)
+{
+	CScopeLock lock(m_Mutex);
+	CThreadDebugger* pThreadDebugger = new CThreadDebugger;
+	// ThreadID 0 is reserved
+	pThreadDebugger->Initialize(++m_LastThreadDebuggerID, name, pScriptInterface, this);
+	m_ThreadDebuggers.push_back(pThreadDebugger);
+}
+
+void CDebuggingServer::UnRegisterScriptinterface(ScriptInterface* pScriptInterface)
+{
+	CScopeLock lock(m_Mutex);
+	std::list<CThreadDebugger*>::iterator itr;
+	for (itr = m_ThreadDebuggers.begin(); itr != m_ThreadDebuggers.end(); itr++)
+	{
+		if ((*itr)->CompareScriptInterfacePtr(pScriptInterface))
+		{
+			delete (*itr);
+			m_ThreadDebuggers.erase(itr);
+			break;
+		}
+	}
+}
+
+
+void CDebuggingServer::GetThreadDebuggerStatus(std::stringstream& response)
+{
+	CScopeLock lock(m_Mutex);
+	response.str("");
+	std::list<CThreadDebugger*>::iterator itr;
+
+	response << "[";
+	for (itr = m_ThreadDebuggers.begin(); itr != m_ThreadDebuggers.end(); itr++)
+	{
+		if (itr == m_ThreadDebuggers.begin())
+			response << "{ ";
+		else
+			response << ",{ ";
+		
+		response << "\"ThreadDebuggerID\" : " << (*itr)->GetID() << ",";
+		response << "\"ScriptInterfaceName\" : \"" << (*itr)->GetName() << "\",";
+		response << "\"ThreadInBreak\" : " << ((*itr)->GetIsInBreak() ? "true" : "false") << ",";
+		response << "\"BreakFileName\" : \"" << (*itr)->GetBreakFileName() << "\",";
+		response << "\"BreakLine\" : " << (*itr)->GetLastBreakLine();
+		response << " }";
+	}
+	response << "]";
+}
+
+void CDebuggingServer::ToggleBreakPoint(std::string filename, uint line)
+{
+	// First, pass the message to all associated CThreadDebugger objects and check if one returns true (handled);
+	{
+		CScopeLock lock(m_Mutex);
+		std::list<CThreadDebugger*>::iterator itr;
+		for (itr = m_ThreadDebuggers.begin(); itr != m_ThreadDebuggers.end(); itr++)
+		{
+			if ((*itr)->ToggleBreakPoint(filename, line))
+				return;
+		}
+	}
+	
+	// If the breakpoint isn't handled yet search the breakpoints registered in this class
+	std::list<CBreakPoint>* pBreakPoints = NULL;
+	double breakPointsLockID = AquireBreakPointAccess(&pBreakPoints);
+	std::list<CBreakPoint>::iterator itr;
+
+	// If set, delete
+	bool deleted = false;
+	for (itr = pBreakPoints->begin(); itr != pBreakPoints->end(); itr++)
+	{
+		if ((*itr).m_Filename == filename && (*itr).m_UserLine == line)
+		{
+			itr = pBreakPoints->erase(itr);
+			deleted = true;
+			break;
+		}
+	}
+
+	// If not set, set
+	if (!deleted)
+	{
+		CBreakPoint bP;
+		bP.m_Filename = filename;
+		bP.m_UserLine = line;
+		pBreakPoints->push_back(bP);
+	}
+	
+	ReleaseBreakPointAccess(breakPointsLockID);
+	return;
+}
+
+double CDebuggingServer::AquireBreakPointAccess(std::list<CBreakPoint>** breakPoints)
+{
+	int ret;
+	ret = SDL_SemWait(m_BreakPointsSem);
+	ENSURE(0 == ret);
+	(*breakPoints) = &m_BreakPoints;
+	m_BreakPointsLockID = timer_Time();
+	return m_BreakPointsLockID;
+}
+
+void CDebuggingServer::ReleaseBreakPointAccess(double breakPointsLockID)
+{
+	ENSURE(m_BreakPointsLockID == breakPointsLockID); 
+	SDL_SemPost(m_BreakPointsSem);
+}
Index: binaries/data/config/default.cfg
===================================================================
--- binaries/data/config/default.cfg	(Revision 13210)
+++ binaries/data/config/default.cfg	(Arbeitskopie)
@@ -290,11 +290,15 @@
 hotkey.profile2.enable = "F11"              ; Enable HTTP/GPU modes for new profiler
 
 profiler2.http.autoenable = false           ; Enable HTTP server output at startup (default off for security/performance)
+profiler2.script.enable = false             ; Enable Javascript profiling. Needs to be set before startup and can't be changed later. (default off for performance)
 profiler2.gpu.autoenable = false            ; Enable GPU timing at startup (default off for performance/compatibility)
 profiler2.gpu.arb.enable = true             ; Allow GL_ARB_timer_query timing mode when available
 profiler2.gpu.ext.enable = true             ; Allow GL_EXT_timer_query timing mode when available
 profiler2.gpu.intel.enable = true           ; Allow GL_INTEL_performance_queries timing mode when available
 
+; > JS DEBUGGER
+jsdebugger.enable = false                   ; Enable Javascript debugging (default off for security/performance) 			
+
 ; > QUICKSAVE
 hotkey.quicksave = "Shift+F5"
 hotkey.quickload = "Shift+F8"
