From 02510f1e028a1da5b6a698c1937ae61b7f0ecb41 Mon Sep 17 00:00:00 2001 From: James Chen Date: Sat, 5 Oct 2013 10:56:52 +0800 Subject: [PATCH 01/29] issue #2823: firefox remote connection ok. --- .../javascript/bindings/ScriptingCore.cpp | 177 ++++++++++++++++-- .../javascript/bindings/js_bindings_config.h | 2 +- 2 files changed, 167 insertions(+), 12 deletions(-) diff --git a/scripting/javascript/bindings/ScriptingCore.cpp b/scripting/javascript/bindings/ScriptingCore.cpp index 33fd2aee1f35..fc2146492238 100644 --- a/scripting/javascript/bindings/ScriptingCore.cpp +++ b/scripting/javascript/bindings/ScriptingCore.cpp @@ -1938,7 +1938,8 @@ void ScriptingCore::enableDebugger() { JS_DefineFunction(cx_, debugGlobal_, "_unlockVM", JSBDebug_UnlockExecution, 0, JSPROP_READONLY | JSPROP_PERMANENT); runScript("jsb_debugger.js", debugGlobal_); - + runScript("SysTest/script.js", debugGlobal_); + // prepare the debugger jsval argv = OBJECT_TO_JSVAL(global_); jsval outval; @@ -2166,6 +2167,13 @@ static void clearBuffers() { } } +static int replyToClient(int socket, const std::string& buf) +{ + std::stringstream bufSend; + bufSend << buf.length() << ":" << buf; + return ::send(socket, bufSend.str().c_str(), bufSend.str().length(), 0); +} + static void serverEntryPoint(void) { // start a server, accept the connection and keep reading data from it @@ -2218,25 +2226,172 @@ static void serverEntryPoint(void) listen(s, 1); + int recieveIndex = 0; while (true) { clientSocket = accept(s, NULL, NULL); + if (clientSocket < 0) - { - TRACE_DEBUGGER_SERVER("debug server : error on accept"); - return; - } else { + { + TRACE_DEBUGGER_SERVER("debug server : error on accept"); + return; + } + else + { // read/write data TRACE_DEBUGGER_SERVER("debug server : client connected"); - char buf[256]; - int readBytes; - while ((readBytes = read(clientSocket, buf, 256)) > 0) { + + if (recieveIndex == 0) + { + replyToClient(clientSocket, "{\"from\":\"root\",\"applicationType\":\"browser\",\"traits\":{\"sources\": true}}"); + ++recieveIndex; + } + + + char buf[256] = {0}; + int readBytes = 0; + while ((readBytes = ::recv(clientSocket, buf, sizeof(buf), 0)) > 0) + { buf[readBytes] = '\0'; TRACE_DEBUGGER_SERVER("debug server : received command >%s", buf); + + if (recieveIndex == 1) + { + replyToClient(clientSocket, "{ \"from\":\"root\", \"tabs\":[{ \"actor\":\"JSBTabActor\", \"title\":\"Hello cocos2d-x JSB\", \"url\":\"http://www.cocos2d-x.org\" }], \"selected\":0 }"); + } + else if (recieveIndex == 2) + { + replyToClient(clientSocket, "{ \"from\":\"JSBTabActor\", \"type\":\"tabAttached\", \"threadActor\":\"tabThreadActor111\" }"); + } + else if (recieveIndex == 3) + { + replyToClient(clientSocket, "{\ + \"from\": \"tabThreadActor111\",\ + \"type\": \"paused\",\ + \"actor\": \"JSBTabActor\",\ + \"poppedFrames\": [],\ + \"why\": {\ + \"type\": \"attached\"\ + }\ + }"); + + + + //replyToClient(clientSocket, "{ \"from\":\"JSBTabActor\", \"type\":\"tabNavigated\", \"state\":\"start\", \"url\":\"my_url.js\" }"); + } + else if (recieveIndex == 4) + { +// replyToClient(clientSocket, "{\"from\": \"tabThreadActor111\",\ +// \"type\": \"newSource\",\ +// \"source\": {\ +// \"actor\": \"source_actor2\",\ +// \"url\": \"http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js\",\ +// \"isBlackBoxed\": false\ +// }}"); + + replyToClient(clientSocket, "{\ + \"from\": \"tabThreadActor111\",\ + \"type\": \"newSource\",\ + \"source\": {\ + \"actor\": \"source_actor1\",\ + \"url\": \"file://~/Project/cocos2d-html5/cocos2d/CCDirector.js\",\ + \"isBlackBoxed\": false\ + }\ + }"); + + replyToClient(clientSocket, + "{\ + \"sources\": [\ + {\ + \"actor\": \"source_actor1\",\ + \"url\": \"file://~/Project/cocos2d-html5/cocos2d/CCDirector.js\",\ + \"isBlackBoxed\": false\ + }\ + ],\ + \"from\": \"tabThreadActor111\"\ + }"); + } + else if (recieveIndex == 5) + { + replyToClient(clientSocket, "{\ + \"from\": \"source_actor1\",\ + \"source\": {\ + \"type\": \"longString\",\ + \"initial\": \"var cc = cc || {}; cc.Director = {};\",\ + \"length\": 100,\ + \"actor\": \"conn2.longString48\"\ + }\ + }"); + } + else if (recieveIndex == 6) + { + replyToClient(clientSocket, "{\ + \"from\": \"conn2.longString48\",\ + \"substring\": \"var cc = cc || {}; cc.Director = {};\ +\\ncc.Sprite = {};\"\ + }"); + + + replyToClient(clientSocket, "{\ + \"from\": \"tabThreadActor111\",\ + \"type\": \"resumed\"\ + }"); + } + else if (recieveIndex == 7) + { + replyToClient(clientSocket, "{\ + \"from\": \"tabThreadActor111\",\ + \"type\": \"resumed\"\ + }"); + } + else if (recieveIndex == 8) + { + replyToClient(clientSocket, "{ \"from\":\"tabThreadActor111\", \"actor\":\"breakActor\"}");//, \"actualLocation\":2 }"); + } + else if (recieveIndex == 9) + { + replyToClient(clientSocket, "{ \"from\":\"breakActor\" }"); + } + else if (recieveIndex == 10) + { + replyToClient(clientSocket, "{\ + \"from\": \"tabThreadActor111\",\ + \"type\": \"resumed\"\ + }"); + } + else + { + + + std::string recvBuf = buf; + auto found = recvBuf.find("setBreakpoint"); + if (found != std::string::npos) + { + replyToClient(clientSocket, "{ \"from\":\"tabThreadActor111\", \"actor\":\"breakActor\"}");//, \"actualLocation\":2 }"); + } + + found = recvBuf.find("delete"); + if (found != std::string::npos) + { + replyToClient(clientSocket, "{ \"from\":\"breakActor\" }"); + } + + found = recvBuf.find("interrupt"); + if (found != std::string::npos) + { + replyToClient(clientSocket, "{\ + \"from\": \"tabThreadActor111\",\ + \"type\": \"resumed\"\ + }"); + } + + } + ++recieveIndex; // no other thread is using this - inData.append(buf); - // process any input, send any output - clearBuffers(); +// inData.append(buf); +// // process any input, send any output +// clearBuffers(); } // while(read) + close(clientSocket); } } // while(true) diff --git a/scripting/javascript/bindings/js_bindings_config.h b/scripting/javascript/bindings/js_bindings_config.h index 2216f2fca2d5..bfe1d64f962c 100644 --- a/scripting/javascript/bindings/js_bindings_config.h +++ b/scripting/javascript/bindings/js_bindings_config.h @@ -141,7 +141,7 @@ Set this to 1 to enable the debugger */ #ifndef JSB_ENABLE_DEBUGGER -#define JSB_ENABLE_DEBUGGER 0 +#define JSB_ENABLE_DEBUGGER 1 #endif // JSB_ENABLE_DEBUGGER #if JSB_ENABLE_DEBUGGER From 6772715cef841899180e1bfa7c9a24f5d9fddf10 Mon Sep 17 00:00:00 2001 From: James Chen Date: Sat, 5 Oct 2013 11:24:25 +0800 Subject: [PATCH 02/29] issue #2823: comment some test codes. --- .../javascript/bindings/ScriptingCore.cpp | 258 +++++++++--------- 1 file changed, 125 insertions(+), 133 deletions(-) diff --git a/scripting/javascript/bindings/ScriptingCore.cpp b/scripting/javascript/bindings/ScriptingCore.cpp index fc2146492238..77ba232fc08b 100644 --- a/scripting/javascript/bindings/ScriptingCore.cpp +++ b/scripting/javascript/bindings/ScriptingCore.cpp @@ -1938,7 +1938,7 @@ void ScriptingCore::enableDebugger() { JS_DefineFunction(cx_, debugGlobal_, "_unlockVM", JSBDebug_UnlockExecution, 0, JSPROP_READONLY | JSPROP_PERMANENT); runScript("jsb_debugger.js", debugGlobal_); - runScript("SysTest/script.js", debugGlobal_); +// runScript("SysTest/script.js", debugGlobal_); // prepare the debugger jsval argv = OBJECT_TO_JSVAL(global_); @@ -2254,142 +2254,134 @@ static void serverEntryPoint(void) buf[readBytes] = '\0'; TRACE_DEBUGGER_SERVER("debug server : received command >%s", buf); - if (recieveIndex == 1) - { - replyToClient(clientSocket, "{ \"from\":\"root\", \"tabs\":[{ \"actor\":\"JSBTabActor\", \"title\":\"Hello cocos2d-x JSB\", \"url\":\"http://www.cocos2d-x.org\" }], \"selected\":0 }"); - } - else if (recieveIndex == 2) - { - replyToClient(clientSocket, "{ \"from\":\"JSBTabActor\", \"type\":\"tabAttached\", \"threadActor\":\"tabThreadActor111\" }"); - } - else if (recieveIndex == 3) - { - replyToClient(clientSocket, "{\ - \"from\": \"tabThreadActor111\",\ - \"type\": \"paused\",\ - \"actor\": \"JSBTabActor\",\ - \"poppedFrames\": [],\ - \"why\": {\ - \"type\": \"attached\"\ - }\ - }"); - - - - //replyToClient(clientSocket, "{ \"from\":\"JSBTabActor\", \"type\":\"tabNavigated\", \"state\":\"start\", \"url\":\"my_url.js\" }"); - } - else if (recieveIndex == 4) - { -// replyToClient(clientSocket, "{\"from\": \"tabThreadActor111\",\ +// if (recieveIndex == 1) +// { +// replyToClient(clientSocket, "{ \"from\":\"root\", \"tabs\":[{ \"actor\":\"JSBTabActor\", \"title\":\"Hello cocos2d-x JSB\", \"url\":\"http://www.cocos2d-x.org\" }], \"selected\":0 }"); +// } +// else if (recieveIndex == 2) +// { +// replyToClient(clientSocket, "{ \"from\":\"JSBTabActor\", \"type\":\"tabAttached\", \"threadActor\":\"tabThreadActor111\" }"); +// } +// else if (recieveIndex == 3) +// { +// replyToClient(clientSocket, "{\ +// \"from\": \"tabThreadActor111\",\ +// \"type\": \"paused\",\ +// \"actor\": \"JSBTabActor\",\ +// \"poppedFrames\": [],\ +// \"why\": {\ +// \"type\": \"attached\"\ +// }\ +// }"); +// +// +// +// //replyToClient(clientSocket, "{ \"from\":\"JSBTabActor\", \"type\":\"tabNavigated\", \"state\":\"start\", \"url\":\"my_url.js\" }"); +// } +// else if (recieveIndex == 4) +// { +// replyToClient(clientSocket, "{\ +// \"from\": \"tabThreadActor111\",\ // \"type\": \"newSource\",\ // \"source\": {\ -// \"actor\": \"source_actor2\",\ -// \"url\": \"http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js\",\ +// \"actor\": \"source_actor1\",\ +// \"url\": \"file://~/Project/cocos2d-html5/cocos2d/CCDirector.js\",\ // \"isBlackBoxed\": false\ -// }}"); - - replyToClient(clientSocket, "{\ - \"from\": \"tabThreadActor111\",\ - \"type\": \"newSource\",\ - \"source\": {\ - \"actor\": \"source_actor1\",\ - \"url\": \"file://~/Project/cocos2d-html5/cocos2d/CCDirector.js\",\ - \"isBlackBoxed\": false\ - }\ - }"); - - replyToClient(clientSocket, - "{\ - \"sources\": [\ - {\ - \"actor\": \"source_actor1\",\ - \"url\": \"file://~/Project/cocos2d-html5/cocos2d/CCDirector.js\",\ - \"isBlackBoxed\": false\ - }\ - ],\ - \"from\": \"tabThreadActor111\"\ - }"); - } - else if (recieveIndex == 5) - { - replyToClient(clientSocket, "{\ - \"from\": \"source_actor1\",\ - \"source\": {\ - \"type\": \"longString\",\ - \"initial\": \"var cc = cc || {}; cc.Director = {};\",\ - \"length\": 100,\ - \"actor\": \"conn2.longString48\"\ - }\ - }"); - } - else if (recieveIndex == 6) - { - replyToClient(clientSocket, "{\ - \"from\": \"conn2.longString48\",\ - \"substring\": \"var cc = cc || {}; cc.Director = {};\ -\\ncc.Sprite = {};\"\ - }"); - - - replyToClient(clientSocket, "{\ - \"from\": \"tabThreadActor111\",\ - \"type\": \"resumed\"\ - }"); - } - else if (recieveIndex == 7) - { - replyToClient(clientSocket, "{\ - \"from\": \"tabThreadActor111\",\ - \"type\": \"resumed\"\ - }"); - } - else if (recieveIndex == 8) - { - replyToClient(clientSocket, "{ \"from\":\"tabThreadActor111\", \"actor\":\"breakActor\"}");//, \"actualLocation\":2 }"); - } - else if (recieveIndex == 9) - { - replyToClient(clientSocket, "{ \"from\":\"breakActor\" }"); - } - else if (recieveIndex == 10) - { - replyToClient(clientSocket, "{\ - \"from\": \"tabThreadActor111\",\ - \"type\": \"resumed\"\ - }"); - } - else - { - - - std::string recvBuf = buf; - auto found = recvBuf.find("setBreakpoint"); - if (found != std::string::npos) - { - replyToClient(clientSocket, "{ \"from\":\"tabThreadActor111\", \"actor\":\"breakActor\"}");//, \"actualLocation\":2 }"); - } - - found = recvBuf.find("delete"); - if (found != std::string::npos) - { - replyToClient(clientSocket, "{ \"from\":\"breakActor\" }"); - } - - found = recvBuf.find("interrupt"); - if (found != std::string::npos) - { - replyToClient(clientSocket, "{\ - \"from\": \"tabThreadActor111\",\ - \"type\": \"resumed\"\ - }"); - } - - } - ++recieveIndex; +// }\ +// }"); +// +// replyToClient(clientSocket, +// "{\ +// \"sources\": [\ +// {\ +// \"actor\": \"source_actor1\",\ +// \"url\": \"file://~/Project/cocos2d-html5/cocos2d/CCDirector.js\",\ +// \"isBlackBoxed\": false\ +// }\ +// ],\ +// \"from\": \"tabThreadActor111\"\ +// }"); +// } +// else if (recieveIndex == 5) +// { +// replyToClient(clientSocket, "{\ +// \"from\": \"source_actor1\",\ +// \"source\": {\ +// \"type\": \"longString\",\ +// \"initial\": \"var cc = cc || {}; cc.Director = {};\",\ +// \"length\": 100,\ +// \"actor\": \"conn2.longString48\"\ +// }\ +// }"); +// } +// else if (recieveIndex == 6) +// { +// replyToClient(clientSocket, "{\ +// \"from\": \"conn2.longString48\",\ +// \"substring\": \"var cc = cc || {}; cc.Director = {};\ +//\\ncc.Sprite = {};\"\ +// }"); +// +// +// replyToClient(clientSocket, "{\ +// \"from\": \"tabThreadActor111\",\ +// \"type\": \"resumed\"\ +// }"); +// } +// else if (recieveIndex == 7) +// { +// replyToClient(clientSocket, "{\ +// \"from\": \"tabThreadActor111\",\ +// \"type\": \"resumed\"\ +// }"); +// } +// else if (recieveIndex == 8) +// { +// replyToClient(clientSocket, "{ \"from\":\"tabThreadActor111\", \"actor\":\"breakActor\"}");//, \"actualLocation\":2 }"); +// } +// else if (recieveIndex == 9) +// { +// replyToClient(clientSocket, "{ \"from\":\"breakActor\" }"); +// } +// else if (recieveIndex == 10) +// { +// replyToClient(clientSocket, "{\ +// \"from\": \"tabThreadActor111\",\ +// \"type\": \"resumed\"\ +// }"); +// } +// else +// { +// +// +// std::string recvBuf = buf; +// auto found = recvBuf.find("setBreakpoint"); +// if (found != std::string::npos) +// { +// replyToClient(clientSocket, "{ \"from\":\"tabThreadActor111\", \"actor\":\"breakActor\"}");//, \"actualLocation\":2 }"); +// } +// +// found = recvBuf.find("delete"); +// if (found != std::string::npos) +// { +// replyToClient(clientSocket, "{ \"from\":\"breakActor\" }"); +// } +// +// found = recvBuf.find("interrupt"); +// if (found != std::string::npos) +// { +// replyToClient(clientSocket, "{\ +// \"from\": \"tabThreadActor111\",\ +// \"type\": \"resumed\"\ +// }"); +// } +// +// } +// ++recieveIndex; // no other thread is using this -// inData.append(buf); -// // process any input, send any output -// clearBuffers(); + inData.append(buf); + // process any input, send any output + clearBuffers(); } // while(read) close(clientSocket); From 1dbb747aeaf0d1206fe62040efde81fbaa5fdfd8 Mon Sep 17 00:00:00 2001 From: James Chen Date: Mon, 7 Oct 2013 10:50:12 +0800 Subject: [PATCH 03/29] issue #2823: load js file in firefox. --- .../javascript/bindings/ScriptingCore.cpp | 92 ++-- scripting/javascript/bindings/ScriptingCore.h | 2 +- .../javascript/bindings/js/jsb_debugger.js | 408 +++++++++++++++++- 3 files changed, 466 insertions(+), 36 deletions(-) diff --git a/scripting/javascript/bindings/ScriptingCore.cpp b/scripting/javascript/bindings/ScriptingCore.cpp index 77ba232fc08b..0ed5b8eb6874 100644 --- a/scripting/javascript/bindings/ScriptingCore.cpp +++ b/scripting/javascript/bindings/ScriptingCore.cpp @@ -392,7 +392,6 @@ JSBool ScriptingCore::evalString(const char *string, jsval *outVal, const char * global = global_; JSScript* script = JS_CompileScript(cx, global, string, strlen(string), filename, 1); if (script) { - // JSAutoCompartment ac(cx, global); JSAutoCompartment ac(cx, global); JSBool evaluatedOK = JS_ExecuteScript(cx, global, script, outVal); if (JS_FALSE == evaluatedOK) { @@ -463,19 +462,23 @@ void ScriptingCore::createGlobalContext() { this->cx_ = JS_NewContext(rt_, 8192); JS_SetOptions(this->cx_, JSOPTION_TYPE_INFERENCE); - JS_SetVersion(this->cx_, JSVERSION_LATEST); + +// JS_SetVersion(this->cx_, JSVERSION_LATEST); // Only disable METHODJIT on iOS. -#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) - JS_SetOptions(this->cx_, JS_GetOptions(this->cx_) & ~JSOPTION_METHODJIT); - JS_SetOptions(this->cx_, JS_GetOptions(this->cx_) & ~JSOPTION_METHODJIT_ALWAYS); -#endif +//#if (CC_TARGET_PLATFORM == CC_PLATFORM_ +// JS_SetOptions(this->cx_, JS_GetOptions(this->cx_) & ~JSOPTION_METHODJIT); +// JS_SetOptions(this->cx_, JS_GetOptions(this->cx_) & ~JSOPTION_METHODJIT_ALWAYS); +//#endif JS_SetErrorReporter(this->cx_, ScriptingCore::reportError); #if defined(JS_GC_ZEAL) && defined(DEBUG) //JS_SetGCZeal(this->cx_, 2, JS_DEFAULT_ZEAL_FREQ); #endif + this->global_ = NewGlobalObject(cx_); + JSAutoCompartment ac(cx_, global_); + #if JSB_ENABLE_DEBUGGER JS_SetDebugMode(cx_, JS_TRUE); #endif @@ -500,6 +503,8 @@ JSBool ScriptingCore::runScript(const char *path, JSObject* global, JSContext* c if (!path) { return false; } + + cocos2d::FileUtils *futil = cocos2d::FileUtils::getInstance(); std::string fullPath = futil->fullPathForFilename(path); if (global == NULL) { @@ -508,7 +513,12 @@ JSBool ScriptingCore::runScript(const char *path, JSObject* global, JSContext* c if (cx == NULL) { cx = cx_; } - JSScript *script = NULL; + + JSAutoCompartment ac(cx, global); + + uint32_t oldopts = JS_GetOptions(cx); + JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL); + js::RootedObject obj(cx, global); JS::CompileOptions options(cx); options.setUTF8(true).setFileAndLine(fullPath.c_str(), 1); @@ -519,6 +529,8 @@ JSBool ScriptingCore::runScript(const char *path, JSObject* global, JSContext* c void *data = futil->getFileData(byteCodePath.c_str(), "rb", &length); + + js::RootedScript script(cx); if (data) { script = JS_DecodeScript(cx, data, length, NULL, NULL); CC_SAFE_DELETE_ARRAY(data); @@ -930,15 +942,16 @@ JSBool ScriptingCore::executeFunctionWithOwner(jsval owner, const char *name, ui do { + JSAutoCompartment ac(cx, obj); + if (JS_HasProperty(cx, obj, name, &hasAction) && hasAction) { if (!JS_GetProperty(cx, obj, name, &temp_retval)) { break; } if (temp_retval == JSVAL_VOID) { break; - } + } - JSAutoCompartment ac(cx, obj); if (retVal) { bRet = JS_CallFunctionName(cx, obj, name, argc, vp, retVal); } @@ -1099,6 +1112,8 @@ int ScriptingCore::sendEvent(ScriptEvent* evt) { if (NULL == evt) return 0; + + JSAutoCompartment ac(cx_, global_); switch (evt->type) { @@ -1199,7 +1214,7 @@ JSBool jsval_to_long_long(JSContext *cx, jsval vp, long long* r) { } JSBool jsval_to_std_string(JSContext *cx, jsval v, std::string* ret) { - JSString *tmp = v.isString() ? JS_ValueToString(cx, v) : NULL; + JSString *tmp = JS_ValueToString(cx, v); JSB_PRECONDITION3(tmp, cx, JS_FALSE, "Error processing arguments"); JSStringWrapper str(tmp); @@ -1736,14 +1751,24 @@ jsval long_long_to_jsval(JSContext* cx, long long v) { return OBJECT_TO_JSVAL(tmp); } -jsval std_string_to_jsval(JSContext* cx, const std::string& v) { - return c_string_to_jsval(cx, v.c_str()); +jsval std_string_to_jsval(JSContext* cx, const std::string& v) +{ + return c_string_to_jsval(cx, v.c_str(), v.size()); } -jsval c_string_to_jsval(JSContext* cx, const char* v, size_t length /* = -1 */) { - if (v == NULL) { +jsval c_string_to_jsval(JSContext* cx, const char* v, size_t length /* = -1 */) +{ + if (v == NULL) + { return JSVAL_NULL; } + + if (0 == length) + { + auto emptyStr = JS_NewStringCopyZ(cx, ""); + return STRING_TO_JSVAL(emptyStr); + } + jsval ret = JSVAL_NULL; int utf16_size = 0; jschar* strUTF16 = (jschar*)cc_utf8_to_utf16(v, length, &utf16_size); @@ -1914,6 +1939,8 @@ void SimpleRunLoop::update(float dt) { } void ScriptingCore::debugProcessInput(string str) { + JSAutoCompartment ac(cx_, debugGlobal_); + JSString* jsstr = JS_NewStringCopyZ(cx_, str.c_str()); jsval argv[3] = { STRING_TO_JSVAL(jsstr), @@ -1921,12 +1948,13 @@ void ScriptingCore::debugProcessInput(string str) { script }; jsval outval; - JSAutoCompartment ac(cx_, debugGlobal_); + JS_CallFunctionName(cx_, debugGlobal_, "processInput", 3, argv, &outval); } void ScriptingCore::enableDebugger() { if (debugGlobal_ == NULL) { + JSAutoCompartment ac0(cx_, global_); debugGlobal_ = NewGlobalObject(cx_, true); JS_WrapObject(cx_, &debugGlobal_); JSAutoCompartment ac(cx_, debugGlobal_); @@ -1940,6 +1968,7 @@ void ScriptingCore::enableDebugger() { runScript("jsb_debugger.js", debugGlobal_); // runScript("SysTest/script.js", debugGlobal_); + CCLOG("before _prepareDebugger..."); // prepare the debugger jsval argv = OBJECT_TO_JSVAL(global_); jsval outval; @@ -1947,6 +1976,8 @@ void ScriptingCore::enableDebugger() { if (!ok) { JS_ReportPendingException(cx_); } + + CCLOG("after _prepareDebugger..."); // define the start debugger function JS_DefineFunction(cx_, global_, "startDebugger", JSBDebug_StartDebugger, 3, JSPROP_READONLY | JSPROP_PERMANENT); // start bg thread @@ -1954,6 +1985,7 @@ void ScriptingCore::enableDebugger() { auto t = std::thread(&serverEntryPoint); t.detach(); + CCLOG("after _prepareDebugger...02"); Scheduler* scheduler = Director::getInstance()->getScheduler(); scheduler->scheduleUpdateForTarget(this->runLoop, 0, false); } @@ -1991,15 +2023,23 @@ JSBool jsGetScript(JSContext* cx, unsigned argc, jsval* vp) JSObject* NewGlobalObject(JSContext* cx, bool debug) { - JSObject* glob = JS_NewGlobalObject(cx, &global_class, NULL); - if (!glob) { + JS::CompartmentOptions options; + options.setZone(JS::FreshZone) + .setVersion(JSVERSION_LATEST); + JS::RootedObject glob(cx, JS_NewGlobalObject(cx, &global_class, NULL, options)); + + if (!glob) + { return NULL; } + JSAutoCompartment ac(cx, glob); JSBool ok = JS_TRUE; ok = JS_InitStandardClasses(cx, glob); if (ok) - JS_InitReflect(cx, glob); + { + ok = JS_InitReflect(cx, glob) != NULL; + } if (ok && debug) ok = JS_DefineDebuggerObject(cx, glob); if (!ok) @@ -2240,14 +2280,18 @@ static void serverEntryPoint(void) // read/write data TRACE_DEBUGGER_SERVER("debug server : client connected"); - if (recieveIndex == 0) - { - replyToClient(clientSocket, "{\"from\":\"root\",\"applicationType\":\"browser\",\"traits\":{\"sources\": true}}"); - ++recieveIndex; - } + inData = "connected"; + // process any input, send any output + clearBuffers(); + +// if (recieveIndex == 0) +// { +// replyToClient(clientSocket, "{\"from\":\"root\",\"applicationType\":\"browser\",\"traits\":{\"sources\": true}}"); +// ++recieveIndex; +// } - char buf[256] = {0}; + char buf[1024] = {0}; int readBytes = 0; while ((readBytes = ::recv(clientSocket, buf, sizeof(buf), 0)) > 0) { diff --git a/scripting/javascript/bindings/ScriptingCore.h b/scripting/javascript/bindings/ScriptingCore.h index f7b8cb29e120..bea0541b8280 100644 --- a/scripting/javascript/bindings/ScriptingCore.h +++ b/scripting/javascript/bindings/ScriptingCore.h @@ -192,7 +192,7 @@ class ScriptingCore : public ScriptEngineProtocol void debugProcessInput(string str); void enableDebugger(); JSObject* getDebugGlobal() { return debugGlobal_; } - JSObject* getGlobalObject() { return global_; } + JSObject* getGlobalObject() { return global_ ? global_ : debugGlobal_; } private: void string_report(jsval val); diff --git a/scripting/javascript/bindings/js/jsb_debugger.js b/scripting/javascript/bindings/js/jsb_debugger.js index 20fecc78c20d..8d701d22f651 100644 --- a/scripting/javascript/bindings/js/jsb_debugger.js +++ b/scripting/javascript/bindings/js/jsb_debugger.js @@ -1,4 +1,8 @@ -dbg = {}; +dbg = { + LONG_STRING_LENGTH: 10000, + LONG_STRING_INITIAL_LENGTH: 1000, + LONG_STRING_READ_LENGTH: 1000 +}; dbg.log = log; var textCommandProcessor = {}; @@ -299,7 +303,39 @@ jsonResponder.onBreakpoint = function (filename, linenumber) { "data" : {"jsfilename" : filename, "linenumber" : linenumber}}; - this.write(JSON.stringify(response)); + dbg.log("onBreakpoint: " + JSON.stringify(response)); + +var breakInfo = { "from":"tabThreadActor111", "type":"paused", "actor":"pauseActor", + "why":{ "type":"breakpoint", "actors":["breakpointActor1"] }, + "frame":{ "actor":"frameActor", "depth":1, + "type":"call", "where":{ "url":"sample.js", "line":3 }, + "environment":{ "type":"function", "actor":"gFrameActor", + "function":{ "type":"object", "class":"Function", "actor":"gActor" }, + "functionName":"g", + "bindings":{ arguments: [ { "y": { "value":"argument to g", "configurable":"false", + "writable":true, "enumerable":true } } ] }, + "parent":{ "type":"function", "actor":"fFrameActor", + "function":{ "type":"object", "class":"Function", "actor":"fActor" }, + "functionName":"f", + "bindings": { arguments: [ { "x": { "value":"argument to f", "configurable":"false", + "writable":true, "enumerable":true } } ], + variables: { "z": { "value":"value of z", "configurable":"false", + "writable":true, "enumerable":true } } }, + "parent":{ "type":"object", "actor":"globalCodeActor", + "object":{ "type":"object", "class":"Global", + "actor":"globalObjectActor" } + } + } + }, + "callee":"gActor", "calleeName":"g", + "this":{ "type":"object", "class":"Function", "actor":"gActor" }, + "arguments":["argument to g"] + } + }; + + _RDPWrite(breakInfo); + + //this.write(JSON.stringify(response)); } jsonResponder.onStep = function (filename, linenumber) { @@ -332,10 +368,45 @@ textResponder.write = function (str) { _bufferWrite(String.fromCharCode(23)); } -textResponder.onBreakpoint = function (filename, linenumber) { - var shortFilename = filename.substring(filename.lastIndexOf("/") + 1); - var response = "Breakpoint hit at " + shortFilename + " line number : " + linenumber; - this.write(response); +var breakpointFrame = null; + +textResponder.onBreakpoint = function (frame) {//filename, linenumber) { + // var shortFilename = filename.substring(filename.lastIndexOf("/") + 1); + // var response = "Breakpoint hit at " + shortFilename + " line number : " + linenumber; + // dbg.log("textResponder.onBreakpoint:"+response); + + var breakInfo = { "from":"tabThreadActor111", "type":"paused", "actor":"pauseActor", + "why":{ "type":"breakpoint", "actors":["breakpointActor1"] }, + "frame":{ "actor":"frameActor", "depth":1, + "type":"call", "where":{ "url":"sample.js", "line":3 }, + "environment":{ "type":"function", "actor":"gFrameActor", + "function":{ "type":"object", "class":"Function", "actor":"gActor" }, + "functionName":"g", + "bindings":{ arguments: [ { "y": { "value":"argument to g", "configurable":"false", + "writable":true, "enumerable":true } } ] }, + "parent":{ "type":"function", "actor":"fFrameActor", + "function":{ "type":"object", "class":"Function", "actor":"fActor" }, + "functionName":"f", + "bindings": { arguments: [ { "x": { "value":"argument to f", "configurable":"false", + "writable":true, "enumerable":true } } ], + variables: { "z": { "value":"value of z", "configurable":"false", + "writable":true, "enumerable":true } } }, + "parent":{ "type":"object", "actor":"globalCodeActor", + "object":{ "type":"object", "class":"Global", + "actor":"globalObjectActor" } + } + } + }, + "callee":"gActor", "calleeName":"g", + "this":{ "type":"object", "class":"Function", "actor":"gActor" }, + "arguments":["argument to g"] + } + }; + + breakpointFrame = frame; + _RDPWrite(breakInfo); + + // this.write(response); } textResponder.onStep = function (filename, linenumber) { @@ -420,8 +491,9 @@ textResponder.commandNotFound = function () { var breakpointHandler = { hit: function (frame) { + dbg.log("breakpointHandler hit"); try { - dbg.responder.onBreakpoint(frame.script.url, frame.script.getOffsetLine(frame.offset)); + dbg.responder.onBreakpoint(frame);//frame.script.url, frame.script.getOffsetLine(frame.offset)); } catch (e) { dbg.log("exception " + e); } @@ -487,6 +559,71 @@ var debugObject = function (r, isNormal) { dbg.breakLine = 0; +dbg.scriptSourceActorMap = {}; + + + +addFiles = function() { + for (var key in dbg.scripts) + { + dbg.log("sources:" + key); + var scripts = dbg.scripts[key]; + for (var i = 0; i < scripts.length; ++i) + { + dbg.log("url:" + scripts[i].source.url); + dbg.log("-----------"); + + dbg.scriptSourceActorMap[key+"_SourceActor"] = scripts[i]; + + _RDPWrite( + { + from: "tabThreadActor111", + type: "newSource", + source: { + actor: key+"_SourceActor", + url: "file://" + scripts[i].source.url, + isBlackBoxed: false + } + }); + } + } +}; + +addInitialSource = function(sourceActor, script) +{ + _RDPWrite( + { + from: sourceActor, + source: { + type: "longString", + initial: script.source.text.substring(0, dbg.LONG_STRING_INITIAL_LENGTH), + length: script.source.text.length, + actor: sourceActor+"_LongString" + } + }); +} + +addSource = function(longStringActor, script) +{ + _RDPWrite( + { + from: longStringActor, + substring: script.source.text + }); +} + +isStringStartWith = function (str, substring) { + var reg = new RegExp("^"+substring); + return reg.test(str); +}; + +isStringEndsWith = function (str, substring) { + var reg = new RegExp(substring + "$"); + return reg.test(str); +}; + +var breakpointActorIndex = 0; + this.processInput = function (inputstr, frame, script) { var command_func; var command_return; @@ -498,6 +635,240 @@ this.processInput = function (inputstr, frame, script) { return; } +// var testStr = "104:{\ +// \"to\": \"tabThreadActor111\",\ +// \"type\": \"resume\",\ +// \"resumeLimit\": null,\ +// \"pauseOnExceptions\": false\ +// }60:{\ +// \"to\": \"ActionsTest.js_SourceActor\",\ +// \"type\": \"source\"\ +// }"; + + // for (var i = 0; i < testArr.length; ++i) + // { + // dbg.log("split: " +"{"+ testArr[i] + ",length= "+testArr.length); + // } + + var inputArr = inputstr.split(/\d+:{/g); + + if (inputArr.length > 1) + { + inputArr.shift(); + for (var i = 0; i < inputArr.length; ++i) { + // dbg.log("---> "+inputArr[i]); + processInput("{"+inputArr[i], frame, script); + } + return; + } + + // dbg.log("inputStr:"+inputstr); + + if (inputstr === "connected") + { + var rootInit = {from:"root",applicationType:"browser",traits:{sources: true}}; + _RDPWrite(rootInit); + return; + } + + // var semi = inputstr.indexOf(":"); + + // if (semi === -1) + // { + // dbg.log("wrong input remote debugger protocol string."); + // return; + // } + + var jsonStr = inputstr;//.substring(semi+1); + dbg.log("jsonStr:" + jsonStr); + var jsonObj = JSON.parse(jsonStr); + // for (var key in jsonObj) + // { + // dbg.log("["+key+"]="+jsonObj[key]); + // } + + if (jsonObj.to === "root" && jsonObj.type === "listTabs") + { + _RDPWrite({ from:"root", tabs:[{ actor:"JSBTabActor", title:"Hello cocos2d-x JSB", url:"http://www.cocos2d-x.org" }], selected:0 }); + } + else if (jsonObj.to === "JSBTabActor" && jsonObj.type === "attach") + { + _RDPWrite({ from:"JSBTabActor", type:"tabAttached", threadActor:"tabThreadActor111" }); + } + else if (jsonObj.to === "tabThreadActor111" && jsonObj.type === "attach") + { + _RDPWrite( + { + from: "tabThreadActor111", + type: "paused", + actor: "JSBTabActor", + poppedFrames: [], + why: { + type: "attached" + } + }); + } + else if (jsonObj.to === "tabThreadActor111" && jsonObj.type === "sources") + { + addFiles(); + } + else if (jsonObj.to && isStringEndsWith(jsonObj.to, "_SourceActor") && jsonObj.type === "source") + { + dbg.log("require source ...: " + jsonObj.to); + var script = dbg.scriptSourceActorMap[jsonObj.to]; + + if (script) + { + addInitialSource(jsonObj.to, script); + } + } + else if (jsonObj.to && isStringEndsWith(jsonObj.to, "_LongString") && jsonObj.type === "substring") + { + var sourceActor = jsonObj.to.substring(0, jsonObj.to.length-"_LongString".length); + dbg.log("source actor: " + sourceActor); + var script = dbg.scriptSourceActorMap[sourceActor]; + + if (script) + { + addSource(jsonObj.to, script); + } + + _RDPWrite({ + from: "tabThreadActor111", + type: "resumed" + }); + } + else if (jsonObj.to === "tabThreadActor111" && jsonObj.type === "resume") + { + dbg.log("resume type to server...."); + _RDPWrite({ + from: "tabThreadActor111", + type: "resumed" + }); + } + else if (jsonObj.to === "tabThreadActor111" && jsonObj.type === "setBreakpoint") + { + ++breakpointActorIndex; + + var scripts = dbg.scripts[jsonObj.location.url], + tmpScript = null; + + if (scripts) { + var breakLine = jsonObj.location.line, + off = -1; + for (var n=0; n < scripts.length; n++) { + offsets = scripts[n].getLineOffsets(breakLine); + if (offsets.length > 0) { + off = offsets[0]; + tmpScript = scripts[n]; + break; + } + } + if (off >= 0) { + tmpScript.setBreakpoint(off, breakpointHandler); + // return ({commandname : "break", + // success : true, + // jsfilename : md[2], + // breakpointlinenumber : breakLine}); + } else { + // return ({commandname : "break", + // success : false, + // stringResult : "no valid offsets at that line"}); + } + } else { + // return ({commandname : "break", + // success : false, + // jsfilename : md[2], + // stringResult : "Invalid script name"}); + } + + _RDPWrite({ from: "tabThreadActor111", "actor":"breakpointActor"+breakpointActorIndex}); + // jsonObj.location.url + // jsonObj.location.line + } + else if (isStringStartWith(jsonObj.to, "breakpointActor") && jsonObj.type === "delete") + { + _RDPWrite({ from: jsonObj.to }); + } + else if (jsonObj.to === "tabThreadActor111" && jsonObj.type === "frames") + { + dbg.log("sdfsld...."+breakpointFrame.arguments); + + // var arr = breakpointFrame.getOwnPropertyNames(); + // log("names: "+ arr); + + var parentEnv = breakpointFrame.environment.parent; + log("parentEnv:" + parentEnv); + log("parentEnv.type:" + parentEnv.type); + log("parentEnv.actor:" + parentEnv.actor); + log("parentEnv.functionName:" + parentEnv.functionName); + log("parentEnv.object:" + parentEnv.object); + log("parentEnv.object.type:" + parentEnv.object.type); + log("parentEnv.object.class:" + parentEnv.object.class); + var keys = Object.keys(parentEnv.object); + log("keys: " + keys); + + parentEnv = parentEnv.parent; + log("2parentEnv:" + parentEnv); + log("parentEnv.type:" + parentEnv.type); + log("parentEnv.actor:" + parentEnv.actor); + log("parentEnv.functionName:" + parentEnv.functionName); + + var bindings = parentEnv.bindings; + log("bindings:" + bindings); + + var args = bindings.arguments; + log("args:"+args); + + var vars = bindings.variables; + log("vars:" + vars); + + // if (breakpointFrame != null) + { + dbg.log("get frames....."); + var obj = { + "from": "tabThreadActor111", + "frame":{ "actor":"frameActor", "depth":1, + "type":"call", "where":{ "url":"sample.js", "line":3 }, + "environment":{ "type":"function", "actor":"gFrameActor", + "function":{ "type":"object", "class":"Function", "actor":"gActor" }, + "functionName":"g", + "bindings":{ arguments: [ { "y": { "value":"argument to g", "configurable":"false", + "writable":true, "enumerable":true } } ] }, + "parent":{ "type":"function", "actor":"fFrameActor", + "function":{ "type":"object", "class":"Function", "actor":"fActor" }, + "functionName":"f", + "bindings": { arguments: [ { "x": { "value":"argument to f", "configurable":"false", + "writable":true, "enumerable":true } } ], + variables: { "z": { "value":"value of z", "configurable":"false", + "writable":true, "enumerable":true } } }, + "parent":{ "type":"object", "actor":"globalCodeActor", + "object":{ "type":"object", "class":"Global", + "actor":"globalObjectActor" } + } + } + }, + "callee":"gActor", "calleeName":"g", + "this":{ "type":"object", "class":"Function", "actor":"gActor" }, + "arguments":["argument to g"] + }}; + + _RDPWrite(obj); + + breakpointFrame = null; + } + } + else if (jsonObj.type === "interrupt") + { + _RDPWrite({ + from: "tabThreadActor111", + type: "resumed" + }); + } + + + + return; // remove Carriage Return's inputstr = inputstr.replace(/\r+/, ""); @@ -570,9 +941,15 @@ _printHelp = function() { dbg.scripts = []; +_RDPWrite = function(jsonObj){ + var buf = JSON.stringify(jsonObj); + _bufferWrite("" + buf.length + ":" + buf); +}; + dbg.onNewScript = function (script) { + dbg.log("onNewScript, "+script.url); // skip if the url is this script - var last = script.url.split("/").pop(); + // var last = script.url.split("/").pop(); var children = script.getChildScripts(), arr = [script].concat(children); @@ -584,9 +961,13 @@ dbg.onNewScript = function (script) { var offsets = arr[i].getLineOffsets(j); dbg.log(" off: " + offsets.join(",") + "; line: " + j); } - } - */ - dbg.scripts[last] = arr; + }*/ + + dbg.scripts["file://"+script.url] = arr; + + // dbg.log("source: "+script.source.text); + + }; dbg.onError = function (frame, report) { @@ -596,6 +977,11 @@ dbg.onError = function (frame, report) { dbg.log("!! exception"); }; +dbg.onDebuggerStatement = function(frame) +{ + dbg.log("onDebuggerStatement..."); +}; + this._prepareDebugger = function (global) { var tmp = new Debugger(global); tmp.onNewScript = dbg.onNewScript; From 226ff2bbb902b1045d58f43fb6c8bf48eeef0cfa Mon Sep 17 00:00:00 2001 From: James Chen Date: Mon, 7 Oct 2013 17:19:00 +0800 Subject: [PATCH 04/29] issue #2823: firefox debugger step ok. --- cocos2d_libs.xcodeproj/project.pbxproj | 40 +- cocos2dx/platform/CCFileUtils.cpp | 1 - .../TestJavascript/Classes/AppDelegate.cpp | 5 +- samples/samples.xcodeproj/project.pbxproj | 353 +- .../javascript/bindings/ScriptingCore.cpp | 145 +- .../javascript/bindings/cocos2d_specifics.cpp | 9 +- .../bindings/js/debugger/DevToolsUtils.js | 74 + .../bindings/js/debugger/actors/gcli.js | 77 + .../bindings/js/debugger/actors/inspector.js | 2100 ++++++++++++ .../bindings/js/debugger/actors/profiler.js | 218 ++ .../bindings/js/debugger/actors/root.js | 329 ++ .../bindings/js/debugger/actors/script.js | 2946 +++++++++++++++++ .../bindings/js/debugger/actors/string.js | 145 + .../js/debugger/actors/styleeditor.js | 740 +++++ .../bindings/js/debugger/actors/webapps.js | 397 +++ .../bindings/js/debugger/actors/webbrowser.js | 866 +++++ .../bindings/js/debugger/actors/webconsole.js | 1555 +++++++++ .../bindings/js/debugger/core/promise.js | 294 ++ .../js/debugger/jsb-tests/test_dbgglobal.js | 82 + .../js/debugger/jsb-tests/testactors.js | 98 + .../javascript/bindings/js/debugger/main.js | 921 ++++++ .../bindings/js/debugger/protocol.js | 1219 +++++++ .../js/debugger/tests/mochitest/Makefile.in | 31 + .../tests/mochitest/inspector-helpers.js | 293 ++ .../mochitest/inspector-traversal-data.html | 54 + .../js/debugger/tests/mochitest/moz.build | 5 + .../nonchrome_unsafeDereference.html | 8 + .../mochitest/test_inspector-changeattrs.html | 102 + .../mochitest/test_inspector-changevalue.html | 84 + .../test_inspector-mutations-attr.html | 127 + .../test_inspector-mutations-childlist.html | 313 ++ .../test_inspector-mutations-frameload.html | 217 ++ .../test_inspector-mutations-value.html | 151 + .../test_inspector-pseudoclass-lock.html | 177 + .../mochitest/test_inspector-release.html | 94 + .../mochitest/test_inspector-retain.html | 183 + .../mochitest/test_inspector-traversal.html | 332 ++ .../mochitest/test_unsafeDereference.html | 52 + .../bindings/js/debugger/tests/moz.build | 11 + .../js/debugger/tests/unit/head_dbg.js | 328 ++ .../tests/unit/post_init_global_actors.js | 17 + .../tests/unit/post_init_tab_actors.js | 17 + .../tests/unit/pre_init_global_actors.js | 17 + .../tests/unit/pre_init_tab_actors.js | 17 + .../tests/unit/registertestactors-01.js | 15 + .../tests/unit/registertestactors-02.js | 13 + .../unit/source-map-data/sourcemapped.coffee | 6 + .../unit/source-map-data/sourcemapped.map | 10 + .../js/debugger/tests/unit/sourcemapped.js | 16 + .../js/debugger/tests/unit/test_add_actors.js | 87 + .../js/debugger/tests/unit/test_attach.js | 37 + .../tests/unit/test_blackboxing-01.js | 178 + .../tests/unit/test_blackboxing-02.js | 104 + .../tests/unit/test_blackboxing-03.js | 104 + .../tests/unit/test_blackboxing-04.js | 79 + .../tests/unit/test_blackboxing-05.js | 79 + .../debugger/tests/unit/test_breakpoint-01.js | 62 + .../debugger/tests/unit/test_breakpoint-02.js | 50 + .../debugger/tests/unit/test_breakpoint-03.js | 68 + .../debugger/tests/unit/test_breakpoint-04.js | 67 + .../debugger/tests/unit/test_breakpoint-05.js | 70 + .../debugger/tests/unit/test_breakpoint-06.js | 76 + .../debugger/tests/unit/test_breakpoint-07.js | 73 + .../debugger/tests/unit/test_breakpoint-08.js | 71 + .../debugger/tests/unit/test_breakpoint-09.js | 77 + .../debugger/tests/unit/test_breakpoint-10.js | 77 + .../debugger/tests/unit/test_breakpoint-11.js | 76 + .../debugger/tests/unit/test_breakpoint-12.js | 99 + .../tests/unit/test_breakpointstore.js | 21 + .../js/debugger/tests/unit/test_dbgactor.js | 118 + .../unit/test_dbgclient_debuggerstatement.js | 74 + .../js/debugger/tests/unit/test_dbgglobal.js | 65 + .../js/debugger/tests/unit/test_dbgsocket.js | 164 + .../js/debugger/tests/unit/test_eval-01.js | 58 + .../js/debugger/tests/unit/test_eval-02.js | 48 + .../js/debugger/tests/unit/test_eval-03.js | 50 + .../js/debugger/tests/unit/test_eval-04.js | 69 + .../js/debugger/tests/unit/test_eval-05.js | 54 + .../debugger/tests/unit/test_frameactor-01.js | 44 + .../debugger/tests/unit/test_frameactor-02.js | 46 + .../debugger/tests/unit/test_frameactor-03.js | 48 + .../debugger/tests/unit/test_frameactor-04.js | 92 + .../debugger/tests/unit/test_frameactor-05.js | 90 + .../tests/unit/test_framearguments-01.js | 51 + .../tests/unit/test_framebindings-01.js | 77 + .../tests/unit/test_framebindings-02.js | 62 + .../tests/unit/test_framebindings-03.js | 69 + .../tests/unit/test_framebindings-04.js | 84 + .../tests/unit/test_framebindings-05.js | 62 + .../tests/unit/test_framebindings-06.js | 58 + .../tests/unit/test_frameclient-01.js | 54 + .../tests/unit/test_frameclient-02.js | 47 + .../tests/unit/test_functiongrips-01.js | 94 + .../tests/unit/test_getyoungestframe.js | 30 + .../js/debugger/tests/unit/test_interrupt.js | 50 + .../tests/unit/test_listsources-01.js | 58 + .../tests/unit/test_listsources-02.js | 52 + .../tests/unit/test_listsources-03.js | 52 + .../tests/unit/test_longstringactor.js | 107 + .../tests/unit/test_longstringgrips-01.js | 71 + .../tests/unit/test_longstringgrips-02.js | 60 + .../tests/unit/test_nativewrappers.js | 30 + .../debugger/tests/unit/test_new_source-01.js | 40 + .../debugger/tests/unit/test_nsjsinspector.js | 59 + .../tests/unit/test_objectgrips-01.js | 49 + .../tests/unit/test_objectgrips-02.js | 56 + .../tests/unit/test_objectgrips-03.js | 64 + .../tests/unit/test_objectgrips-04.js | 67 + .../tests/unit/test_objectgrips-05.js | 57 + .../tests/unit/test_objectgrips-06.js | 57 + .../tests/unit/test_objectgrips-07.js | 65 + .../tests/unit/test_pause_exceptions-01.js | 51 + .../tests/unit/test_pause_exceptions-02.js | 48 + .../tests/unit/test_pauselifetime-01.js | 55 + .../tests/unit/test_pauselifetime-02.js | 57 + .../tests/unit/test_pauselifetime-03.js | 62 + .../tests/unit/test_pauselifetime-04.js | 49 + .../tests/unit/test_profiler_activation.js | 66 + .../tests/unit/test_profiler_actor.js | 168 + .../tests/unit/test_protocol_children.js | 434 +++ .../tests/unit/test_protocol_longstring.js | 207 ++ .../tests/unit/test_protocol_simple.js | 276 ++ .../tests/unit/test_register_actor.js | 43 + .../js/debugger/tests/unit/test_source-01.js | 72 + .../debugger/tests/unit/test_sourcemaps-01.js | 64 + .../debugger/tests/unit/test_sourcemaps-02.js | 69 + .../debugger/tests/unit/test_sourcemaps-03.js | 149 + .../debugger/tests/unit/test_sourcemaps-04.js | 57 + .../debugger/tests/unit/test_sourcemaps-05.js | 57 + .../debugger/tests/unit/test_sourcemaps-06.js | 86 + .../debugger/tests/unit/test_sourcemaps-07.js | 67 + .../debugger/tests/unit/test_sourcemaps-08.js | 52 + .../unit/test_sources_backwards_compat-01.js | 77 + .../unit/test_sources_backwards_compat-02.js | 54 + .../debugger/tests/unit/test_stepping-01.js | 74 + .../debugger/tests/unit/test_stepping-02.js | 74 + .../debugger/tests/unit/test_stepping-03.js | 53 + .../debugger/tests/unit/test_stepping-04.js | 65 + .../debugger/tests/unit/test_stepping-05.js | 92 + .../debugger/tests/unit/test_stepping-06.js | 85 + .../tests/unit/test_threadlifetime-01.js | 59 + .../tests/unit/test_threadlifetime-02.js | 60 + .../tests/unit/test_threadlifetime-03.js | 82 + .../tests/unit/test_threadlifetime-04.js | 54 + .../tests/unit/test_threadlifetime-05.js | 84 + .../tests/unit/test_threadlifetime-06.js | 72 + .../tests/unit/test_unsafeDereference.js | 134 + .../js/debugger/tests/unit/testactors.js | 98 + .../debugger/tests/unit/testcompatactors.js | 119 + .../js/debugger/tests/unit/xpcshell.ini | 144 + .../bindings/js/debugger/transport.js | 285 ++ .../javascript/bindings/js/jsb_debugger.js | 154 +- 152 files changed, 23131 insertions(+), 538 deletions(-) create mode 100644 scripting/javascript/bindings/js/debugger/DevToolsUtils.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/gcli.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/inspector.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/profiler.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/root.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/script.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/string.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/styleeditor.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/webapps.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/webbrowser.js create mode 100644 scripting/javascript/bindings/js/debugger/actors/webconsole.js create mode 100644 scripting/javascript/bindings/js/debugger/core/promise.js create mode 100644 scripting/javascript/bindings/js/debugger/jsb-tests/test_dbgglobal.js create mode 100644 scripting/javascript/bindings/js/debugger/jsb-tests/testactors.js create mode 100644 scripting/javascript/bindings/js/debugger/main.js create mode 100644 scripting/javascript/bindings/js/debugger/protocol.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/Makefile.in create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/inspector-helpers.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/inspector-traversal-data.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/moz.build create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/nonchrome_unsafeDereference.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-changeattrs.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-changevalue.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-mutations-attr.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-mutations-childlist.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-mutations-frameload.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-mutations-value.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-pseudoclass-lock.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-release.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-retain.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_inspector-traversal.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/mochitest/test_unsafeDereference.html create mode 100644 scripting/javascript/bindings/js/debugger/tests/moz.build create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/head_dbg.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/post_init_global_actors.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/post_init_tab_actors.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/pre_init_global_actors.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/pre_init_tab_actors.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/registertestactors-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/registertestactors-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/source-map-data/sourcemapped.coffee create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/source-map-data/sourcemapped.map create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/sourcemapped.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_add_actors.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_attach.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_blackboxing-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_blackboxing-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_blackboxing-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_blackboxing-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_blackboxing-05.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-05.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-06.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-07.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-08.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-09.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-10.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-11.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpoint-12.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_breakpointstore.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_dbgactor.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_dbgclient_debuggerstatement.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_dbgglobal.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_dbgsocket.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_eval-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_eval-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_eval-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_eval-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_eval-05.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_frameactor-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_frameactor-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_frameactor-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_frameactor-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_frameactor-05.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_framearguments-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_framebindings-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_framebindings-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_framebindings-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_framebindings-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_framebindings-05.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_framebindings-06.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_frameclient-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_frameclient-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_functiongrips-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_getyoungestframe.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_interrupt.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_listsources-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_listsources-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_listsources-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_longstringactor.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_longstringgrips-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_longstringgrips-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_nativewrappers.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_new_source-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_nsjsinspector.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_objectgrips-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_objectgrips-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_objectgrips-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_objectgrips-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_objectgrips-05.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_objectgrips-06.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_objectgrips-07.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_pause_exceptions-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_pause_exceptions-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_pauselifetime-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_pauselifetime-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_pauselifetime-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_pauselifetime-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_profiler_activation.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_profiler_actor.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_protocol_children.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_protocol_longstring.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_protocol_simple.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_register_actor.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_source-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sourcemaps-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sourcemaps-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sourcemaps-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sourcemaps-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sourcemaps-05.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sourcemaps-06.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sourcemaps-07.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sourcemaps-08.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sources_backwards_compat-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_sources_backwards_compat-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_stepping-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_stepping-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_stepping-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_stepping-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_stepping-05.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_stepping-06.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_threadlifetime-01.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_threadlifetime-02.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_threadlifetime-03.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_threadlifetime-04.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_threadlifetime-05.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_threadlifetime-06.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/test_unsafeDereference.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/testactors.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/testcompatactors.js create mode 100644 scripting/javascript/bindings/js/debugger/tests/unit/xpcshell.ini create mode 100644 scripting/javascript/bindings/js/debugger/transport.js diff --git a/cocos2d_libs.xcodeproj/project.pbxproj b/cocos2d_libs.xcodeproj/project.pbxproj index 3959af89b6bb..8ad5afb13501 100644 --- a/cocos2d_libs.xcodeproj/project.pbxproj +++ b/cocos2d_libs.xcodeproj/project.pbxproj @@ -116,8 +116,6 @@ 1A11982A1785388100D62A44 /* tolua_map.c in Sources */ = {isa = PBXBuildFile; fileRef = 1A1196981785201F00D62A44 /* tolua_map.c */; }; 1A11982B1785388100D62A44 /* tolua_push.c in Sources */ = {isa = PBXBuildFile; fileRef = 1A1196991785201F00D62A44 /* tolua_push.c */; }; 1A11982C1785388100D62A44 /* tolua_to.c in Sources */ = {isa = PBXBuildFile; fileRef = 1A11969A1785201F00D62A44 /* tolua_to.c */; }; - 1A2E82381793EDB70058E421 /* jsb_deprecated.js in Sources */ = {isa = PBXBuildFile; fileRef = 1A2E82371793EDB70058E421 /* jsb_deprecated.js */; }; - 1A2E82391793EDB70058E421 /* jsb_deprecated.js in Sources */ = {isa = PBXBuildFile; fileRef = 1A2E82371793EDB70058E421 /* jsb_deprecated.js */; }; 1A50FEA517A0C89300A422C8 /* CCDeprecated-ext.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A50FEA417A0C89300A422C8 /* CCDeprecated-ext.h */; }; 1A50FEA617A0C89300A422C8 /* CCDeprecated-ext.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A50FEA417A0C89300A422C8 /* CCDeprecated-ext.h */; }; 1A6CC67B17E07C89006DEE69 /* CCEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A6CC66117E07C89006DEE69 /* CCEvent.cpp */; }; @@ -2366,7 +2364,7 @@ 1A1196991785201F00D62A44 /* tolua_push.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tolua_push.c; sourceTree = ""; }; 1A11969A1785201F00D62A44 /* tolua_to.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tolua_to.c; sourceTree = ""; }; 1A119791178526AA00D62A44 /* libluabindings iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libluabindings iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 1A2E82371793EDB70058E421 /* jsb_deprecated.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_deprecated.js; sourceTree = ""; }; + 1A2B004118025936009E8C43 /* js */ = {isa = PBXFileReference; lastKnownFileType = folder; path = js; sourceTree = ""; }; 1A50FEA417A0C89300A422C8 /* CCDeprecated-ext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CCDeprecated-ext.h"; sourceTree = ""; }; 1A6CC66117E07C89006DEE69 /* CCEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CCEvent.cpp; sourceTree = ""; }; 1A6CC66217E07C89006DEE69 /* CCEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCEvent.h; sourceTree = ""; }; @@ -3138,18 +3136,6 @@ A03F320617814803006731B9 /* cocos2d_specifics.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cocos2d_specifics.hpp; sourceTree = ""; }; A03F320717814803006731B9 /* cocosjs_manual_conversions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cocosjs_manual_conversions.cpp; sourceTree = ""; }; A03F320817814803006731B9 /* cocosjs_manual_conversions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cocosjs_manual_conversions.h; sourceTree = ""; }; - A03F321217814803006731B9 /* jsb.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb.js; sourceTree = ""; }; - A03F321317814803006731B9 /* jsb_chipmunk.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_chipmunk.js; sourceTree = ""; }; - A03F321417814803006731B9 /* jsb_chipmunk_constants.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_chipmunk_constants.js; sourceTree = ""; }; - A03F321517814803006731B9 /* jsb_cocos2d.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_cocos2d.js; sourceTree = ""; }; - A03F321617814803006731B9 /* jsb_cocos2d_constants.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_cocos2d_constants.js; sourceTree = ""; }; - A03F321717814803006731B9 /* jsb_cocos2d_extension.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_cocos2d_extension.js; sourceTree = ""; }; - A03F321817814803006731B9 /* jsb_cocosbuilder.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_cocosbuilder.js; sourceTree = ""; }; - A03F321917814803006731B9 /* jsb_debugger.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_debugger.js; sourceTree = ""; }; - A03F321A17814803006731B9 /* jsb_opengl.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_opengl.js; sourceTree = ""; }; - A03F321B17814803006731B9 /* jsb_opengl_constants.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_opengl_constants.js; sourceTree = ""; }; - A03F321C17814803006731B9 /* jsb_sys.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_sys.js; sourceTree = ""; }; - A03F321D17814803006731B9 /* main.debug.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = main.debug.js; sourceTree = ""; }; A03F321E17814803006731B9 /* js_bindings_ccbreader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_bindings_ccbreader.cpp; sourceTree = ""; }; A03F321F17814803006731B9 /* js_bindings_ccbreader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = js_bindings_ccbreader.h; sourceTree = ""; }; A03F322017814803006731B9 /* js_bindings_chipmunk_auto_classes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = js_bindings_chipmunk_auto_classes.cpp; sourceTree = ""; }; @@ -5438,12 +5424,12 @@ A03F320317814803006731B9 /* bindings */ = { isa = PBXGroup; children = ( + 1A2B004118025936009E8C43 /* js */, A03F320417814803006731B9 /* Android.mk */, A03F320517814803006731B9 /* cocos2d_specifics.cpp */, A03F320617814803006731B9 /* cocos2d_specifics.hpp */, A03F320717814803006731B9 /* cocosjs_manual_conversions.cpp */, A03F320817814803006731B9 /* cocosjs_manual_conversions.h */, - A03F321117814803006731B9 /* js */, A03F321E17814803006731B9 /* js_bindings_ccbreader.cpp */, A03F321F17814803006731B9 /* js_bindings_ccbreader.h */, A03F322017814803006731B9 /* js_bindings_chipmunk_auto_classes.cpp */, @@ -5489,26 +5475,6 @@ path = bindings; sourceTree = ""; }; - A03F321117814803006731B9 /* js */ = { - isa = PBXGroup; - children = ( - A03F321217814803006731B9 /* jsb.js */, - A03F321317814803006731B9 /* jsb_chipmunk.js */, - A03F321417814803006731B9 /* jsb_chipmunk_constants.js */, - A03F321517814803006731B9 /* jsb_cocos2d.js */, - A03F321617814803006731B9 /* jsb_cocos2d_constants.js */, - A03F321717814803006731B9 /* jsb_cocos2d_extension.js */, - A03F321817814803006731B9 /* jsb_cocosbuilder.js */, - A03F321917814803006731B9 /* jsb_debugger.js */, - A03F321A17814803006731B9 /* jsb_opengl.js */, - A03F321B17814803006731B9 /* jsb_opengl_constants.js */, - A03F321C17814803006731B9 /* jsb_sys.js */, - A03F321D17814803006731B9 /* main.debug.js */, - 1A2E82371793EDB70058E421 /* jsb_deprecated.js */, - ); - path = js; - sourceTree = ""; - }; A03F324117814803006731B9 /* obfuscate */ = { isa = PBXGroup; children = ( @@ -8409,7 +8375,6 @@ A03F33BD17814804006731B9 /* jsb_websocket.cpp in Sources */, A03F33C317814804006731B9 /* ScriptingCore.cpp in Sources */, A03F33C617814804006731B9 /* XMLHTTPRequest.cpp in Sources */, - 1A2E82381793EDB70058E421 /* jsb_deprecated.js in Sources */, 1ADB26ED17CC92D200634B5E /* jsb_cocos2dx_auto.cpp in Sources */, 1ADB26F917CC92D200634B5E /* jsb_cocos2dx_extension_auto.cpp in Sources */, ); @@ -8851,7 +8816,6 @@ A07A4FC9178387750073F6A7 /* jsb_websocket.cpp in Sources */, A07A4FCA178387750073F6A7 /* ScriptingCore.cpp in Sources */, A07A4FCB178387750073F6A7 /* XMLHTTPRequest.cpp in Sources */, - 1A2E82391793EDB70058E421 /* jsb_deprecated.js in Sources */, 1ADB26EF17CC92D200634B5E /* jsb_cocos2dx_auto.cpp in Sources */, 1ADB26FB17CC92D200634B5E /* jsb_cocos2dx_extension_auto.cpp in Sources */, ); diff --git a/cocos2dx/platform/CCFileUtils.cpp b/cocos2dx/platform/CCFileUtils.cpp index 6f286607fbbf..0db8c28f1ad4 100644 --- a/cocos2dx/platform/CCFileUtils.cpp +++ b/cocos2dx/platform/CCFileUtils.cpp @@ -618,7 +618,6 @@ std::string FileUtils::fullPathForFilename(const std::string &filename) for (auto searchIt = _searchPathArray.begin(); searchIt != _searchPathArray.end(); ++searchIt) { for (auto resolutionIt = _searchResolutionsOrderArray.begin(); resolutionIt != _searchResolutionsOrderArray.end(); ++resolutionIt) { - fullpath = this->getPathForFilename(newFilename, *resolutionIt, *searchIt); if (fullpath.length() > 0) diff --git a/samples/Javascript/TestJavascript/Classes/AppDelegate.cpp b/samples/Javascript/TestJavascript/Classes/AppDelegate.cpp index 4b7b6c87ffab..fc44e06b9bba 100644 --- a/samples/Javascript/TestJavascript/Classes/AppDelegate.cpp +++ b/samples/Javascript/TestJavascript/Classes/AppDelegate.cpp @@ -41,6 +41,9 @@ bool AppDelegate::applicationDidFinishLaunching() // set FPS. the default value is 1.0/60 if you don't call this pDirector->setAnimationInterval(1.0 / 60); + FileUtils::getInstance()->addSearchPath("res"); + FileUtils::getInstance()->addSearchPath("js"); + ScriptingCore* sc = ScriptingCore::getInstance(); sc->addRegisterCallback(register_all_cocos2dx); sc->addRegisterCallback(register_all_cocos2dx_extension); @@ -54,8 +57,6 @@ bool AppDelegate::applicationDidFinishLaunching() sc->addRegisterCallback(register_CCBuilderReader); sc->start(); - - FileUtils::getInstance()->addSearchPath("res"); auto pEngine = ScriptingCore::getInstance(); ScriptEngineManager::getInstance()->setScriptEngine(pEngine); diff --git a/samples/samples.xcodeproj/project.pbxproj b/samples/samples.xcodeproj/project.pbxproj index 381c282bed40..0017c1d76ce4 100644 --- a/samples/samples.xcodeproj/project.pbxproj +++ b/samples/samples.xcodeproj/project.pbxproj @@ -143,18 +143,8 @@ 1A119CB41785480400D62A44 /* Opengl.lua in Resources */ = {isa = PBXBuildFile; fileRef = 1A119C9117853D8D00D62A44 /* Opengl.lua */; }; 1A119CB51785480400D62A44 /* OpenglConstants.lua in Resources */ = {isa = PBXBuildFile; fileRef = 1A119C9217853D8D00D62A44 /* OpenglConstants.lua */; }; 1A219E2617E8A2CD00EE4980 /* KeyboardTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A0359A4017821D9B00987F6C /* KeyboardTest.cpp */; }; - 1A2E823B1793EED50058E421 /* jsb_deprecated.js in Sources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E823C1793EED50058E421 /* jsb_deprecated.js in Sources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E823F1793EEF50058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E82401793EF260058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E824517952E8D0058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E824817952E9A0058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E824917952EAC0058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E824A17952EB70058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E824B17952EC50058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E824C17952ECE0058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E824D17952ED70058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; - 1A2E824E17952EDF0058E421 /* jsb_deprecated.js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2E823A1793EED50058E421 /* jsb_deprecated.js */; }; + 1A2B004518025A4F009E8C43 /* js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2B004418025A4F009E8C43 /* js */; }; + 1A2B004618025A4F009E8C43 /* js in Resources */ = {isa = PBXBuildFile; fileRef = 1A2B004418025A4F009E8C43 /* js */; }; 1A33319A17E2DFEA00ED3802 /* NewEventDispatcherTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A33319817E2DFEA00ED3802 /* NewEventDispatcherTest.cpp */; }; 1A33319B17E2DFEA00ED3802 /* NewEventDispatcherTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A33319817E2DFEA00ED3802 /* NewEventDispatcherTest.cpp */; }; 1A6FB4DE17854A6A00CDF010 /* animations in Resources */ = {isa = PBXBuildFile; fileRef = 1A6FB4C417854A6A00CDF010 /* animations */; }; @@ -474,30 +464,6 @@ A01E17CE1784C1A700B0CA4A /* game.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A5F61782296200987F6C /* game.js */; }; A01E17CF1784C1AB00B0CA4A /* obfuscate.xml in Resources */ = {isa = PBXBuildFile; fileRef = A035A5F71782296200987F6C /* obfuscate.xml */; }; A01E17D01784C23400B0CA4A /* libjsbindings iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510717839B9C0073F6A7 /* libjsbindings iOS.a */; }; - A01E17D51784C28700B0CA4A /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A01E17D61784C28700B0CA4A /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A01E17D71784C28700B0CA4A /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A01E17D81784C28700B0CA4A /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A01E17D91784C28700B0CA4A /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A01E17DA1784C28700B0CA4A /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A01E17DB1784C28700B0CA4A /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A01E17DC1784C28700B0CA4A /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A01E17DD1784C28700B0CA4A /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A01E17DE1784C28700B0CA4A /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A01E17DF1784C28700B0CA4A /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A01E17E01784C28700B0CA4A /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; - A01E17F51784C45400B0CA4A /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A01E17F61784C45400B0CA4A /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A01E17F71784C45400B0CA4A /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A01E17F81784C45400B0CA4A /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A01E17F91784C45400B0CA4A /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A01E17FA1784C45400B0CA4A /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A01E17FB1784C45400B0CA4A /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A01E17FC1784C45400B0CA4A /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A01E17FD1784C45400B0CA4A /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A01E17FE1784C45400B0CA4A /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A01E17FF1784C45400B0CA4A /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A01E18001784C45400B0CA4A /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A01E18441784C45400B0CA4A /* libjsbindings iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510717839B9C0073F6A7 /* libjsbindings iOS.a */; }; A01E18451784C45400B0CA4A /* libbox2d iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510317839B9C0073F6A7 /* libbox2d iOS.a */; }; A01E18461784C45400B0CA4A /* libchipmunk iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510117839B9C0073F6A7 /* libchipmunk iOS.a */; }; @@ -513,18 +479,6 @@ A01E18511784C45400B0CA4A /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A52B91783AE900073F6A7 /* OpenGLES.framework */; }; A01E18521784C45400B0CA4A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A52B71783AE6D0073F6A7 /* UIKit.framework */; }; A01E18531784C45400B0CA4A /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A52C11783B01F0073F6A7 /* AVFoundation.framework */; }; - A01E18671784C4D600B0CA4A /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A01E18681784C4D600B0CA4A /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A01E18691784C4D600B0CA4A /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A01E186A1784C4D600B0CA4A /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A01E186B1784C4D600B0CA4A /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A01E186C1784C4D600B0CA4A /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A01E186D1784C4D600B0CA4A /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A01E186E1784C4D600B0CA4A /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A01E186F1784C4D600B0CA4A /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A01E18701784C4D600B0CA4A /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A01E18711784C4D600B0CA4A /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A01E18721784C4D600B0CA4A /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A01E18791784C4D600B0CA4A /* libjsbindings iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510717839B9C0073F6A7 /* libjsbindings iOS.a */; }; A01E187A1784C4D600B0CA4A /* libbox2d iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510317839B9C0073F6A7 /* libbox2d iOS.a */; }; A01E187B1784C4D600B0CA4A /* libchipmunk iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510117839B9C0073F6A7 /* libchipmunk iOS.a */; }; @@ -540,18 +494,6 @@ A01E18861784C4D600B0CA4A /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A52B91783AE900073F6A7 /* OpenGLES.framework */; }; A01E18871784C4D600B0CA4A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A52B71783AE6D0073F6A7 /* UIKit.framework */; }; A01E18881784C4D600B0CA4A /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A52C11783B01F0073F6A7 /* AVFoundation.framework */; }; - A01E189C1784C4DC00B0CA4A /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A01E189D1784C4DC00B0CA4A /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A01E189E1784C4DC00B0CA4A /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A01E189F1784C4DC00B0CA4A /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A01E18A01784C4DC00B0CA4A /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A01E18A11784C4DC00B0CA4A /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A01E18A21784C4DC00B0CA4A /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A01E18A31784C4DC00B0CA4A /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A01E18A41784C4DC00B0CA4A /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A01E18A51784C4DC00B0CA4A /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A01E18A61784C4DC00B0CA4A /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A01E18A71784C4DC00B0CA4A /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A01E18AE1784C4DC00B0CA4A /* libjsbindings iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510717839B9C0073F6A7 /* libjsbindings iOS.a */; }; A01E18AF1784C4DC00B0CA4A /* libbox2d iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510317839B9C0073F6A7 /* libbox2d iOS.a */; }; A01E18B01784C4DC00B0CA4A /* libchipmunk iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510117839B9C0073F6A7 /* libchipmunk iOS.a */; }; @@ -567,18 +509,6 @@ A01E18BB1784C4DC00B0CA4A /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A52B91783AE900073F6A7 /* OpenGLES.framework */; }; A01E18BC1784C4DC00B0CA4A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A52B71783AE6D0073F6A7 /* UIKit.framework */; }; A01E18BD1784C4DC00B0CA4A /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A52C11783B01F0073F6A7 /* AVFoundation.framework */; }; - A01E18D11784C4E000B0CA4A /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A01E18D21784C4E000B0CA4A /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A01E18D31784C4E000B0CA4A /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A01E18D41784C4E000B0CA4A /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A01E18D51784C4E000B0CA4A /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A01E18D61784C4E000B0CA4A /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A01E18D71784C4E000B0CA4A /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A01E18D81784C4E000B0CA4A /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A01E18D91784C4E000B0CA4A /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A01E18DA1784C4E000B0CA4A /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A01E18DB1784C4E000B0CA4A /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A01E18DC1784C4E000B0CA4A /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A01E18E31784C4E000B0CA4A /* libjsbindings iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510717839B9C0073F6A7 /* libjsbindings iOS.a */; }; A01E18E41784C4E000B0CA4A /* libbox2d iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510317839B9C0073F6A7 /* libbox2d iOS.a */; }; A01E18E51784C4E000B0CA4A /* libchipmunk iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A07A510117839B9C0073F6A7 /* libchipmunk iOS.a */; }; @@ -661,18 +591,6 @@ A01E19901784C88400B0CA4A /* RootViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A01E19851784C88400B0CA4A /* RootViewController.mm */; }; A01E19941784C89300B0CA4A /* Published files iOS in Resources */ = {isa = PBXBuildFile; fileRef = A035ACB8178245FD00987F6C /* Published files iOS */; }; A01E19951784C89E00B0CA4A /* AppDelegate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A035AC3D178245AA00987F6C /* AppDelegate.cpp */; }; - A01E19AC1784DA0600B0CA4A /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A01E19AD1784DA0600B0CA4A /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A01E19AE1784DA0600B0CA4A /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A01E19AF1784DA0600B0CA4A /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A01E19B01784DA0600B0CA4A /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A01E19B11784DA0600B0CA4A /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A01E19B21784DA0600B0CA4A /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A01E19B31784DA0600B0CA4A /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A01E19B41784DA0600B0CA4A /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A01E19B51784DA0600B0CA4A /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A01E19B61784DA0600B0CA4A /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A01E19B71784DA0600B0CA4A /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A01E19BF1784DA0600B0CA4A /* game.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A5F61782296200987F6C /* game.js */; }; A01E19C01784DA0600B0CA4A /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A01E17771784C0DF00B0CA4A /* Default-568h@2x.png */; }; A01E19C11784DA0600B0CA4A /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = A01E17781784C0DF00B0CA4A /* Default.png */; }; @@ -713,18 +631,6 @@ A01E1A281784DC4000B0CA4A /* tests-boot-jsb-for-obfuscation.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6B6178229E400987F6C /* tests-boot-jsb-for-obfuscation.js */; }; A01E1A291784DC4000B0CA4A /* tests-boot-jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6B7178229E400987F6C /* tests-boot-jsb.js */; }; A01E1A2A1784DC4000B0CA4A /* tests-main.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6B8178229E400987F6C /* tests-main.js */; }; - A01E1A2B1784DC4000B0CA4A /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A01E1A2C1784DC4000B0CA4A /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A01E1A2D1784DC4000B0CA4A /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A01E1A2E1784DC4000B0CA4A /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A01E1A2F1784DC4000B0CA4A /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A01E1A301784DC4000B0CA4A /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A01E1A311784DC4000B0CA4A /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A01E1A321784DC4000B0CA4A /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A01E1A331784DC4000B0CA4A /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A01E1A341784DC4000B0CA4A /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A01E1A351784DC4000B0CA4A /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A01E1A361784DC4000B0CA4A /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A01E1A371784DC4000B0CA4A /* obfuscate.xml in Resources */ = {isa = PBXBuildFile; fileRef = A035A5F71782296200987F6C /* obfuscate.xml */; }; A01E1A381784DC4000B0CA4A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A035A6351782296200987F6C /* InfoPlist.strings */; }; A01E1A391784DC4000B0CA4A /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A035A6371782296200987F6C /* MainMenu.xib */; }; @@ -946,31 +852,7 @@ A035A6F1178229E400987F6C /* TransitionsTest in Resources */ = {isa = PBXBuildFile; fileRef = A035A6BD178229E400987F6C /* TransitionsTest */; }; A035A6F2178229E400987F6C /* UnitTest in Resources */ = {isa = PBXBuildFile; fileRef = A035A6BE178229E400987F6C /* UnitTest */; }; A035A6F3178229E400987F6C /* XHRTest in Resources */ = {isa = PBXBuildFile; fileRef = A035A6BF178229E400987F6C /* XHRTest */; }; - A035A70217822A8E00987F6C /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A035A70317822A8E00987F6C /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A035A70417822A8E00987F6C /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A035A70517822A8E00987F6C /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A035A70617822A8E00987F6C /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A035A70717822A8E00987F6C /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A035A70817822A8E00987F6C /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A035A70917822A8E00987F6C /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A035A70A17822A8E00987F6C /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A035A70B17822A8E00987F6C /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A035A70C17822A8E00987F6C /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A035A70D17822A8E00987F6C /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A035A71217822E9E00987F6C /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A035A71117822E9E00987F6C /* libsqlite3.dylib */; }; - A035A7291782301C00987F6C /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A035A72A1782301C00987F6C /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A035A72B1782301C00987F6C /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A035A72C1782301C00987F6C /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A035A72D1782301C00987F6C /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A035A72E1782301C00987F6C /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A035A72F1782301C00987F6C /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A035A7301782301C00987F6C /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A035A7311782301C00987F6C /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A035A7321782301C00987F6C /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A035A7331782301C00987F6C /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A035A7341782301C00987F6C /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A035A7701782301C00987F6C /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A035A71117822E9E00987F6C /* libsqlite3.dylib */; }; A035A7711782301C00987F6C /* libcurl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A9F808C177E98A600D9A1CB /* libcurl.dylib */; }; A035A7721782301C00987F6C /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 15C6482E165F399D007D4F18 /* libz.dylib */; }; @@ -995,18 +877,6 @@ A035A9B21782355600987F6C /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A035A9AB1782355600987F6C /* MainMenu.xib */; }; A035A9B31782355600987F6C /* Icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = A035A9AD1782355600987F6C /* Icon.icns */; }; A035A9B51782355600987F6C /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A035A9AF1782355600987F6C /* main.cpp */; }; - A035A9CF17823FE100987F6C /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A035A9D017823FE100987F6C /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A035A9D117823FE100987F6C /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A035A9D217823FE100987F6C /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A035A9D317823FE100987F6C /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A035A9D417823FE100987F6C /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A035A9D517823FE100987F6C /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A035A9D617823FE100987F6C /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A035A9D717823FE100987F6C /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A035A9D817823FE100987F6C /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A035A9D917823FE100987F6C /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A035A9DA17823FE100987F6C /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A035A9EA17823FE100987F6C /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A035A71117822E9E00987F6C /* libsqlite3.dylib */; }; A035A9EB17823FE100987F6C /* libcurl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A9F808C177E98A600D9A1CB /* libcurl.dylib */; }; A035A9EC17823FE100987F6C /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 15C6482E165F399D007D4F18 /* libz.dylib */; }; @@ -1026,18 +896,6 @@ A035AA861782413200987F6C /* MoonWarriors-jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035AA7D1782413200987F6C /* MoonWarriors-jsb.js */; }; A035AA881782413200987F6C /* res in Resources */ = {isa = PBXBuildFile; fileRef = A035AA7F1782413200987F6C /* res */; }; A035AA8B1782413200987F6C /* src in Resources */ = {isa = PBXBuildFile; fileRef = A035AA821782413200987F6C /* src */; }; - A035AA9D1782422400987F6C /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A035AA9E1782422400987F6C /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A035AA9F1782422400987F6C /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A035AAA01782422400987F6C /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A035AAA11782422400987F6C /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A035AAA21782422400987F6C /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A035AAA31782422400987F6C /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A035AAA41782422400987F6C /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A035AAA51782422400987F6C /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A035AAA61782422400987F6C /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A035AAA71782422400987F6C /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A035AAA81782422400987F6C /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A035AAB81782422400987F6C /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A035A71117822E9E00987F6C /* libsqlite3.dylib */; }; A035AAB91782422400987F6C /* libcurl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A9F808C177E98A600D9A1CB /* libcurl.dylib */; }; A035AABA1782422400987F6C /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 15C6482E165F399D007D4F18 /* libz.dylib */; }; @@ -1054,18 +912,6 @@ A035AB38178242AF00987F6C /* Icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = A035AB07178242AF00987F6C /* Icon.icns */; }; A035AB3A178242AF00987F6C /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A035AB09178242AF00987F6C /* main.cpp */; }; A035AC001782435F00987F6C /* Published-iOS in Resources */ = {isa = PBXBuildFile; fileRef = A035ABFF1782435F00987F6C /* Published-iOS */; }; - A035AC101782453000987F6C /* jsb.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F617822A8E00987F6C /* jsb.js */; }; - A035AC111782453000987F6C /* jsb_chipmunk.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F717822A8E00987F6C /* jsb_chipmunk.js */; }; - A035AC121782453000987F6C /* jsb_chipmunk_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */; }; - A035AC131782453000987F6C /* jsb_cocos2d.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6F917822A8E00987F6C /* jsb_cocos2d.js */; }; - A035AC141782453000987F6C /* jsb_cocos2d_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */; }; - A035AC151782453000987F6C /* jsb_cocos2d_extension.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */; }; - A035AC161782453000987F6C /* jsb_cocosbuilder.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */; }; - A035AC171782453000987F6C /* jsb_debugger.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FD17822A8E00987F6C /* jsb_debugger.js */; }; - A035AC181782453000987F6C /* jsb_opengl.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FE17822A8E00987F6C /* jsb_opengl.js */; }; - A035AC191782453000987F6C /* jsb_opengl_constants.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */; }; - A035AC1A1782453000987F6C /* jsb_sys.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70017822A8E00987F6C /* jsb_sys.js */; }; - A035AC1B1782453000987F6C /* main.debug.js in Resources */ = {isa = PBXBuildFile; fileRef = A035A70117822A8E00987F6C /* main.debug.js */; }; A035AC2A1782453000987F6C /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A035A71117822E9E00987F6C /* libsqlite3.dylib */; }; A035AC2B1782453000987F6C /* libcurl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A9F808C177E98A600D9A1CB /* libcurl.dylib */; }; A035AC2C1782453000987F6C /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 15C6482E165F399D007D4F18 /* libz.dylib */; }; @@ -2430,7 +2276,7 @@ 1A119C9017853D8D00D62A44 /* Cocos2dConstants.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Cocos2dConstants.lua; path = ../scripting/lua/script/Cocos2dConstants.lua; sourceTree = ""; }; 1A119C9117853D8D00D62A44 /* Opengl.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Opengl.lua; path = ../scripting/lua/script/Opengl.lua; sourceTree = ""; }; 1A119C9217853D8D00D62A44 /* OpenglConstants.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = OpenglConstants.lua; path = ../scripting/lua/script/OpenglConstants.lua; sourceTree = ""; }; - 1A2E823A1793EED50058E421 /* jsb_deprecated.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_deprecated.js; sourceTree = ""; }; + 1A2B004418025A4F009E8C43 /* js */ = {isa = PBXFileReference; lastKnownFileType = folder; name = js; path = ../../../scripting/javascript/bindings/js; sourceTree = ""; }; 1A33319817E2DFEA00ED3802 /* NewEventDispatcherTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NewEventDispatcherTest.cpp; sourceTree = ""; }; 1A33319917E2DFEA00ED3802 /* NewEventDispatcherTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewEventDispatcherTest.h; sourceTree = ""; }; 1A6FB4C417854A6A00CDF010 /* animations */ = {isa = PBXFileReference; lastKnownFileType = folder; name = animations; path = Cpp/TestCpp/Resources/animations; sourceTree = SOURCE_ROOT; }; @@ -2998,18 +2844,6 @@ A035A6BD178229E400987F6C /* TransitionsTest */ = {isa = PBXFileReference; lastKnownFileType = folder; name = TransitionsTest; path = Javascript/Shared/tests/TransitionsTest; sourceTree = SOURCE_ROOT; }; A035A6BE178229E400987F6C /* UnitTest */ = {isa = PBXFileReference; lastKnownFileType = folder; name = UnitTest; path = Javascript/Shared/tests/UnitTest; sourceTree = SOURCE_ROOT; }; A035A6BF178229E400987F6C /* XHRTest */ = {isa = PBXFileReference; lastKnownFileType = folder; name = XHRTest; path = Javascript/Shared/tests/XHRTest; sourceTree = SOURCE_ROOT; }; - A035A6F617822A8E00987F6C /* jsb.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb.js; sourceTree = ""; }; - A035A6F717822A8E00987F6C /* jsb_chipmunk.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_chipmunk.js; sourceTree = ""; }; - A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_chipmunk_constants.js; sourceTree = ""; }; - A035A6F917822A8E00987F6C /* jsb_cocos2d.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_cocos2d.js; sourceTree = ""; }; - A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_cocos2d_constants.js; sourceTree = ""; }; - A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_cocos2d_extension.js; sourceTree = ""; }; - A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_cocosbuilder.js; sourceTree = ""; }; - A035A6FD17822A8E00987F6C /* jsb_debugger.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_debugger.js; sourceTree = ""; }; - A035A6FE17822A8E00987F6C /* jsb_opengl.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_opengl.js; sourceTree = ""; }; - A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_opengl_constants.js; sourceTree = ""; }; - A035A70017822A8E00987F6C /* jsb_sys.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jsb_sys.js; sourceTree = ""; }; - A035A70117822A8E00987F6C /* main.debug.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = main.debug.js; sourceTree = ""; }; A035A71117822E9E00987F6C /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; A035A77D1782301C00987F6C /* JS Watermelon With Me Mac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "JS Watermelon With Me Mac.app"; sourceTree = BUILT_PRODUCTS_DIR; }; A035A7811782340A00987F6C /* AppDelegate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AppDelegate.cpp; sourceTree = ""; }; @@ -5207,9 +5041,9 @@ A035A5F21782296200987F6C /* TestJavascript */ = { isa = PBXGroup; children = ( + 1A2B004418025A4F009E8C43 /* js */, A035A6321782296200987F6C /* proj.mac */, A01E17741784C0DF00B0CA4A /* proj.ios */, - A035A6F517822A8E00987F6C /* JS common */, A035A6F4178229ED00987F6C /* JS Tests */, A035A5F31782296200987F6C /* Classes */, A035A5F61782296200987F6C /* game.js */, @@ -5301,27 +5135,6 @@ name = "JS Tests"; sourceTree = ""; }; - A035A6F517822A8E00987F6C /* JS common */ = { - isa = PBXGroup; - children = ( - A035A6F617822A8E00987F6C /* jsb.js */, - A035A6F717822A8E00987F6C /* jsb_chipmunk.js */, - A035A6F817822A8E00987F6C /* jsb_chipmunk_constants.js */, - A035A6F917822A8E00987F6C /* jsb_cocos2d.js */, - A035A6FA17822A8E00987F6C /* jsb_cocos2d_constants.js */, - A035A6FB17822A8E00987F6C /* jsb_cocos2d_extension.js */, - A035A6FC17822A8E00987F6C /* jsb_cocosbuilder.js */, - A035A6FD17822A8E00987F6C /* jsb_debugger.js */, - 1A2E823A1793EED50058E421 /* jsb_deprecated.js */, - A035A6FE17822A8E00987F6C /* jsb_opengl.js */, - A035A6FF17822A8E00987F6C /* jsb_opengl_constants.js */, - A035A70017822A8E00987F6C /* jsb_sys.js */, - A035A70117822A8E00987F6C /* main.debug.js */, - ); - name = "JS common"; - path = ../../../scripting/javascript/bindings/js; - sourceTree = ""; - }; A035A77F1782340A00987F6C /* WatermelonWithMe */ = { isa = PBXGroup; children = ( @@ -6609,19 +6422,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E82401793EF260058E421 /* jsb_deprecated.js in Resources */, - A01E17D51784C28700B0CA4A /* jsb.js in Resources */, - A01E17D61784C28700B0CA4A /* jsb_chipmunk.js in Resources */, - A01E17D71784C28700B0CA4A /* jsb_chipmunk_constants.js in Resources */, - A01E17D81784C28700B0CA4A /* jsb_cocos2d.js in Resources */, - A01E17D91784C28700B0CA4A /* jsb_cocos2d_constants.js in Resources */, - A01E17DA1784C28700B0CA4A /* jsb_cocos2d_extension.js in Resources */, - A01E17DB1784C28700B0CA4A /* jsb_cocosbuilder.js in Resources */, - A01E17DC1784C28700B0CA4A /* jsb_debugger.js in Resources */, - A01E17DD1784C28700B0CA4A /* jsb_opengl.js in Resources */, - A01E17DE1784C28700B0CA4A /* jsb_opengl_constants.js in Resources */, - A01E17DF1784C28700B0CA4A /* jsb_sys.js in Resources */, - A01E17E01784C28700B0CA4A /* main.debug.js in Resources */, A01E17C01784C19700B0CA4A /* main.js in Resources */, A01E17C11784C19D00B0CA4A /* tests_resources-html5.js in Resources */, A01E17C21784C19D00B0CA4A /* tests_resources-jsb.js in Resources */, @@ -6683,6 +6483,7 @@ A01E17CC1784C1A100B0CA4A /* UnitTest in Resources */, A01E17CD1784C1A100B0CA4A /* XHRTest in Resources */, A01E17CF1784C1AB00B0CA4A /* obfuscate.xml in Resources */, + 1A2B004618025A4F009E8C43 /* js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6690,7 +6491,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E824B17952EC50058E421 /* jsb_deprecated.js in Resources */, A01E18FD1784C5B300B0CA4A /* boot-html5.js in Resources */, A01E18FE1784C5B300B0CA4A /* boot-jsb.js in Resources */, A01E19001784C5B300B0CA4A /* levels.js in Resources */, @@ -6698,18 +6498,6 @@ A01E19021784C5B300B0CA4A /* resources-html5.js in Resources */, A01E19031784C5B300B0CA4A /* resources-jsb.js in Resources */, A01E19041784C5B300B0CA4A /* watermelon_with_me.js in Resources */, - A01E17F51784C45400B0CA4A /* jsb.js in Resources */, - A01E17F61784C45400B0CA4A /* jsb_chipmunk.js in Resources */, - A01E17F71784C45400B0CA4A /* jsb_chipmunk_constants.js in Resources */, - A01E17F81784C45400B0CA4A /* jsb_cocos2d.js in Resources */, - A01E17F91784C45400B0CA4A /* jsb_cocos2d_constants.js in Resources */, - A01E17FA1784C45400B0CA4A /* jsb_cocos2d_extension.js in Resources */, - A01E17FB1784C45400B0CA4A /* jsb_cocosbuilder.js in Resources */, - A01E17FC1784C45400B0CA4A /* jsb_debugger.js in Resources */, - A01E17FD1784C45400B0CA4A /* jsb_opengl.js in Resources */, - A01E17FE1784C45400B0CA4A /* jsb_opengl_constants.js in Resources */, - A01E17FF1784C45400B0CA4A /* jsb_sys.js in Resources */, - A01E18001784C45400B0CA4A /* main.debug.js in Resources */, A01E18FF1784C5B300B0CA4A /* index.html in Resources */, A01E19051784C5B700B0CA4A /* res in Resources */, A01E191A1784C5CE00B0CA4A /* Default-568h@2x.png in Resources */, @@ -6726,19 +6514,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E824C17952ECE0058E421 /* jsb_deprecated.js in Resources */, - A01E18671784C4D600B0CA4A /* jsb.js in Resources */, - A01E18681784C4D600B0CA4A /* jsb_chipmunk.js in Resources */, - A01E18691784C4D600B0CA4A /* jsb_chipmunk_constants.js in Resources */, - A01E186A1784C4D600B0CA4A /* jsb_cocos2d.js in Resources */, - A01E186B1784C4D600B0CA4A /* jsb_cocos2d_constants.js in Resources */, - A01E186C1784C4D600B0CA4A /* jsb_cocos2d_extension.js in Resources */, - A01E186D1784C4D600B0CA4A /* jsb_cocosbuilder.js in Resources */, - A01E186E1784C4D600B0CA4A /* jsb_debugger.js in Resources */, - A01E186F1784C4D600B0CA4A /* jsb_opengl.js in Resources */, - A01E18701784C4D600B0CA4A /* jsb_opengl_constants.js in Resources */, - A01E18711784C4D600B0CA4A /* jsb_sys.js in Resources */, - A01E18721784C4D600B0CA4A /* main.debug.js in Resources */, A01E19281784C7A000B0CA4A /* res in Resources */, A01E19291784C7A000B0CA4A /* src in Resources */, A01E19421784C7B200B0CA4A /* Default-568h@2x.png in Resources */, @@ -6760,19 +6535,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E824D17952ED70058E421 /* jsb_deprecated.js in Resources */, - A01E189C1784C4DC00B0CA4A /* jsb.js in Resources */, - A01E189D1784C4DC00B0CA4A /* jsb_chipmunk.js in Resources */, - A01E189E1784C4DC00B0CA4A /* jsb_chipmunk_constants.js in Resources */, - A01E189F1784C4DC00B0CA4A /* jsb_cocos2d.js in Resources */, - A01E18A01784C4DC00B0CA4A /* jsb_cocos2d_constants.js in Resources */, - A01E18A11784C4DC00B0CA4A /* jsb_cocos2d_extension.js in Resources */, - A01E18A21784C4DC00B0CA4A /* jsb_cocosbuilder.js in Resources */, - A01E18A31784C4DC00B0CA4A /* jsb_debugger.js in Resources */, - A01E18A41784C4DC00B0CA4A /* jsb_opengl.js in Resources */, - A01E18A51784C4DC00B0CA4A /* jsb_opengl_constants.js in Resources */, - A01E18A61784C4DC00B0CA4A /* jsb_sys.js in Resources */, - A01E18A71784C4DC00B0CA4A /* main.debug.js in Resources */, A01E19521784C81B00B0CA4A /* Published-iOS in Resources */, A01E19671784C83200B0CA4A /* Default-568h@2x.png in Resources */, A01E19681784C83200B0CA4A /* Default.png in Resources */, @@ -6788,19 +6550,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E824E17952EDF0058E421 /* jsb_deprecated.js in Resources */, - A01E18D11784C4E000B0CA4A /* jsb.js in Resources */, - A01E18D21784C4E000B0CA4A /* jsb_chipmunk.js in Resources */, - A01E18D31784C4E000B0CA4A /* jsb_chipmunk_constants.js in Resources */, - A01E18D41784C4E000B0CA4A /* jsb_cocos2d.js in Resources */, - A01E18D51784C4E000B0CA4A /* jsb_cocos2d_constants.js in Resources */, - A01E18D61784C4E000B0CA4A /* jsb_cocos2d_extension.js in Resources */, - A01E18D71784C4E000B0CA4A /* jsb_cocosbuilder.js in Resources */, - A01E18D81784C4E000B0CA4A /* jsb_debugger.js in Resources */, - A01E18D91784C4E000B0CA4A /* jsb_opengl.js in Resources */, - A01E18DA1784C4E000B0CA4A /* jsb_opengl_constants.js in Resources */, - A01E18DB1784C4E000B0CA4A /* jsb_sys.js in Resources */, - A01E18DC1784C4E000B0CA4A /* main.debug.js in Resources */, A01E19871784C88400B0CA4A /* Default-568h@2x.png in Resources */, A01E19881784C88400B0CA4A /* Default.png in Resources */, A01E19891784C88400B0CA4A /* Default@2x.png in Resources */, @@ -6816,18 +6565,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - A01E19AC1784DA0600B0CA4A /* jsb.js in Resources */, - A01E19AD1784DA0600B0CA4A /* jsb_chipmunk.js in Resources */, - A01E19AE1784DA0600B0CA4A /* jsb_chipmunk_constants.js in Resources */, - A01E19AF1784DA0600B0CA4A /* jsb_cocos2d.js in Resources */, - A01E19B01784DA0600B0CA4A /* jsb_cocos2d_constants.js in Resources */, - A01E19B11784DA0600B0CA4A /* jsb_cocos2d_extension.js in Resources */, - A01E19B21784DA0600B0CA4A /* jsb_cocosbuilder.js in Resources */, - A01E19B31784DA0600B0CA4A /* jsb_debugger.js in Resources */, - A01E19B41784DA0600B0CA4A /* jsb_opengl.js in Resources */, - A01E19B51784DA0600B0CA4A /* jsb_opengl_constants.js in Resources */, - A01E19B61784DA0600B0CA4A /* jsb_sys.js in Resources */, - A01E19B71784DA0600B0CA4A /* main.debug.js in Resources */, A01E19BF1784DA0600B0CA4A /* game.js in Resources */, A01E19C01784DA0600B0CA4A /* Default-568h@2x.png in Resources */, A01E19C11784DA0600B0CA4A /* Default.png in Resources */, @@ -6855,18 +6592,6 @@ A01E1A281784DC4000B0CA4A /* tests-boot-jsb-for-obfuscation.js in Resources */, A01E1A291784DC4000B0CA4A /* tests-boot-jsb.js in Resources */, A01E1A2A1784DC4000B0CA4A /* tests-main.js in Resources */, - A01E1A2B1784DC4000B0CA4A /* jsb.js in Resources */, - A01E1A2C1784DC4000B0CA4A /* jsb_chipmunk.js in Resources */, - A01E1A2D1784DC4000B0CA4A /* jsb_chipmunk_constants.js in Resources */, - A01E1A2E1784DC4000B0CA4A /* jsb_cocos2d.js in Resources */, - A01E1A2F1784DC4000B0CA4A /* jsb_cocos2d_constants.js in Resources */, - A01E1A301784DC4000B0CA4A /* jsb_cocos2d_extension.js in Resources */, - A01E1A311784DC4000B0CA4A /* jsb_cocosbuilder.js in Resources */, - A01E1A321784DC4000B0CA4A /* jsb_debugger.js in Resources */, - A01E1A331784DC4000B0CA4A /* jsb_opengl.js in Resources */, - A01E1A341784DC4000B0CA4A /* jsb_opengl_constants.js in Resources */, - A01E1A351784DC4000B0CA4A /* jsb_sys.js in Resources */, - A01E1A361784DC4000B0CA4A /* main.debug.js in Resources */, A01E1A371784DC4000B0CA4A /* obfuscate.xml in Resources */, A01E1A381784DC4000B0CA4A /* InfoPlist.strings in Resources */, A01E1A391784DC4000B0CA4A /* MainMenu.xib in Resources */, @@ -6881,7 +6606,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E823F1793EEF50058E421 /* jsb_deprecated.js in Resources */, A035A64C1782296200987F6C /* game.js in Resources */, A035A6D7178229E400987F6C /* main.js in Resources */, A035A6E7178229E400987F6C /* tests_resources-html5.js in Resources */, @@ -6890,18 +6614,6 @@ A035A6EA178229E400987F6C /* tests-boot-jsb-for-obfuscation.js in Resources */, A035A6EB178229E400987F6C /* tests-boot-jsb.js in Resources */, A035A6EC178229E400987F6C /* tests-main.js in Resources */, - A035A70217822A8E00987F6C /* jsb.js in Resources */, - A035A70317822A8E00987F6C /* jsb_chipmunk.js in Resources */, - A035A70417822A8E00987F6C /* jsb_chipmunk_constants.js in Resources */, - A035A70517822A8E00987F6C /* jsb_cocos2d.js in Resources */, - A035A70617822A8E00987F6C /* jsb_cocos2d_constants.js in Resources */, - A035A70717822A8E00987F6C /* jsb_cocos2d_extension.js in Resources */, - A035A70817822A8E00987F6C /* jsb_cocosbuilder.js in Resources */, - A035A70917822A8E00987F6C /* jsb_debugger.js in Resources */, - A035A70A17822A8E00987F6C /* jsb_opengl.js in Resources */, - A035A70B17822A8E00987F6C /* jsb_opengl_constants.js in Resources */, - A035A70C17822A8E00987F6C /* jsb_sys.js in Resources */, - A035A70D17822A8E00987F6C /* main.debug.js in Resources */, A035A64D1782296200987F6C /* obfuscate.xml in Resources */, A035A6741782296200987F6C /* InfoPlist.strings in Resources */, A035A6751782296200987F6C /* MainMenu.xib in Resources */, @@ -6951,6 +6663,7 @@ A035A6F1178229E400987F6C /* TransitionsTest in Resources */, A035A6F2178229E400987F6C /* UnitTest in Resources */, A035A6F3178229E400987F6C /* XHRTest in Resources */, + 1A2B004518025A4F009E8C43 /* js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -6958,7 +6671,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E824517952E8D0058E421 /* jsb_deprecated.js in Resources */, A035A8661782343C00987F6C /* boot-html5.js in Resources */, A035A8671782343C00987F6C /* boot-jsb.js in Resources */, A035A8691782343C00987F6C /* levels.js in Resources */, @@ -6966,18 +6678,6 @@ A035A8D31782343C00987F6C /* resources-html5.js in Resources */, A035A8D41782343C00987F6C /* resources-jsb.js in Resources */, A035A8D51782343C00987F6C /* watermelon_with_me.js in Resources */, - A035A7291782301C00987F6C /* jsb.js in Resources */, - A035A72A1782301C00987F6C /* jsb_chipmunk.js in Resources */, - A035A72B1782301C00987F6C /* jsb_chipmunk_constants.js in Resources */, - A035A72C1782301C00987F6C /* jsb_cocos2d.js in Resources */, - A035A72D1782301C00987F6C /* jsb_cocos2d_constants.js in Resources */, - A035A72E1782301C00987F6C /* jsb_cocos2d_extension.js in Resources */, - A035A72F1782301C00987F6C /* jsb_cocosbuilder.js in Resources */, - A035A7301782301C00987F6C /* jsb_debugger.js in Resources */, - A035A7311782301C00987F6C /* jsb_opengl.js in Resources */, - A035A7321782301C00987F6C /* jsb_opengl_constants.js in Resources */, - A035A7331782301C00987F6C /* jsb_sys.js in Resources */, - A035A7341782301C00987F6C /* main.debug.js in Resources */, A035A8681782343C00987F6C /* index.html in Resources */, A035A9A3178234A900987F6C /* res in Resources */, A035A9B11782355600987F6C /* InfoPlist.strings in Resources */, @@ -6990,21 +6690,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E824817952E9A0058E421 /* jsb_deprecated.js in Resources */, A035AA841782413200987F6C /* main.js in Resources */, A035AA861782413200987F6C /* MoonWarriors-jsb.js in Resources */, - A035A9CF17823FE100987F6C /* jsb.js in Resources */, - A035A9D017823FE100987F6C /* jsb_chipmunk.js in Resources */, - A035A9D117823FE100987F6C /* jsb_chipmunk_constants.js in Resources */, - A035A9D217823FE100987F6C /* jsb_cocos2d.js in Resources */, - A035A9D317823FE100987F6C /* jsb_cocos2d_constants.js in Resources */, - A035A9D417823FE100987F6C /* jsb_cocos2d_extension.js in Resources */, - A035A9D517823FE100987F6C /* jsb_cocosbuilder.js in Resources */, - A035A9D617823FE100987F6C /* jsb_debugger.js in Resources */, - A035A9D717823FE100987F6C /* jsb_opengl.js in Resources */, - A035A9D817823FE100987F6C /* jsb_opengl_constants.js in Resources */, - A035A9D917823FE100987F6C /* jsb_sys.js in Resources */, - A035A9DA17823FE100987F6C /* main.debug.js in Resources */, A035AA6C178240E000987F6C /* InfoPlist.strings in Resources */, A035AA6D178240E000987F6C /* MainMenu.xib in Resources */, A035AA6E178240E000987F6C /* Icon.icns in Resources */, @@ -7017,19 +6704,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E824917952EAC0058E421 /* jsb_deprecated.js in Resources */, - A035AA9D1782422400987F6C /* jsb.js in Resources */, - A035AA9E1782422400987F6C /* jsb_chipmunk.js in Resources */, - A035AA9F1782422400987F6C /* jsb_chipmunk_constants.js in Resources */, - A035AAA01782422400987F6C /* jsb_cocos2d.js in Resources */, - A035AAA11782422400987F6C /* jsb_cocos2d_constants.js in Resources */, - A035AAA21782422400987F6C /* jsb_cocos2d_extension.js in Resources */, - A035AAA31782422400987F6C /* jsb_cocosbuilder.js in Resources */, - A035AAA41782422400987F6C /* jsb_debugger.js in Resources */, - A035AAA51782422400987F6C /* jsb_opengl.js in Resources */, - A035AAA61782422400987F6C /* jsb_opengl_constants.js in Resources */, - A035AAA71782422400987F6C /* jsb_sys.js in Resources */, - A035AAA81782422400987F6C /* main.debug.js in Resources */, A035AB36178242AF00987F6C /* InfoPlist.strings in Resources */, A035AB37178242AF00987F6C /* MainMenu.xib in Resources */, A035AB38178242AF00987F6C /* Icon.icns in Resources */, @@ -7041,19 +6715,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1A2E824A17952EB70058E421 /* jsb_deprecated.js in Resources */, - A035AC101782453000987F6C /* jsb.js in Resources */, - A035AC111782453000987F6C /* jsb_chipmunk.js in Resources */, - A035AC121782453000987F6C /* jsb_chipmunk_constants.js in Resources */, - A035AC131782453000987F6C /* jsb_cocos2d.js in Resources */, - A035AC141782453000987F6C /* jsb_cocos2d_constants.js in Resources */, - A035AC151782453000987F6C /* jsb_cocos2d_extension.js in Resources */, - A035AC161782453000987F6C /* jsb_cocosbuilder.js in Resources */, - A035AC171782453000987F6C /* jsb_debugger.js in Resources */, - A035AC181782453000987F6C /* jsb_opengl.js in Resources */, - A035AC191782453000987F6C /* jsb_opengl_constants.js in Resources */, - A035AC1A1782453000987F6C /* jsb_sys.js in Resources */, - A035AC1B1782453000987F6C /* main.debug.js in Resources */, A035ACAA178245AB00987F6C /* InfoPlist.strings in Resources */, A035ACAB178245AB00987F6C /* MainMenu.xib in Resources */, A035ACAC178245AB00987F6C /* Icon.icns in Resources */, @@ -7386,7 +7047,6 @@ A01E17911784C0E000B0CA4A /* main.m in Sources */, A01E17921784C0E000B0CA4A /* RootViewController.mm in Sources */, A01E17991784C16100B0CA4A /* AppDelegate.cpp in Sources */, - 1A2E823C1793EED50058E421 /* jsb_deprecated.js in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7460,7 +7120,6 @@ files = ( A035A64B1782296200987F6C /* AppDelegate.cpp in Sources */, A035A6781782296200987F6C /* main.cpp in Sources */, - 1A2E823B1793EED50058E421 /* jsb_deprecated.js in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/scripting/javascript/bindings/ScriptingCore.cpp b/scripting/javascript/bindings/ScriptingCore.cpp index 0ed5b8eb6874..9c9370e79bf6 100644 --- a/scripting/javascript/bindings/ScriptingCore.cpp +++ b/scripting/javascript/bindings/ScriptingCore.cpp @@ -662,9 +662,10 @@ JSBool ScriptingCore::executeScript(JSContext *cx, uint32_t argc, jsval *vp) if (argc == 2 && argv[1].isString()) { JSString* globalName = JSVAL_TO_STRING(argv[1]); JSStringWrapper name(globalName); - js::RootedObject* rootedGlobal = globals[name]; - if (rootedGlobal) { - res = ScriptingCore::getInstance()->runScript(path, rootedGlobal->get()); +// js::RootedObject* rootedGlobal = globals[name]; + JSObject* debugObj = ScriptingCore::getInstance()->getDebugGlobal(); + if (debugObj) { + res = ScriptingCore::getInstance()->runScript(path, debugObj); } else { JS_ReportError(cx, "Invalid global object: %s", (char*)name); return JS_FALSE; @@ -1964,9 +1965,9 @@ void ScriptingCore::enableDebugger() { JS_DefineFunction(cx_, debugGlobal_, "_bufferRead", JSBDebug_BufferRead, 0, JSPROP_READONLY | JSPROP_PERMANENT); JS_DefineFunction(cx_, debugGlobal_, "_lockVM", JSBDebug_LockExecution, 2, JSPROP_READONLY | JSPROP_PERMANENT); JS_DefineFunction(cx_, debugGlobal_, "_unlockVM", JSBDebug_UnlockExecution, 0, JSPROP_READONLY | JSPROP_PERMANENT); - + + runScript("jsb_debugger.js", debugGlobal_); -// runScript("SysTest/script.js", debugGlobal_); CCLOG("before _prepareDebugger..."); // prepare the debugger @@ -2266,7 +2267,6 @@ static void serverEntryPoint(void) listen(s, 1); - int recieveIndex = 0; while (true) { clientSocket = accept(s, NULL, NULL); @@ -2284,144 +2284,13 @@ static void serverEntryPoint(void) // process any input, send any output clearBuffers(); -// if (recieveIndex == 0) -// { -// replyToClient(clientSocket, "{\"from\":\"root\",\"applicationType\":\"browser\",\"traits\":{\"sources\": true}}"); -// ++recieveIndex; -// } - - char buf[1024] = {0}; int readBytes = 0; while ((readBytes = ::recv(clientSocket, buf, sizeof(buf), 0)) > 0) { buf[readBytes] = '\0'; - TRACE_DEBUGGER_SERVER("debug server : received command >%s", buf); +// TRACE_DEBUGGER_SERVER("debug server : received command >%s", buf); -// if (recieveIndex == 1) -// { -// replyToClient(clientSocket, "{ \"from\":\"root\", \"tabs\":[{ \"actor\":\"JSBTabActor\", \"title\":\"Hello cocos2d-x JSB\", \"url\":\"http://www.cocos2d-x.org\" }], \"selected\":0 }"); -// } -// else if (recieveIndex == 2) -// { -// replyToClient(clientSocket, "{ \"from\":\"JSBTabActor\", \"type\":\"tabAttached\", \"threadActor\":\"tabThreadActor111\" }"); -// } -// else if (recieveIndex == 3) -// { -// replyToClient(clientSocket, "{\ -// \"from\": \"tabThreadActor111\",\ -// \"type\": \"paused\",\ -// \"actor\": \"JSBTabActor\",\ -// \"poppedFrames\": [],\ -// \"why\": {\ -// \"type\": \"attached\"\ -// }\ -// }"); -// -// -// -// //replyToClient(clientSocket, "{ \"from\":\"JSBTabActor\", \"type\":\"tabNavigated\", \"state\":\"start\", \"url\":\"my_url.js\" }"); -// } -// else if (recieveIndex == 4) -// { -// replyToClient(clientSocket, "{\ -// \"from\": \"tabThreadActor111\",\ -// \"type\": \"newSource\",\ -// \"source\": {\ -// \"actor\": \"source_actor1\",\ -// \"url\": \"file://~/Project/cocos2d-html5/cocos2d/CCDirector.js\",\ -// \"isBlackBoxed\": false\ -// }\ -// }"); -// -// replyToClient(clientSocket, -// "{\ -// \"sources\": [\ -// {\ -// \"actor\": \"source_actor1\",\ -// \"url\": \"file://~/Project/cocos2d-html5/cocos2d/CCDirector.js\",\ -// \"isBlackBoxed\": false\ -// }\ -// ],\ -// \"from\": \"tabThreadActor111\"\ -// }"); -// } -// else if (recieveIndex == 5) -// { -// replyToClient(clientSocket, "{\ -// \"from\": \"source_actor1\",\ -// \"source\": {\ -// \"type\": \"longString\",\ -// \"initial\": \"var cc = cc || {}; cc.Director = {};\",\ -// \"length\": 100,\ -// \"actor\": \"conn2.longString48\"\ -// }\ -// }"); -// } -// else if (recieveIndex == 6) -// { -// replyToClient(clientSocket, "{\ -// \"from\": \"conn2.longString48\",\ -// \"substring\": \"var cc = cc || {}; cc.Director = {};\ -//\\ncc.Sprite = {};\"\ -// }"); -// -// -// replyToClient(clientSocket, "{\ -// \"from\": \"tabThreadActor111\",\ -// \"type\": \"resumed\"\ -// }"); -// } -// else if (recieveIndex == 7) -// { -// replyToClient(clientSocket, "{\ -// \"from\": \"tabThreadActor111\",\ -// \"type\": \"resumed\"\ -// }"); -// } -// else if (recieveIndex == 8) -// { -// replyToClient(clientSocket, "{ \"from\":\"tabThreadActor111\", \"actor\":\"breakActor\"}");//, \"actualLocation\":2 }"); -// } -// else if (recieveIndex == 9) -// { -// replyToClient(clientSocket, "{ \"from\":\"breakActor\" }"); -// } -// else if (recieveIndex == 10) -// { -// replyToClient(clientSocket, "{\ -// \"from\": \"tabThreadActor111\",\ -// \"type\": \"resumed\"\ -// }"); -// } -// else -// { -// -// -// std::string recvBuf = buf; -// auto found = recvBuf.find("setBreakpoint"); -// if (found != std::string::npos) -// { -// replyToClient(clientSocket, "{ \"from\":\"tabThreadActor111\", \"actor\":\"breakActor\"}");//, \"actualLocation\":2 }"); -// } -// -// found = recvBuf.find("delete"); -// if (found != std::string::npos) -// { -// replyToClient(clientSocket, "{ \"from\":\"breakActor\" }"); -// } -// -// found = recvBuf.find("interrupt"); -// if (found != std::string::npos) -// { -// replyToClient(clientSocket, "{\ -// \"from\": \"tabThreadActor111\",\ -// \"type\": \"resumed\"\ -// }"); -// } -// -// } -// ++recieveIndex; // no other thread is using this inData.append(buf); // process any input, send any output diff --git a/scripting/javascript/bindings/cocos2d_specifics.cpp b/scripting/javascript/bindings/cocos2d_specifics.cpp index db6aaa41d075..ceb78008acf8 100644 --- a/scripting/javascript/bindings/cocos2d_specifics.cpp +++ b/scripting/javascript/bindings/cocos2d_specifics.cpp @@ -3228,10 +3228,13 @@ JSBool js_cocos2dx_CCFileUtils_getStringFromFile(JSContext *cx, uint32_t argc, j CC_SAFE_DELETE_ARRAY(data); JS_SET_RVAL(cx, vp, jsret); - return JS_TRUE; } - JS_ReportError(cx, "get file(%s) data fails", arg0); - return JS_FALSE; + else + { + JS_SET_RVAL(cx, vp, JSVAL_VOID); + } + + return JS_TRUE; } JS_ReportError(cx, "wrong number of arguments: %d, was expecting %d", argc, 3); return JS_FALSE; diff --git a/scripting/javascript/bindings/js/debugger/DevToolsUtils.js b/scripting/javascript/bindings/js/debugger/DevToolsUtils.js new file mode 100644 index 000000000000..8b8fb19feb94 --- /dev/null +++ b/scripting/javascript/bindings/js/debugger/DevToolsUtils.js @@ -0,0 +1,74 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var dump = function(msg) { + log(msg); +}; + +/* General utilities used throughout devtools. */ + +/* Turn the error e into a string, without fail. */ +this.safeErrorString = function safeErrorString(aError) { + try { + var s = aError.toString(); + if (typeof s === "string") + return s; + } catch (ee) { } + + return ""; +} + +/** + * Report that |aWho| threw an exception, |aException|. + */ +this.reportException = function reportException(aWho, aException) { + let msg = aWho + " threw an exception: " + safeErrorString(aException); + if (aException.stack) { + msg += "\nCall stack:\n" + aException.stack; + } + + dump(msg + "\n"); + + // if (Components.utils.reportError) { + // /* + // * Note that the xpcshell test harness registers an observer for + // * console messages, so when we're running tests, this will cause + // * the test to quit. + // */ + // Components.utils.reportError(msg); + // } +} + +/** + * Given a handler function that may throw, return an infallible handler + * function that calls the fallible handler, and logs any exceptions it + * throws. + * + * @param aHandler function + * A handler function, which may throw. + * @param aName string + * A name for aHandler, for use in error messages. If omitted, we use + * aHandler.name. + * + * (SpiderMonkey does generate good names for anonymous functions, but we + * don't have a way to get at them from JavaScript at the moment.) + */ +this.makeInfallible = function makeInfallible(aHandler, aName) { + if (!aName) + aName = aHandler.name; + + return function (/* arguments */) { + try { + return aHandler.apply(this, arguments); + } catch (ex) { + let who = "Handler function"; + if (aName) { + who += " " + aName; + } + reportException(who, ex); + } + } +} diff --git a/scripting/javascript/bindings/js/debugger/actors/gcli.js b/scripting/javascript/bindings/js/debugger/actors/gcli.js new file mode 100644 index 000000000000..1f3c914d4e06 --- /dev/null +++ b/scripting/javascript/bindings/js/debugger/actors/gcli.js @@ -0,0 +1,77 @@ +/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +let { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); + +XPCOMUtils.defineLazyModuleGetter(this, "console", + "resource://gre/modules/devtools/Console.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "CommandUtils", + "resource:///modules/devtools/DeveloperToolbar.jsm"); + +XPCOMUtils.defineLazyGetter(this, "require", function() { + let { require } = Cu.import("resource://gre/modules/devtools/Require.jsm", {}); + Cu.import("resource://gre/modules/devtools/gcli.jsm", {}); + return require; +}); + +XPCOMUtils.defineLazyGetter(this, "canon", () => require("gcli/canon")); +XPCOMUtils.defineLazyGetter(this, "Requisition", () => require("gcli/cli").Requisition); +XPCOMUtils.defineLazyGetter(this, "util", () => require("util/util")); + + +/** + * Manage remote connections that want to talk to GCLI + * @constructor + * @param connection The connection to the client, DebuggerServerConnection + * @param parentActor Optional, the parent actor + */ +function GcliActor(connection, parentActor) { + this.connection = connection; +} + +GcliActor.prototype.actorPrefix = "gcli"; + +GcliActor.prototype.disconnect = function() { +}; + +GcliActor.prototype.getCommandSpecs = function(request) { + return { commandSpecs: canon.getCommandSpecs() }; +}; + +GcliActor.prototype.execute = function(request) { + let windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Ci.nsIWindowMediator); + let chromeWindow = windowMediator.getMostRecentWindow("navigator:browser"); + let contentWindow = chromeWindow.gBrowser.selectedTab.linkedBrowser.contentWindow; + + let environment = CommandUtils.createEnvironment(chromeWindow.document, + contentWindow.document); + + let requisition = new Requisition(environment); + requisition.updateExec(request.typed).then(output => { + return output.promise.then(() => { + this.connection.send({ + from: this.actorID, + requestId: request.requestId, + data: output.data, + type: output.type, + error: output.error + }); + }); + }).then(null, console.error); +}; + +GcliActor.prototype.requestTypes = { + getCommandSpecs: GcliActor.prototype.getCommandSpecs, + execute: GcliActor.prototype.execute, +}; + +addTabActor(GcliActor, "gcliActor"); +addGlobalActor(GcliActor, "gcliActor"); diff --git a/scripting/javascript/bindings/js/debugger/actors/inspector.js b/scripting/javascript/bindings/js/debugger/actors/inspector.js new file mode 100644 index 000000000000..ac07ad103a8f --- /dev/null +++ b/scripting/javascript/bindings/js/debugger/actors/inspector.js @@ -0,0 +1,2100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/** + * Here's the server side of the remote inspector. + * + * The WalkerActor is the client's view of the debuggee's DOM. It's gives + * the client a tree of NodeActor objects. + * + * The walker presents the DOM tree mostly unmodified from the source DOM + * tree, but with a few key differences: + * + * - Empty text nodes are ignored. This is pretty typical of developer + * tools, but maybe we should reconsider that on the server side. + * - iframes with documents loaded have the loaded document as the child, + * the walker provides one big tree for the whole document tree. + * + * There are a few ways to get references to NodeActors: + * + * - When you first get a WalkerActor reference, it comes with a free + * reference to the root document's node. + * - Given a node, you can ask for children, siblings, and parents. + * - You can issue querySelector and querySelectorAll requests to find + * other elements. + * - Requests that return arbitrary nodes from the tree (like querySelector + * and querySelectorAll) will also return any nodes the client hasn't + * seen in order to have a complete set of parents. + * + * Once you have a NodeFront, you should be able to answer a few questions + * without further round trips, like the node's name, namespace/tagName, + * attributes, etc. Other questions (like a text node's full nodeValue) + * might require another round trip. + * + * The protocol guarantees that the client will always know the parent of + * any node that is returned by the server. This means that some requests + * (like querySelector) will include the extra nodes needed to satisfy this + * requirement. The client keeps track of this parent relationship, so the + * node fronts form a tree that is a subset of the actual DOM tree. + */ + +const {Cc, Ci, Cu} = require("chrome"); + +const protocol = require("devtools/server/protocol"); +const {Arg, Option, method, RetVal, types} = protocol; +const {LongStringActor, ShortLongString} = require("devtools/server/actors/string"); +const promise = require("sdk/core/promise"); +const object = require("sdk/util/object"); +const events = require("sdk/event/core"); +const { Unknown } = require("sdk/platform/xpcom"); +const { Class } = require("sdk/core/heritage"); + +const PSEUDO_CLASSES = [":hover", ":active", ":focus"]; + +Cu.import("resource://gre/modules/Services.jsm"); + +exports.register = function(handle) { + handle.addTabActor(InspectorActor, "inspectorActor"); +}; + +exports.unregister = function(handle) { + handle.removeTabActor(InspectorActor); +}; + +// XXX: A poor man's makeInfallible until we move it out of transport.js +// Which should be very soon. +function makeInfallible(handler) { + return function(...args) { + try { + return handler.apply(this, args); + } catch(ex) { + console.error(ex); + } + return undefined; + } +} + +// A resolve that hits the main loop first. +function delayedResolve(value) { + let deferred = promise.defer(); + Services.tm.mainThread.dispatch(makeInfallible(function delayedResolveHandler() { + deferred.resolve(value); + }), 0); + return deferred.promise; +} + +/** + * We only send nodeValue up to a certain size by default. This stuff + * controls that size. + */ +exports.DEFAULT_VALUE_SUMMARY_LENGTH = 50; +var gValueSummaryLength = exports.DEFAULT_VALUE_SUMMARY_LENGTH; + +exports.getValueSummaryLength = function() { + return gValueSummaryLength; +}; + +exports.setValueSummaryLength = function(val) { + gValueSummaryLength = val; +}; + +/** + * Server side of the node actor. + */ +var NodeActor = protocol.ActorClass({ + typeName: "domnode", + + initialize: function(walker, node) { + protocol.Actor.prototype.initialize.call(this, null); + this.walker = walker; + this.rawNode = node; + }, + + toString: function() { + return "[NodeActor " + this.actorID + " for " + this.rawNode.toString() + "]"; + }, + + /** + * Instead of storing a connection object, the NodeActor gets its connection + * from its associated walker. + */ + get conn() this.walker.conn, + + // Returns the JSON representation of this object over the wire. + form: function(detail) { + let parentNode = this.walker.parentNode(this); + + // Estimate the number of children. + let numChildren = this.rawNode.childNodes.length; + if (numChildren === 0 && + (this.rawNode.contentDocument || this.rawNode.getSVGDocument)) { + // This might be an iframe with virtual children. + numChildren = 1; + } + + let form = { + actor: this.actorID, + parent: parentNode ? parentNode.actorID : undefined, + nodeType: this.rawNode.nodeType, + namespaceURI: this.namespaceURI, + nodeName: this.rawNode.nodeName, + numChildren: numChildren, + + // doctype attributes + name: this.rawNode.name, + publicId: this.rawNode.publicId, + systemId: this.rawNode.systemId, + + attrs: this.writeAttrs(), + + pseudoClassLocks: this.writePseudoClassLocks(), + }; + + if (this.rawNode.ownerDocument && + this.rawNode.ownerDocument.documentElement === this.rawNode) { + form.isDocumentElement = true; + } + + if (this.rawNode.nodeValue) { + // We only include a short version of the value if it's longer than + // gValueSummaryLength + if (this.rawNode.nodeValue.length > gValueSummaryLength) { + form.shortValue = this.rawNode.nodeValue.substring(0, gValueSummaryLength); + form.incompleteValue = true; + } else { + form.shortValue = this.rawNode.nodeValue; + } + } + + return form; + }, + + writeAttrs: function() { + if (!this.rawNode.attributes) { + return undefined; + } + return [{namespace: attr.namespace, name: attr.name, value: attr.value } + for (attr of this.rawNode.attributes)]; + }, + + writePseudoClassLocks: function() { + if (this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) { + return undefined; + } + let ret = undefined; + for (let pseudo of PSEUDO_CLASSES) { + if (DOMUtils.hasPseudoClassLock(this.rawNode, pseudo)) { + ret = ret || []; + ret.push(pseudo); + } + } + return ret; + }, + + /** + * Returns a LongStringActor with the node's value. + */ + getNodeValue: method(function() { + return new LongStringActor(this.conn, this.rawNode.nodeValue || ""); + }, { + request: {}, + response: { + value: RetVal("longstring") + } + }), + + /** + * Set the node's value to a given string. + */ + setNodeValue: method(function(value) { + this.rawNode.nodeValue = value; + }, { + request: { value: Arg(0) }, + response: {} + }), + + /** + * Modify a node's attributes. Passed an array of modifications + * similar in format to "attributes" mutations. + * { + * attributeName: + * attributeNamespace: + * newValue: - If null or undefined, the attribute + * will be removed. + * } + * + * Returns when the modifications have been made. Mutations will + * be queued for any changes made. + */ + modifyAttributes: method(function(modifications) { + let rawNode = this.rawNode; + for (let change of modifications) { + if (change.newValue == null) { + if (change.attributeNamespace) { + rawNode.removeAttributeNS(change.attributeNamespace, change.attributeName); + } else { + rawNode.removeAttribute(change.attributeName); + } + } else { + if (change.attributeNamespace) { + rawNode.setAttributeNS(change.attributeNamespace, change.attributeName, change.newValue); + } else { + rawNode.setAttribute(change.attributeName, change.newValue); + } + } + } + }, { + request: { + modifications: Arg(0, "array:json") + }, + response: {} + }), + +}); + +/** + * Client side of the node actor. + * + * Node fronts are strored in a tree that mirrors the DOM tree on the + * server, but with a few key differences: + * - Not all children will be necessary loaded for each node. + * - The order of children isn't guaranteed to be the same as the DOM. + * Children are stored in a doubly-linked list, to make addition/removal + * and traversal quick. + * + * Due to the order/incompleteness of the child list, it is safe to use + * the parent node from clients, but the `children` request should be used + * to traverse children. + */ +let NodeFront = protocol.FrontClass(NodeActor, { + initialize: function(conn, form, detail, ctx) { + this._parent = null; // The parent node + this._child = null; // The first child of this node. + this._next = null; // The next sibling of this node. + this._prev = null; // The previous sibling of this node. + protocol.Front.prototype.initialize.call(this, conn, form, detail, ctx); + }, + + /** + * Destroy a node front. The node must have been removed from the + * ownership tree before this is called, unless the whole walker front + * is being destroyed. + */ + destroy: function() { + // If an observer was added on this node, shut it down. + if (this.observer) { + this._observer.disconnect(); + this._observer = null; + } + + protocol.Front.prototype.destroy.call(this); + }, + + // Update the object given a form representation off the wire. + form: function(form, detail, ctx) { + // Shallow copy of the form. We could just store a reference, but + // eventually we'll want to update some of the data. + this._form = object.merge(form); + this._form.attrs = this._form.attrs ? this._form.attrs.slice() : []; + + if (form.parent) { + // Get the owner actor for this actor (the walker), and find the + // parent node of this actor from it, creating a standin node if + // necessary. + let parentNodeFront = ctx.marshallPool().ensureParentFront(form.parent); + this.reparent(parentNodeFront); + } + }, + + /** + * Returns the parent NodeFront for this NodeFront. + */ + parentNode: function() { + return this._parent; + }, + + /** + * Process a mutation entry as returned from the walker's `getMutations` + * request. Only tries to handle changes of the node's contents + * themselves (character data and attribute changes), the walker itself + * will keep the ownership tree up to date. + */ + updateMutation: function(change) { + if (change.type === "attributes") { + // We'll need to lazily reparse the attributes after this change. + this._attrMap = undefined; + + // Update any already-existing attributes. + let found = false; + for (let i = 0; i < this.attributes.length; i++) { + let attr = this.attributes[i]; + if (attr.name == change.attributeName && + attr.namespace == change.attributeNamespace) { + if (change.newValue !== null) { + attr.value = change.newValue; + } else { + this.attributes.splice(i, 1); + } + found = true; + break; + } + } + // This is a new attribute. + if (!found) { + this.attributes.push({ + name: change.attributeName, + namespace: change.attributeNamespace, + value: change.newValue + }); + } + } else if (change.type === "characterData") { + this._form.shortValue = change.newValue; + this._form.incompleteValue = change.incompleteValue; + } else if (change.type === "pseudoClassLock") { + this._form.pseudoClassLocks = change.pseudoClassLocks; + } + }, + + // Some accessors to make NodeFront feel more like an nsIDOMNode + + get id() this.getAttribute("id"), + + get nodeType() this._form.nodeType, + get namespaceURI() this._form.namespaceURI, + get nodeName() this._form.nodeName, + + get className() { + return this.getAttribute("class") || ''; + }, + + get hasChildren() this._form.numChildren > 0, + get numChildren() this._form.numChildren, + + get tagName() this.nodeType === Ci.nsIDOMNode.ELEMENT_NODE ? this.nodeName : null, + get shortValue() this._form.shortValue, + get incompleteValue() !!this._form.incompleteValue, + + get isDocumentElement() !!this._form.isDocumentElement, + + // doctype properties + get name() this._form.name, + get publicId() this._form.publicId, + get systemId() this._form.systemId, + + getAttribute: function(name) { + let attr = this._getAttribute(name); + return attr ? attr.value : null; + }, + hasAttribute: function(name) { + this._cacheAttributes(); + return (name in this._attrMap); + }, + + get attributes() this._form.attrs, + + get pseudoClassLocks() this._form.pseudoClassLocks || [], + hasPseudoClassLock: function(pseudo) { + return this.pseudoClassLocks.some(locked => locked === pseudo); + }, + + getNodeValue: protocol.custom(function() { + if (!this.incompleteValue) { + return delayedResolve(new ShortLongString(this.shortValue)); + } else { + return this._getNodeValue(); + } + }, { + impl: "_getNodeValue" + }), + + /** + * Return a new AttributeModificationList for this node. + */ + startModifyingAttributes: function() { + return AttributeModificationList(this); + }, + + _cacheAttributes: function() { + if (typeof(this._attrMap) != "undefined") { + return; + } + this._attrMap = {}; + for (let attr of this.attributes) { + this._attrMap[attr.name] = attr; + } + }, + + _getAttribute: function(name) { + this._cacheAttributes(); + return this._attrMap[name] || undefined; + }, + + /** + * Set this node's parent. Note that the children saved in + * this tree are unordered and incomplete, so shouldn't be used + * instead of a `children` request. + */ + reparent: function(parent) { + if (this._parent === parent) { + return; + } + + if (this._parent && this._parent._child === this) { + this._parent._child = this._next; + } + if (this._prev) { + this._prev._next = this._next; + } + if (this._next) { + this._next._prev = this._prev; + } + this._next = null; + this._prev = null; + this._parent = parent; + if (!parent) { + // Subtree is disconnected, we're done + return; + } + this._next = parent._child; + if (this._next) { + this._next._prev = this; + } + parent._child = this; + }, + + /** + * Return all the known children of this node. + */ + treeChildren: function() { + let ret = []; + for (let child = this._child; child != null; child = child._next) { + ret.push(child); + } + return ret; + }, + + /** + * Get an nsIDOMNode for the given node front. This only works locally, + * and is only intended as a stopgap during the transition to the remote + * protocol. If you depend on this you're likely to break soon. + */ + rawNode: function(rawNode) { + if (!this.conn._transport._serverConnection) { + console.warn("Tried to use rawNode on a remote connection."); + return null; + } + let actor = this.conn._transport._serverConnection.getActor(this.actorID); + if (!actor) { + // Can happen if we try to get the raw node for an already-expired + // actor. + return null; + } + return actor.rawNode; + } +}); + +/** + * Returned from any call that might return a node that isn't connected to root by + * nodes the child has seen, such as querySelector. + */ +types.addDictType("disconnectedNode", { + // The actual node to return + node: "domnode", + + // Nodes that are needed to connect the node to a node the client has already seen + newNodes: "array:domnode" +}); + +types.addDictType("disconnectedNodeArray", { + // The actual node list to return + nodes: "array:domnode", + + // Nodes that are needed to connect those nodes to the root. + newNodes: "array:domnode" +}); + +types.addDictType("dommutation", {}); + +/** + * Server side of a node list as returned by querySelectorAll() + */ +var NodeListActor = exports.NodeListActor = protocol.ActorClass({ + typeName: "domnodelist", + + initialize: function(walker, nodeList) { + protocol.Actor.prototype.initialize.call(this); + this.walker = walker; + this.nodeList = nodeList; + }, + + destroy: function() { + protocol.Actor.prototype.destroy.call(this); + }, + + /** + * Instead of storing a connection object, the NodeActor gets its connection + * from its associated walker. + */ + get conn() { + return this.walker.conn; + }, + + /** + * Items returned by this actor should belong to the parent walker. + */ + marshallPool: function() { + return this.walker; + }, + + // Returns the JSON representation of this object over the wire. + form: function() { + return { + actor: this.actorID, + length: this.nodeList.length + } + }, + + /** + * Get a single node from the node list. + */ + item: method(function(index) { + let node = this.walker._ref(this.nodeList[index]); + let newNodes = [node for (node of this.walker.ensurePathToRoot(node))]; + return { + node: node, + newNodes: newNodes + } + }, { + request: { item: Arg(0) }, + response: RetVal("disconnectedNode") + }), + + /** + * Get a range of the items from the node list. + */ + items: method(function(start=0, end=this.nodeList.length) { + let items = [this.walker._ref(item) for (item of Array.prototype.slice.call(this.nodeList, start, end))]; + let newNodes = new Set(); + for (let item of items) { + this.walker.ensurePathToRoot(item, newNodes); + } + return { + nodes: items, + newNodes: [node for (node of newNodes)] + } + }, { + request: { + start: Arg(0, "number", { optional: true }), + end: Arg(1, "number", { optional: true }) + }, + response: { nodes: RetVal("disconnectedNodeArray") } + }), + + release: method(function() {}, { release: true }) +}); + +/** + * Client side of a node list as returned by querySelectorAll() + */ +var NodeListFront = exports.NodeLIstFront = protocol.FrontClass(NodeListActor, { + initialize: function(client, form) { + protocol.Front.prototype.initialize.call(this, client, form); + }, + + destroy: function() { + protocol.Front.prototype.destroy.call(this); + }, + + marshallPool: function() { + return this.parent(); + }, + + // Update the object given a form representation off the wire. + form: function(json) { + this.length = json.length; + }, + + item: protocol.custom(function(index) { + return this._item(index).then(response => { + return response.node; + }); + }, { + impl: "_item" + }), + + items: protocol.custom(function(start, end) { + return this._items(start, end).then(response => { + return response.nodes; + }); + }, { + impl: "_items" + }) +}); + +// Some common request/response templates for the dom walker + +let nodeArrayMethod = { + request: { + node: Arg(0, "domnode"), + maxNodes: Option(1), + center: Option(1, "domnode"), + start: Option(1, "domnode"), + whatToShow: Option(1) + }, + response: RetVal(types.addDictType("domtraversalarray", { + nodes: "array:domnode" + })) +}; + +let traversalMethod = { + request: { + node: Arg(0, "domnode"), + whatToShow: Option(1) + }, + response: { + node: RetVal("domnode", {optional: true}) + } +} + +/** + * We need to know when a document is navigating away so that we can kill + * the nodes underneath it. We also need to know when a document is + * navigated to so that we can send a mutation event for the iframe node. + * + * The nsIWebProgressListener is the easiest/best way to watch these + * loads that works correctly with the bfcache. + * + * See nsIWebProgressListener for details + * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIWebProgressListener + */ +var ProgressListener = Class({ + extends: Unknown, + interfaces: ["nsIWebProgressListener", "nsISupportsWeakReference"], + + initialize: function(webProgress) { + Unknown.prototype.initialize.call(this); + this.webProgress = webProgress; + this.webProgress.addProgressListener(this); + }, + + destroy: function() { + this.webProgress.removeProgressListener(this); + }, + + onStateChange: makeInfallible(function stateChange(progress, request, flag, status) { + let isWindow = flag & Ci.nsIWebProgressListener.STATE_IS_WINDOW; + let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT; + if (!(isWindow || isDocument)) { + return; + } + + if (isDocument && (flag & Ci.nsIWebProgressListener.STATE_START)) { + events.emit(this, "windowchange-start", progress.DOMWindow); + } + if (isWindow && (flag & Ci.nsIWebProgressListener.STATE_STOP)) { + events.emit(this, "windowchange-stop", progress.DOMWindow); + } + }), + onProgressChange: function() {}, + onSecurityChange: function() {}, + onStatusChange: function() {}, + onLocationChange: function() {}, +}); + +/** + * Server side of the DOM walker. + */ +var WalkerActor = protocol.ActorClass({ + typeName: "domwalker", + + events: { + "new-mutations" : { + type: "newMutations" + } + }, + + /** + * Create the WalkerActor + * @param DebuggerServerConnection conn + * The server connection. + */ + initialize: function(conn, document, webProgress, options) { + protocol.Actor.prototype.initialize.call(this, conn); + this.rootDoc = document; + this._refMap = new Map(); + this._pendingMutations = []; + this._activePseudoClassLocks = new Set(); + + // Nodes which have been removed from the client's known + // ownership tree are considered "orphaned", and stored in + // this set. + this._orphaned = new Set(); + + // The client can tell the walker that it is interested in a node + // even when it is orphaned with the `retainNode` method. This + // list contains orphaned nodes that were so retained. + this._retainedOrphans = new Set(); + + this.onMutations = this.onMutations.bind(this); + this.onFrameLoad = this.onFrameLoad.bind(this); + this.onFrameUnload = this.onFrameUnload.bind(this); + + this.progressListener = ProgressListener(webProgress); + + events.on(this.progressListener, "windowchange-start", this.onFrameUnload); + events.on(this.progressListener, "windowchange-stop", this.onFrameLoad); + + // Ensure that the root document node actor is ready and + // managed. + this.rootNode = this.document(); + }, + + // Returns the JSON representation of this object over the wire. + form: function() { + return { + actor: this.actorID, + root: this.rootNode.form() + } + }, + + toString: function() { + return "[WalkerActor " + this.actorID + "]"; + }, + + destroy: function() { + this.clearPseudoClassLocks(); + this._activePseudoClassLocks = null; + this.progressListener.destroy(); + this.rootDoc = null; + protocol.Actor.prototype.destroy.call(this); + }, + + release: method(function() {}, { release: true }), + + unmanage: function(actor) { + if (actor instanceof NodeActor) { + if (this._activePseudoClassLocks && + this._activePseudoClassLocks.has(actor)) { + this.clearPsuedoClassLocks(actor); + } + this._refMap.delete(actor.rawNode); + } + protocol.Actor.prototype.unmanage.call(this, actor); + }, + + _ref: function(node) { + let actor = this._refMap.get(node); + if (actor) return actor; + + actor = new NodeActor(this, node); + + // Add the node actor as a child of this walker actor, assigning + // it an actorID. + this.manage(actor); + this._refMap.set(node, actor); + + if (node.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) { + this._watchDocument(actor); + } + return actor; + }, + + /** + * Watch the given document node for mutations using the DOM observer + * API. + */ + _watchDocument: function(actor) { + let node = actor.rawNode; + // Create the observer on the node's actor. The node will make sure + // the observer is cleaned up when the actor is released. + actor.observer = actor.rawNode.defaultView.MutationObserver(this.onMutations); + actor.observer.observe(node, { + attributes: true, + characterData: true, + childList: true, + subtree: true + }); + }, + + /** + * Return the document node that contains the given node, + * or the root node if no node is specified. + * @param NodeActor node + * The node whose document is needed, or null to + * return the root. + */ + document: method(function(node) { + let doc = node ? nodeDocument(node.rawNode) : this.rootDoc; + return this._ref(doc); + }, { + request: { node: Arg(0, "domnode", {optional: true}) }, + response: { node: RetVal("domnode") }, + }), + + /** + * Return the documentElement for the document containing the + * given node. + * @param NodeActor node + * The node whose documentElement is requested, or null + * to use the root document. + */ + documentElement: method(function(node) { + let elt = node ? nodeDocument(node.rawNode).documentElement : this.rootDoc.documentElement; + return this._ref(elt); + }, { + request: { node: Arg(0, "domnode", {optional: true}) }, + response: { node: RetVal("domnode") }, + }), + + /** + * Return all parents of the given node, ordered from immediate parent + * to root. + * @param NodeActor node + * The node whose parents are requested. + * @param object options + * Named options, including: + * `sameDocument`: If true, parents will be restricted to the same + * document as the node. + */ + parents: method(function(node, options={}) { + let walker = documentWalker(node.rawNode); + let parents = []; + let cur; + while((cur = walker.parentNode())) { + if (options.sameDocument && cur.ownerDocument != node.rawNode.ownerDocument) { + break; + } + parents.push(this._ref(cur)); + } + return parents; + }, { + request: { + node: Arg(0, "domnode"), + sameDocument: Option(1) + }, + response: { + nodes: RetVal("array:domnode") + }, + }), + + parentNode: function(node) { + let walker = documentWalker(node.rawNode); + let parent = walker.parentNode(); + if (parent) { + return this._ref(parent); + } + return null; + }, + + /** + * Mark a node as 'retained'. + * + * A retained node is not released when `releaseNode` is called on its + * parent, or when a parent is released with the `cleanup` option to + * `getMutations`. + * + * When a retained node's parent is released, a retained mode is added to + * the walker's "retained orphans" list. + * + * Retained nodes can be deleted by providing the `force` option to + * `releaseNode`. They will also be released when their document + * has been destroyed. + * + * Retaining a node makes no promise about its children; They can + * still be removed by normal means. + */ + retainNode: method(function(node) { + node.retained = true; + }, { + request: { node: Arg(0, "domnode") }, + response: {} + }), + + /** + * Remove the 'retained' mark from a node. If the node was a + * retained orphan, release it. + */ + unretainNode: method(function(node) { + node.retained = false; + if (this._retainedOrphans.has(node)) { + this._retainedOrphans.delete(node); + this.releaseNode(node); + } + }, { + request: { node: Arg(0, "domnode") }, + response: {}, + }), + + /** + * Release actors for a node and all child nodes. + */ + releaseNode: method(function(node, options={}) { + if (node.retained && !options.force) { + this._retainedOrphans.add(node); + return; + } + + if (node.retained) { + // Forcing a retained node to go away. + this._retainedOrphans.delete(node); + } + + let walker = documentWalker(node.rawNode); + + let child = walker.firstChild(); + while (child) { + let childActor = this._refMap.get(child); + if (childActor) { + this.releaseNode(childActor, options); + } + child = walker.nextSibling(); + } + + node.destroy(); + }, { + request: { + node: Arg(0, "domnode"), + force: Option(1) + } + }), + + /** + * Add any nodes between `node` and the walker's root node that have not + * yet been seen by the client. + */ + ensurePathToRoot: function(node, newParents=new Set()) { + if (!node) { + return newParents; + } + let walker = documentWalker(node.rawNode); + let cur; + while ((cur = walker.parentNode())) { + let parent = this._refMap.get(cur); + if (!parent) { + // This parent didn't exist, so hasn't been seen by the client yet. + newParents.add(this._ref(cur)); + } else { + // This parent did exist, so the client knows about it. + return newParents; + } + } + return newParents; + }, + + /** + * Return children of the given node. By default this method will return + * all children of the node, but there are options that can restrict this + * to a more manageable subset. + * + * @param NodeActor node + * The node whose children you're curious about. + * @param object options + * Named options: + * `maxNodes`: The set of nodes returned by the method will be no longer + * than maxNodes. + * `start`: If a node is specified, the list of nodes will start + * with the given child. Mutally exclusive with `center`. + * `center`: If a node is specified, the given node will be as centered + * as possible in the list, given how close to the ends of the child + * list it is. Mutually exclusive with `start`. + * `whatToShow`: A bitmask of node types that should be included. See + * https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter. + * + * @returns an object with three items: + * hasFirst: true if the first child of the node is included in the list. + * hasLast: true if the last child of the node is included in the list. + * nodes: Child nodes returned by the request. + */ + children: method(function(node, options={}) { + if (options.center && options.start) { + throw Error("Can't specify both 'center' and 'start' options."); + } + let maxNodes = options.maxNodes || -1; + if (maxNodes == -1) { + maxNodes = Number.MAX_VALUE; + } + + // We're going to create a few document walkers with the same filter, + // make it easier. + let filteredWalker = function(node) { + return documentWalker(node, options.whatToShow); + } + + // Need to know the first and last child. + let rawNode = node.rawNode; + let firstChild = filteredWalker(rawNode).firstChild(); + let lastChild = filteredWalker(rawNode).lastChild(); + + if (!firstChild) { + // No children, we're done. + return { hasFirst: true, hasLast: true, nodes: [] }; + } + + let start; + if (options.center) { + start = options.center.rawNode; + } else if (options.start) { + start = options.start.rawNode; + } else { + start = firstChild; + } + + let nodes = []; + + // Start by reading backward from the starting point if we're centering... + let backwardWalker = filteredWalker(start); + if (start != firstChild && options.center) { + backwardWalker.previousSibling(); + let backwardCount = Math.floor(maxNodes / 2); + let backwardNodes = this._readBackward(backwardWalker, backwardCount); + nodes = backwardNodes; + } + + // Then read forward by any slack left in the max children... + let forwardWalker = filteredWalker(start); + let forwardCount = maxNodes - nodes.length; + nodes = nodes.concat(this._readForward(forwardWalker, forwardCount)); + + // If there's any room left, it means we've run all the way to the end. + // If we're centering, check if there are more items to read at the front. + let remaining = maxNodes - nodes.length; + if (options.center && remaining > 0 && nodes[0].rawNode != firstChild) { + let firstNodes = this._readBackward(backwardWalker, remaining); + + // Then put it all back together. + nodes = firstNodes.concat(nodes); + } + + return { + hasFirst: nodes[0].rawNode == firstChild, + hasLast: nodes[nodes.length - 1].rawNode == lastChild, + nodes: nodes + }; + }, nodeArrayMethod), + + /** + * Return siblings of the given node. By default this method will return + * all siblings of the node, but there are options that can restrict this + * to a more manageable subset. + * + * If `start` or `center` are not specified, this method will center on the + * node whose siblings are requested. + * + * @param NodeActor node + * The node whose children you're curious about. + * @param object options + * Named options: + * `maxNodes`: The set of nodes returned by the method will be no longer + * than maxNodes. + * `start`: If a node is specified, the list of nodes will start + * with the given child. Mutally exclusive with `center`. + * `center`: If a node is specified, the given node will be as centered + * as possible in the list, given how close to the ends of the child + * list it is. Mutually exclusive with `start`. + * `whatToShow`: A bitmask of node types that should be included. See + * https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter. + * + * @returns an object with three items: + * hasFirst: true if the first child of the node is included in the list. + * hasLast: true if the last child of the node is included in the list. + * nodes: Child nodes returned by the request. + */ + siblings: method(function(node, options={}) { + let parentNode = documentWalker(node.rawNode).parentNode(); + if (!parentNode) { + return { + hasFirst: true, + hasLast: true, + nodes: [node] + }; + } + + if (!(options.start || options.center)) { + options.center = node; + } + + return this.children(this._ref(parentNode), options); + }, nodeArrayMethod), + + /** + * Get the next sibling of a given node. Getting nodes one at a time + * might be inefficient, be careful. + * + * @param object options + * Named options: + * `whatToShow`: A bitmask of node types that should be included. See + * https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter. + */ + nextSibling: method(function(node, options={}) { + let walker = documentWalker(node.rawNode, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL); + let sibling = walker.nextSibling(); + return sibling ? this._ref(sibling) : null; + }, traversalMethod), + + /** + * Get the previous sibling of a given node. Getting nodes one at a time + * might be inefficient, be careful. + * + * @param object options + * Named options: + * `whatToShow`: A bitmask of node types that should be included. See + * https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter. + */ + previousSibling: method(function(node, options={}) { + let walker = documentWalker(node.rawNode, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL); + let sibling = walker.previousSibling(); + return sibling ? this._ref(sibling) : null; + }, traversalMethod), + + /** + * Helper function for the `children` method: Read forward in the sibling + * list into an array with `count` items, including the current node. + */ + _readForward: function(walker, count) + { + let ret = []; + let node = walker.currentNode; + do { + ret.push(this._ref(node)); + node = walker.nextSibling(); + } while (node && --count); + return ret; + }, + + /** + * Helper function for the `children` method: Read backward in the sibling + * list into an array with `count` items, including the current node. + */ + _readBackward: function(walker, count) + { + let ret = []; + let node = walker.currentNode; + do { + ret.push(this._ref(node)); + node = walker.previousSibling(); + } while(node && --count); + ret.reverse(); + return ret; + }, + + /** + * Return the first node in the document that matches the given selector. + * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelector + * + * @param NodeActor baseNode + * @param string selector + */ + querySelector: method(function(baseNode, selector) { + let node = baseNode.rawNode.querySelector(selector); + + if (!node) { + return { + } + }; + + let node = this._ref(node); + let newParents = this.ensurePathToRoot(node); + return { + node: node, + newNodes: [parent for (parent of newParents)] + } + }, { + request: { + node: Arg(0, "domnode"), + selector: Arg(1) + }, + response: RetVal("disconnectedNode") + }), + + /** + * Return a NodeListActor with all nodes that match the given selector. + * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelectorAll + * + * @param NodeActor baseNode + * @param string selector + */ + querySelectorAll: method(function(baseNode, selector) { + return new NodeListActor(this, baseNode.rawNode.querySelectorAll(selector)); + }, { + request: { + node: Arg(0, "domnode"), + selector: Arg(1) + }, + response: { + list: RetVal("domnodelist") + } + }), + + /** + * Add a pseudo-class lock to a node. + * + * @param NodeActor node + * @param string pseudo + * A pseudoclass: ':hover', ':active', ':focus' + * @param options + * Options object: + * `parents`: True if the pseudo-class should be added + * to parent nodes. + * + * @returns An empty packet. A "pseudoClassLock" mutation will + * be queued for any changed nodes. + */ + addPseudoClassLock: method(function(node, pseudo, options={}) { + this._addPseudoClassLock(node, pseudo); + + if (!options.parents) { + return; + } + + let walker = documentWalker(node.rawNode); + let cur; + while ((cur = walker.parentNode())) { + let curNode = this._ref(cur); + this._addPseudoClassLock(curNode, pseudo); + } + }, { + request: { + node: Arg(0, "domnode"), + pseudoClass: Arg(1), + parents: Option(2) + }, + response: {} + }), + + _queuePseudoClassMutation: function(node) { + this.queueMutation({ + target: node.actorID, + type: "pseudoClassLock", + pseudoClassLocks: node.writePseudoClassLocks() + }); + }, + + _addPseudoClassLock: function(node, pseudo) { + if (node.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) { + return false; + } + DOMUtils.addPseudoClassLock(node.rawNode, pseudo); + this._activePseudoClassLocks.add(node); + this._queuePseudoClassMutation(node); + return true; + }, + + /** + * Remove a pseudo-class lock from a node. + * + * @param NodeActor node + * @param string pseudo + * A pseudoclass: ':hover', ':active', ':focus' + * @param options + * Options object: + * `parents`: True if the pseudo-class should be removed + * from parent nodes. + * + * @returns An empty response. "pseudoClassLock" mutations + * will be emitted for any changed nodes. + */ + removePseudoClassLock: method(function(node, pseudo, options={}) { + this._removePseudoClassLock(node, pseudo); + + if (!options.parents) { + return; + } + + let walker = documentWalker(node.rawNode); + let cur; + while ((cur = walker.parentNode())) { + let curNode = this._ref(cur); + this._removePseudoClassLock(curNode, pseudo); + } + }, { + request: { + node: Arg(0, "domnode"), + pseudoClass: Arg(1), + parents: Option(2) + }, + response: {} + }), + + _removePseudoClassLock: function(node, pseudo) { + if (node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE) { + return false; + } + DOMUtils.removePseudoClassLock(node.rawNode, pseudo); + if (!node.writePseudoClassLocks()) { + this._activePseudoClassLocks.delete(node); + } + this._queuePseudoClassMutation(node); + return true; + }, + + /** + * Clear all the pseudo-classes on a given node + * or all nodes. + */ + clearPseudoClassLocks: method(function(node) { + if (node) { + DOMUtils.clearPseudoClassLocks(node.rawNode); + this._activePseudoClassLocks.delete(node); + this._queuePseudoClassMutation(node); + } else { + for (let locked of this._activePseudoClassLocks) { + DOMUtils.clearPseudoClassLocks(locked.rawNode); + this._activePseudoClassLocks.delete(locked); + this._queuePseudoClassMutation(locked); + } + } + }, { + request: { + node: Arg(0, "domnode", { optional: true }), + }, + response: {} + }), + + /** + * Get a node's innerHTML property. + */ + innerHTML: method(function(node) { + return LongStringActor(this.conn, node.rawNode.innerHTML); + }, { + request: { + node: Arg(0, "domnode") + }, + response: { + value: RetVal("longstring") + } + }), + + /** + * Get a node's outerHTML property. + */ + outerHTML: method(function(node) { + return LongStringActor(this.conn, node.rawNode.outerHTML); + }, { + request: { + node: Arg(0, "domnode") + }, + response: { + value: RetVal("longstring") + } + }), + + /** + * Get any pending mutation records. Must be called by the client after + * the `new-mutations` notification is received. Returns an array of + * mutation records. + * + * Mutation records have a basic structure: + * + * { + * type: attributes|characterData|childList, + * target: , + * } + * + * And additional attributes based on the mutation type: + * + * `attributes` type: + * attributeName: - the attribute that changed + * attributeNamespace: - the attribute's namespace URI, if any. + * newValue: - The new value of the attribute, if any. + * + * `characterData` type: + * newValue: - the new shortValue for the node + * [incompleteValue: true] - True if the shortValue was truncated. + * + * `childList` type is returned when the set of children for a node + * has changed. Includes extra data, which can be used by the client to + * maintain its ownership subtree. + * + * added: array of - The list of actors *previously + * seen by the client* that were added to the target node. + * removed: array of The list of actors *previously + * seen by the client* that were removed from the target node. + * + * Actors that are included in a MutationRecord's `removed` but + * not in an `added` have been removed from the client's ownership + * tree (either by being moved under a node the client has seen yet + * or by being removed from the tree entirely), and is considered + * 'orphaned'. + * + * Keep in mind that if a node that the client hasn't seen is moved + * into or out of the target node, it will not be included in the + * removedNodes and addedNodes list, so if the client is interested + * in the new set of children it needs to issue a `children` request. + */ + getMutations: method(function(options={}) { + let pending = this._pendingMutations || []; + this._pendingMutations = []; + + if (options.cleanup) { + for (let node of this._orphaned) { + // Release the orphaned node. Nodes or children that have been + // retained will be moved to this._retainedOrphans. + this.releaseNode(node); + } + this._orphaned = new Set(); + } + + return pending; + }, { + request: { + cleanup: Option(0) + }, + response: { + mutations: RetVal("array:dommutation") + } + }), + + queueMutation: function(mutation) { + if (!this.actorID) { + // We've been destroyed, don't bother queueing this mutation. + return; + } + // We only send the `new-mutations` notification once, until the client + // fetches mutations with the `getMutations` packet. + let needEvent = this._pendingMutations.length === 0; + + this._pendingMutations.push(mutation); + + if (needEvent) { + events.emit(this, "new-mutations"); + } + }, + + /** + * Handles mutations from the DOM mutation observer API. + * + * @param array[MutationRecord] mutations + * See https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver#MutationRecord + */ + onMutations: function(mutations) { + for (let change of mutations) { + let targetActor = this._refMap.get(change.target); + if (!targetActor) { + continue; + } + let targetNode = change.target; + let mutation = { + type: change.type, + target: targetActor.actorID, + } + + if (mutation.type === "attributes") { + mutation.attributeName = change.attributeName; + mutation.attributeNamespace = change.attributeNamespace || undefined; + mutation.newValue = targetNode.getAttribute(mutation.attributeName); + } else if (mutation.type === "characterData") { + if (targetNode.nodeValue.length > gValueSummaryLength) { + mutation.newValue = targetNode.nodeValue.substring(0, gValueSummaryLength); + mutation.incompleteValue = true; + } else { + mutation.newValue = targetNode.nodeValue; + } + } else if (mutation.type === "childList") { + // Get the list of removed and added actors that the client has seen + // so that it can keep its ownership tree up to date. + let removedActors = []; + let addedActors = []; + for (let removed of change.removedNodes) { + let removedActor = this._refMap.get(removed); + if (!removedActor) { + // If the client never encountered this actor we don't need to + // mention that it was removed. + continue; + } + // While removed from the tree, nodes are saved as orphaned. + this._orphaned.add(removedActor); + removedActors.push(removedActor.actorID); + } + for (let added of change.addedNodes) { + let addedActor = this._refMap.get(added); + if (!addedActor) { + // If the client never encounted this actor we don't need to tell + // it about its addition for ownership tree purposes - if the + // client wants to see the new nodes it can ask for children. + continue; + } + // The actor is reconnected to the ownership tree, unorphan + // it and let the client know so that its ownership tree is up + // to date. + this._orphaned.delete(addedActor); + addedActors.push(addedActor.actorID); + } + mutation.numChildren = change.target.childNodes.length; + mutation.removed = removedActors; + mutation.added = addedActors; + } + this.queueMutation(mutation); + } + }, + + onFrameLoad: function(window) { + let frame = window.frameElement; + let frameActor = this._refMap.get(frame); + if (!frameActor) { + return; + } + + this.queueMutation({ + type: "frameLoad", + target: frameActor.actorID, + }); + + // Send a childList mutation on the frame. + this.queueMutation({ + type: "childList", + target: frameActor.actorID, + added: [], + removed: [] + }) + }, + + // Returns true if domNode is in window or a subframe. + _childOfWindow: function(window, domNode) { + let win = nodeDocument(domNode).defaultView; + while (win) { + if (win === window) { + return true; + } + win = win.frameElement; + } + return false; + }, + + onFrameUnload: function(window) { + // Any retained orphans that belong to this document + // or its children need to be released, and a mutation sent + // to notify of that. + let releasedOrphans = []; + + for (let retained of this._retainedOrphans) { + if (Cu.isDeadWrapper(retained.rawNode) || + this._childOfWindow(window, retained.rawNode)) { + this._retainedOrphans.delete(retained); + releasedOrphans.push(retained.actorID); + this.releaseNode(retained, { force: true }); + } + } + + if (releasedOrphans.length > 0) { + this.queueMutation({ + target: this.rootNode.actorID, + type: "unretained", + nodes: releasedOrphans + }); + } + + let doc = window.document; + let documentActor = this._refMap.get(doc); + if (!documentActor) { + return; + } + + this.queueMutation({ + type: "documentUnload", + target: documentActor.actorID + }); + + let walker = documentWalker(doc); + let parentNode = walker.parentNode(); + if (parentNode) { + // Send a childList mutation on the frame so that clients know + // they should reread the children list. + this.queueMutation({ + type: "childList", + target: this._refMap.get(parentNode).actorID, + added: [], + removed: [] + }); + } + + // Need to force a release of this node, because those nodes can't + // be accessed anymore. + this.releaseNode(documentActor, { force: true }); + } +}); + +/** + * Client side of the DOM walker. + */ +var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, { + // Set to true if cleanup should be requested after every mutation list. + autoCleanup: true, + + initialize: function(client, form) { + protocol.Front.prototype.initialize.call(this, client, form); + this._orphaned = new Set(); + this._retainedOrphans = new Set(); + }, + + destroy: function() { + protocol.Front.prototype.destroy.call(this); + }, + + // Update the object given a form representation off the wire. + form: function(json) { + this.actorID = json.actorID; + this.rootNode = types.getType("domnode").read(json.root, this); + }, + + /** + * When reading an actor form off the wire, we want to hook it up to its + * parent front. The protocol guarantees that the parent will be seen + * by the client in either a previous or the current request. + * So if we've already seen this parent return it, otherwise create + * a bare-bones stand-in node. The stand-in node will be updated + * with a real form by the end of the deserialization. + */ + ensureParentFront: function(id) { + let front = this.get(id); + if (front) { + return front; + } + + return types.getType("domnode").read({ actor: id }, this, "standin"); + }, + + /** + * See the documentation for WalkerActor.prototype.retainNode for + * information on retained nodes. + * + * From the client's perspective, `retainNode` can fail if the node in + * question is removed from the ownership tree before the `retainNode` + * request reaches the server. This can only happen if the client has + * asked the server to release nodes but hasn't gotten a response + * yet: Either a `releaseNode` request or a `getMutations` with `cleanup` + * set is outstanding. + * + * If either of those requests is outstanding AND releases the retained + * node, this request will fail with noSuchActor, but the ownership tree + * will stay in a consistent state. + * + * Because the protocol guarantees that requests will be processed and + * responses received in the order they were sent, we get the right + * semantics by setting our local retained flag on the node only AFTER + * a SUCCESSFUL retainNode call. + */ + retainNode: protocol.custom(function(node) { + return this._retainNode(node).then(() => { + node.retained = true; + }); + }, { + impl: "_retainNode", + }), + + unretainNode: protocol.custom(function(node) { + return this._unretainNode(node).then(() => { + node.retained = false; + if (this._retainedOrphans.has(node)) { + this._retainedOrphans.delete(node); + this._releaseFront(node); + } + }); + }, { + impl: "_unretainNode" + }), + + releaseNode: protocol.custom(function(node, options={}) { + // NodeFront.destroy will destroy children in the ownership tree too, + // mimicking what the server will do here. + let actorID = node.actorID; + this._releaseFront(node, !!options.force); + return this._releaseNode({ actorID: actorID }); + }, { + impl: "_releaseNode" + }), + + querySelector: protocol.custom(function(queryNode, selector) { + return this._querySelector(queryNode, selector).then(response => { + return response.node; + }); + }, { + impl: "_querySelector" + }), + + _releaseFront: function(node, force) { + if (node.retained && !force) { + node.reparent(null); + this._retainedOrphans.add(node); + return; + } + + if (node.retained) { + // Forcing a removal. + this._retainedOrphans.delete(node); + } + + // Release any children + for (let child of node.treeChildren()) { + this._releaseFront(child, force); + } + + // All children will have been removed from the node by this point. + node.reparent(null); + node.destroy(); + }, + + /** + * Get any unprocessed mutation records and process them. + */ + getMutations: protocol.custom(function(options={}) { + return this._getMutations(options).then(mutations => { + let emitMutations = []; + for (let change of mutations) { + // The target is only an actorID, get the associated front. + let targetID = change.target; + let targetFront = this.get(targetID); + if (!targetFront) { + console.trace("Got a mutation for an unexpected actor: " + targetID + ", please file a bug on bugzilla.mozilla.org!"); + continue; + } + + let emittedMutation = object.merge(change, { target: targetFront }); + + if (change.type === "childList") { + // Update the ownership tree according to the mutation record. + let addedFronts = []; + let removedFronts = []; + for (let removed of change.removed) { + let removedFront = this.get(removed); + if (!removedFront) { + console.error("Got a removal of an actor we didn't know about: " + removed); + continue; + } + // Remove from the ownership tree + removedFront.reparent(null); + + // This node is orphaned unless we get it in the 'added' list + // eventually. + this._orphaned.add(removedFront); + removedFronts.push(removedFront); + } + for (let added of change.added) { + let addedFront = this.get(added); + if (!addedFront) { + console.error("Got an addition of an actor we didn't know about: " + added); + continue; + } + addedFront.reparent(targetFront) + + // The actor is reconnected to the ownership tree, unorphan + // it. + this._orphaned.delete(addedFront); + addedFronts.push(addedFront); + } + // Before passing to users, replace the added and removed actor + // ids with front in the mutation record. + emittedMutation.added = addedFronts; + emittedMutation.removed = removedFronts; + targetFront._form.numChildren = change.numChildren; + } else if (change.type === "frameLoad") { + // Nothing we need to do here, except verify that we don't have any + // document children, because we should have gotten a documentUnload + // first. + for (let child of targetFront.treeChildren()) { + if (child.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) { + console.trace("Got an unexpected frameLoad in the inspector, please file a bug on bugzilla.mozilla.org!"); + } + } + } else if (change.type === "documentUnload") { + // We try to give fronts instead of actorIDs, but these fronts need + // to be destroyed now. + emittedMutation.target = targetFront.actorID; + emittedMutation.targetParent = targetFront.parentNode(); + + // Release the document node and all of its children, even retained. + this._releaseFront(targetFront, true); + } else if (change.type === "unretained") { + // Retained orphans were force-released without the intervention of + // client (probably a navigated frame). + for (let released of change.nodes) { + let releasedFront = this.get(released); + this._retainedOrphans.delete(released); + this._releaseFront(releasedFront, true); + } + } else { + targetFront.updateMutation(change); + } + + emitMutations.push(emittedMutation); + } + + if (options.cleanup) { + for (let node of this._orphaned) { + // This will move retained nodes to this._retainedOrphans. + this._releaseFront(node); + } + this._orphaned = new Set(); + } + + events.emit(this, "mutations", emitMutations); + }); + }, { + impl: "_getMutations" + }), + + /** + * Handle the `new-mutations` notification by fetching the + * available mutation records. + */ + onMutations: protocol.preEvent("new-mutations", function() { + // Fetch and process the mutations. + this.getMutations({cleanup: this.autoCleanup}).then(null, console.error); + }), + + isLocal: function() { + return !!this.conn._transport._serverConnection; + }, + + // XXX hack during transition to remote inspector: get a proper NodeFront + // for a given local node. Only works locally. + frontForRawNode: function(rawNode){ + if (!this.isLocal()) { + console.warn("Tried to use frontForRawNode on a remote connection."); + return null; + } + let walkerActor = this.conn._transport._serverConnection.getActor(this.actorID); + if (!walkerActor) { + throw Error("Could not find client side for actor " + this.actorID); + } + let nodeActor = walkerActor._ref(rawNode); + + // Pass the node through a read/write pair to create the client side actor. + let nodeType = types.getType("domnode"); + let returnNode = nodeType.read(nodeType.write(nodeActor, walkerActor), this); + let top = returnNode; + let extras = walkerActor.parents(nodeActor); + for (let extraActor of extras) { + top = nodeType.read(nodeType.write(extraActor, walkerActor), this); + } + + if (top !== this.rootNode) { + // Imported an already-orphaned node. + this._orphaned.add(top); + walkerActor._orphaned.add(this.conn._transport._serverConnection.getActor(top.actorID)); + } + return returnNode; + } +}); + +/** + * Convenience API for building a list of attribute modifications + * for the `modifyAttributes` request. + */ +var AttributeModificationList = Class({ + initialize: function(node) { + this.node = node; + this.modifications = []; + }, + + apply: function() { + let ret = this.node.modifyAttributes(this.modifications); + return ret; + }, + + destroy: function() { + this.node = null; + this.modification = null; + }, + + setAttributeNS: function(ns, name, value) { + this.modifications.push({ + attributeNamespace: ns, + attributeName: name, + newValue: value + }); + }, + + setAttribute: function(name, value) { + this.setAttributeNS(undefined, name, value); + }, + + removeAttributeNS: function(ns, name) { + this.setAttributeNS(ns, name, undefined); + }, + + removeAttribute: function(name) { + this.setAttributeNS(undefined, name, undefined); + } +}) + +/** + * Server side of the inspector actor, which is used to create + * inspector-related actors, including the walker. + */ +var InspectorActor = protocol.ActorClass({ + typeName: "inspector", + initialize: function(conn, tabActor) { + protocol.Actor.prototype.initialize.call(this, conn); + this.tabActor = tabActor; + }, + + get window() { + let tabActor = this.tabActor; + if (tabActor.browser instanceof Ci.nsIDOMWindow) { + return tabActor.browser; + } else if (tabActor.browser instanceof Ci.nsIDOMElement) { + return tabActor.browser.contentWindow; + } + return null; + }, + + getWalker: method(function(options={}) { + let deferred = promise.defer(); + + let window = this.window; + + var domReady = () => { + let tabActor = this.tabActor; + window.removeEventListener("DOMContentLoaded", domReady, true); + deferred.resolve(WalkerActor(this.conn, window.document, tabActor._tabbrowser, options)); + }; + + if (window.document.readyState === "loading") { + window.addEventListener("DOMContentLoaded", domReady, true); + } else { + domReady(); + } + + return deferred.promise; + }, { + request: {}, + response: { + walker: RetVal("domwalker") + } + }) +}); + +/** + * Client side of the inspector actor, which is used to create + * inspector-related actors, including the walker. + */ +var InspectorFront = exports.InspectorFront = protocol.FrontClass(InspectorActor, { + initialize: function(client, tabForm) { + protocol.Front.prototype.initialize.call(this, client); + this.actorID = tabForm.inspectorActor; + + // XXX: This is the first actor type in its hierarchy to use the protocol + // library, so we're going to self-own on the client side for now. + client.addActorPool(this); + this.manage(this); + } +}); + +function documentWalker(node, whatToShow=Ci.nsIDOMNodeFilter.SHOW_ALL) { + return new DocumentWalker(node, whatToShow, whitespaceTextFilter, false); +} + +// Exported for test purposes. +exports._documentWalker = documentWalker; + +function nodeDocument(node) { + return node.ownerDocument || (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null); +} + +/** + * Similar to a TreeWalker, except will dig in to iframes and it doesn't + * implement the good methods like previousNode and nextNode. + * + * See TreeWalker documentation for explanations of the methods. + */ +function DocumentWalker(aNode, aShow, aFilter, aExpandEntityReferences) +{ + let doc = nodeDocument(aNode); + this.walker = doc.createTreeWalker(nodeDocument(aNode), + aShow, aFilter, aExpandEntityReferences); + this.walker.currentNode = aNode; + this.filter = aFilter; +} + +DocumentWalker.prototype = { + get node() this.walker.node, + get whatToShow() this.walker.whatToShow, + get expandEntityReferences() this.walker.expandEntityReferences, + get currentNode() this.walker.currentNode, + set currentNode(aVal) this.walker.currentNode = aVal, + + /** + * Called when the new node is in a different document than + * the current node, creates a new treewalker for the document we've + * run in to. + */ + _reparentWalker: function DW_reparentWalker(aNewNode) { + if (!aNewNode) { + return null; + } + let doc = nodeDocument(aNewNode); + let walker = doc.createTreeWalker(doc, + this.whatToShow, this.filter, this.expandEntityReferences); + walker.currentNode = aNewNode; + this.walker = walker; + return aNewNode; + }, + + parentNode: function DW_parentNode() + { + let currentNode = this.walker.currentNode; + let parentNode = this.walker.parentNode(); + + if (!parentNode) { + if (currentNode && currentNode.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE + && currentNode.defaultView) { + let embeddingFrame = currentNode.defaultView.frameElement; + if (embeddingFrame) { + return this._reparentWalker(embeddingFrame); + } + } + return null; + } + + return parentNode; + }, + + firstChild: function DW_firstChild() + { + let node = this.walker.currentNode; + if (!node) + return null; + if (node.contentDocument) { + return this._reparentWalker(node.contentDocument); + } else if (node.getSVGDocument) { + return this._reparentWalker(node.getSVGDocument()); + } + return this.walker.firstChild(); + }, + + lastChild: function DW_lastChild() + { + let node = this.walker.currentNode; + if (!node) + return null; + if (node.contentDocument) { + return this._reparentWalker(node.contentDocument); + } else if (node.getSVGDocument) { + return this._reparentWalker(node.getSVGDocument()); + } + return this.walker.lastChild(); + }, + + previousSibling: function DW_previousSibling() this.walker.previousSibling(), + nextSibling: function DW_nextSibling() this.walker.nextSibling() +} + +/** + * A tree walker filter for avoiding empty whitespace text nodes. + */ +function whitespaceTextFilter(aNode) +{ + if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE && + !/[^\s]/.exec(aNode.nodeValue)) { + return Ci.nsIDOMNodeFilter.FILTER_SKIP; + } else { + return Ci.nsIDOMNodeFilter.FILTER_ACCEPT; + } +} + +loader.lazyGetter(this, "DOMUtils", function () { + return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils); +}); diff --git a/scripting/javascript/bindings/js/debugger/actors/profiler.js b/scripting/javascript/bindings/js/debugger/actors/profiler.js new file mode 100644 index 000000000000..bc87f4859968 --- /dev/null +++ b/scripting/javascript/bindings/js/debugger/actors/profiler.js @@ -0,0 +1,218 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var startedProfilers = 0; +var startTime = 0; + +function getCurrentTime() { + return (new Date()).getTime() - startTime; +} + +/** + * Creates a ProfilerActor. ProfilerActor provides remote access to the + * built-in profiler module. + */ +function ProfilerActor(aConnection) +{ + this._profiler = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler); + this._started = false; + this._observedEvents = []; +} + +ProfilerActor.prototype = { + actorPrefix: "profiler", + + disconnect: function() { + for (var event of this._observedEvents) { + Services.obs.removeObserver(this, event); + } + + this.stopProfiler(); + this._profiler = null; + }, + + stopProfiler: function() { + // We stop the profiler only after the last client has + // stopped profiling. Otherwise there's a problem where + // we stop the profiler as soon as you close the devtools + // panel in one tab even though there might be other + // profiler instances running in other tabs. + if (!this._started) { + return; + } + this._started = false; + startedProfilers -= 1; + if (startedProfilers <= 0) { + this._profiler.StopProfiler(); + } + }, + + onStartProfiler: function(aRequest) { + this._profiler.StartProfiler(aRequest.entries, aRequest.interval, + aRequest.features, aRequest.features.length); + this._started = true; + startedProfilers += 1; + startTime = (new Date()).getTime(); + return { "msg": "profiler started" } + }, + onStopProfiler: function(aRequest) { + this.stopProfiler(); + return { "msg": "profiler stopped" } + }, + onGetProfileStr: function(aRequest) { + var profileStr = this._profiler.GetProfile(); + return { "profileStr": profileStr } + }, + onGetProfile: function(aRequest) { + var profile = this._profiler.getProfileData(); + return { "profile": profile, "currentTime": getCurrentTime() } + }, + onIsActive: function(aRequest) { + var isActive = this._profiler.IsActive(); + var currentTime = isActive ? getCurrentTime() : null; + return { "isActive": isActive, "currentTime": currentTime } + }, + onGetResponsivenessTimes: function(aRequest) { + var times = this._profiler.GetResponsivenessTimes({}); + return { "responsivenessTimes": times } + }, + onGetFeatures: function(aRequest) { + var features = this._profiler.GetFeatures([]); + return { "features": features } + }, + onGetSharedLibraryInformation: function(aRequest) { + var sharedLibraries = this._profiler.getSharedLibraryInformation(); + return { "sharedLibraryInformation": sharedLibraries } + }, + onRegisterEventNotifications: function(aRequest) { + let registered = []; + for (var event of aRequest.events) { + if (this._observedEvents.indexOf(event) != -1) + continue; + Services.obs.addObserver(this, event, false); + this._observedEvents.push(event); + registered.push(event); + } + return { registered: registered } + }, + onUnregisterEventNotifications: function(aRequest) { + let unregistered = []; + for (var event of aRequest.events) { + let idx = this._observedEvents.indexOf(event); + if (idx == -1) + continue; + Services.obs.removeObserver(this, event); + this._observedEvents.splice(idx, 1); + unregistered.push(event); + } + return { unregistered: unregistered } + }, + observe: makeInfallible(function(aSubject, aTopic, aData) { + /* + * this.conn.send can only transmit acyclic values. However, it is + * idiomatic for wrapped JS objects like aSubject (and possibly aData?) + * to have a 'wrappedJSObject' property pointing to themselves. + * + * this.conn.send also assumes that it can retain the object it is + * passed to be handled on later event ticks; and that it's okay to + * freeze it. Since we don't really know what aSubject and aData are, + * we need to pass this.conn.send a copy of them, not the originals. + * + * We break the cycle and make the copy by JSON.stringifying those + * values with a replacer that omits properties known to introduce + * cycles, and then JSON.parsing the result. This spends processor + * time, but it's simple. + */ + function cycleBreaker(key, value) { + if (key === 'wrappedJSObject') { + return undefined; + } + return value; + } + + /* + * If these values are objects with a non-null 'wrappedJSObject' + * property, use its value. Otherwise, use the value unchanged. + */ + aSubject = (aSubject && aSubject.wrappedJSObject) || aSubject; + aData = (aData && aData.wrappedJSObject) || aData; + + let subj = JSON.parse(JSON.stringify(aSubject, cycleBreaker)); + let data = JSON.parse(JSON.stringify(aData, cycleBreaker)); + + let send = (extra) => { + data = data || {}; + + if (extra) + data.extra = extra; + + this.conn.send({ + from: this.actorID, + type: "eventNotification", + event: aTopic, + subject: subj, + data: data + }); + } + + if (aTopic !== "console-api-profiler") + return void send(); + + // If the event was generated from console.profile or + // console.profileEnd we need to start the profiler + // right away and only then notify our client. Otherwise, + // we'll lose precious samples. + + let name = subj.arguments[0]; + + if (subj.action === "profile") { + let resp = this.onIsActive(); + + if (resp.isActive) { + return void send({ + name: name, + currentTime: resp.currentTime, + action: "profile" + }); + } + + this.onStartProfiler({ + entries: 1000000, + interval: 1, + features: ["js"] + }); + + return void send({ currentTime: 0, action: "profile", name: name }); + } + + if (subj.action === "profileEnd") { + let resp = this.onGetProfile(); + resp.action = "profileEnd"; + resp.name = name; + send(resp); + } + + return undefined; // Otherwise xpcshell tests fail. + }, "ProfilerActor.prototype.observe"), +}; + +/** + * The request types this actor can handle. + */ +ProfilerActor.prototype.requestTypes = { + "startProfiler": ProfilerActor.prototype.onStartProfiler, + "stopProfiler": ProfilerActor.prototype.onStopProfiler, + "getProfileStr": ProfilerActor.prototype.onGetProfileStr, + "getProfile": ProfilerActor.prototype.onGetProfile, + "isActive": ProfilerActor.prototype.onIsActive, + "getResponsivenessTimes": ProfilerActor.prototype.onGetResponsivenessTimes, + "getFeatures": ProfilerActor.prototype.onGetFeatures, + "getSharedLibraryInformation": ProfilerActor.prototype.onGetSharedLibraryInformation, + "registerEventNotifications": ProfilerActor.prototype.onRegisterEventNotifications, + "unregisterEventNotifications": ProfilerActor.prototype.onUnregisterEventNotifications +}; + +DebuggerServer.addGlobalActor(ProfilerActor, "profilerActor"); diff --git a/scripting/javascript/bindings/js/debugger/actors/root.js b/scripting/javascript/bindings/js/debugger/actors/root.js new file mode 100644 index 000000000000..aa0d85342bcf --- /dev/null +++ b/scripting/javascript/bindings/js/debugger/actors/root.js @@ -0,0 +1,329 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/* Root actor for the remote debugging protocol. */ + +/** + * Methods shared between RootActor and BrowserTabActor. + */ + +/** + * Populate |this._extraActors| as specified by |aFactories|, reusing whatever + * actors are already there. Add all actors in the final extra actors table to + * |aPool|. + * + * The root actor and the tab actor use this to instantiate actors that other + * parts of the browser have specified with DebuggerServer.addTabActor antd + * DebuggerServer.addGlobalActor. + * + * @param aFactories + * An object whose own property names are the names of properties to add to + * some reply packet (say, a tab actor grip or the "listTabs" response + * form), and whose own property values are actor constructor functions, as + * documented for addTabActor and addGlobalActor. + * + * @param this + * The BrowserRootActor or BrowserTabActor with which the new actors will + * be associated. It should support whatever API the |aFactories| + * constructor functions might be interested in, as it is passed to them. + * For the sake of CommonCreateExtraActors itself, it should have at least + * the following properties: + * + * - _extraActors + * An object whose own property names are factory table (and packet) + * property names, and whose values are no-argument actor constructors, + * of the sort that one can add to an ActorPool. + * + * - conn + * The DebuggerServerConnection in which the new actors will participate. + * + * - actorID + * The actor's name, for use as the new actors' parentID. + */ +function CommonCreateExtraActors(aFactories, aPool) { + // Walk over global actors added by extensions. + for (let name in aFactories) { + let actor = this._extraActors[name]; + if (!actor) { + actor = aFactories[name].bind(null, this.conn, this); + actor.prototype = aFactories[name].prototype; + actor.parentID = this.actorID; + this._extraActors[name] = actor; + } + aPool.addActor(actor); + } +} + +/** + * Append the extra actors in |this._extraActors|, constructed by a prior call + * to CommonCreateExtraActors, to |aObject|. + * + * @param aObject + * The object to which the extra actors should be added, under the + * property names given in the |aFactories| table passed to + * CommonCreateExtraActors. + * + * @param this + * The BrowserRootActor or BrowserTabActor whose |_extraActors| table we + * should use; see above. + */ +function CommonAppendExtraActors(aObject) { + for (let name in this._extraActors) { + let actor = this._extraActors[name]; + aObject[name] = actor.actorID; + } +} + +/** + * Create a remote debugging protocol root actor. + * + * @param aConnection + * The DebuggerServerConnection whose root actor we are constructing. + * + * @param aParameters + * The properties of |aParameters| provide backing objects for the root + * actor's requests; if a given property is omitted from |aParameters|, the + * root actor won't implement the corresponding requests or notifications. + * Supported properties: + * + * - tabList: a live list (see below) of tab actors. If present, the + * new root actor supports the 'listTabs' request, providing the live + * list's elements as its tab actors, and sending 'tabListChanged' + * notifications when the live list's contents change. One actor in + * this list must have a true '.selected' property. + * + * - globalActorFactories: an object |A| describing further actors to + * attach to the 'listTabs' reply. This is the type accumulated by + * DebuggerServer.addGlobalActor. For each own property |P| of |A|, + * the root actor adds a property named |P| to the 'listTabs' + * reply whose value is the name of an actor constructed by + * |A[P]|. + * + * - onShutdown: a function to call when the root actor is disconnected. + * + * Instance properties: + * + * - applicationType: the string the root actor will include as the + * "applicationType" property in the greeting packet. By default, this + * is "browser". + * + * Live lists: + * + * A "live list", as used for the |tabList|, is an object that presents a + * list of actors, and also notifies its clients of changes to the list. A + * live list's interface is two properties: + * + * - iterator: a method that returns an iterator. A for-of loop will call + * this method to obtain an iterator for the loop, so if LL is + * a live list, one can simply write 'for (i of LL) ...'. + * + * - onListChanged: a handler called, with no arguments, when the set of + * values the iterator would produce has changed since the last + * time 'iterator' was called. This may only be set to null or a + * callable value (one for which the typeof operator returns + * 'function'). (Note that the live list will not call the + * onListChanged handler until the list has been iterated over + * once; if nobody's seen the list in the first place, nobody + * should care if its contents have changed!) + * + * When the list changes, the list implementation should ensure that any + * actors yielded in previous iterations whose referents (tabs) still exist + * get yielded again in subsequent iterations. If the underlying referent + * is the same, the same actor should be presented for it. + * + * The root actor registers an 'onListChanged' handler on the appropriate + * list when it may need to send the client 'tabListChanged' notifications, + * and is careful to remove the handler whenever it does not need to send + * such notifications (including when it is disconnected). This means that + * live list implementations can use the state of the handler property (set + * or null) to install and remove observers and event listeners. + * + * Note that, as the only way for the root actor to see the members of the + * live list is to begin an iteration over the list, the live list need not + * actually produce any actors until they are reached in the course of + * iteration: alliterative lazy live lists. + */ +function RootActor(aConnection, aParameters) { + this.conn = aConnection; + this._parameters = aParameters; + this._onTabListChanged = this.onTabListChanged.bind(this); + this._extraActors = {}; +} + +RootActor.prototype = { + constructor: RootActor, + applicationType: "browser", + + /** + * Return a 'hello' packet as specified by the Remote Debugging Protocol. + */ + sayHello: function() { + return { + from: "root", + applicationType: this.applicationType, + /* This is not in the spec, but it's used by tests. */ + testConnectionPrefix: this.conn.prefix, + traits: { + sources: true + } + }; + }, + + /** + * Disconnects the actor from the browser window. + */ + disconnect: function() { + /* Tell the live lists we aren't watching any more. */ + if (this._parameters.tabList) { + this._parameters.tabList.onListChanged = null; + } + if (typeof this._parameters.onShutdown === 'function') { + this._parameters.onShutdown(); + } + this._extraActors = null; + }, + + /* The 'listTabs' request and the 'tabListChanged' notification. */ + + /** + * Handles the listTabs request. The actors will survive until at least + * the next listTabs request. + */ + onListTabs: function() { + + let tabList = this._parameters.tabList; + if (!tabList) { + return { from: "root", error: "noTabs", + message: "This root actor has no browser tabs." }; + } + + /* + * Walk the tab list, accumulating the array of tab actors for the + * reply, and moving all the actors to a new ActorPool. We'll + * replace the old tab actor pool with the one we build here, thus + * retiring any actors that didn't get listed again, and preparing any + * new actors to receive packets. + */ + let newActorPool = new ActorPool(this.conn); + let tabActorList = []; + let selected; + for (let tabActor of tabList) { + if (tabActor.selected) { + selected = tabActorList.length; + } + tabActor.parentID = this.actorID; + newActorPool.addActor(tabActor); + tabActorList.push(tabActor); + } + + /* DebuggerServer.addGlobalActor support: create actors. */ + this._createExtraActors(this._parameters.globalActorFactories, newActorPool); + + /* + * Drop the old actorID -> actor map. Actors that still mattered were + * added to the new map; others will go away. + */ + if (this._tabActorPool) { + this.conn.removeActorPool(this._tabActorPool); + } + this._tabActorPool = newActorPool; + this.conn.addActorPool(this._tabActorPool); + + let reply = { + "from": "root", + "selected": selected || 0, + "tabs": [actor.grip() for (actor of tabActorList)] + }; + + /* DebuggerServer.addGlobalActor support: name actors in 'listTabs' reply. */ + this._appendExtraActors(reply); + + /* + * Now that we're actually going to report the contents of tabList to + * the client, we're responsible for letting the client know if it + * changes. + */ + tabList.onListChanged = this._onTabListChanged; + + return reply; + }, + + onTabListChanged: function () { + this.conn.send({ from:"root", type:"tabListChanged" }); + /* It's a one-shot notification; no need to watch any more. */ + this._parameters.tabList.onListChanged = null; + }, + + /* This is not in the spec, but it's used by tests. */ + onEcho: (aRequest) => aRequest, + + /* Support for DebuggerServer.addGlobalActor. */ + _createExtraActors: CommonCreateExtraActors, + _appendExtraActors: CommonAppendExtraActors, + + /* ThreadActor hooks. */ + + /** + * Prepare to enter a nested event loop by disabling debuggee events. + */ + preNest: function() { + // Disable events in all open windows. + let e = windowMediator.getEnumerator(null); + while (e.hasMoreElements()) { + let win = e.getNext(); + let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + windowUtils.suppressEventHandling(true); + windowUtils.suspendTimeouts(); + } + }, + + /** + * Prepare to exit a nested event loop by enabling debuggee events. + */ + postNest: function(aNestData) { + // Enable events in all open windows. + let e = windowMediator.getEnumerator(null); + while (e.hasMoreElements()) { + let win = e.getNext(); + let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + windowUtils.resumeTimeouts(); + windowUtils.suppressEventHandling(false); + } + }, + + /* ChromeDebuggerActor hooks. */ + + /** + * Add the specified actor to the default actor pool connection, in order to + * keep it alive as long as the server is. This is used by breakpoints in the + * thread and chrome debugger actors. + * + * @param actor aActor + * The actor object. + */ + addToParentPool: function(aActor) { + this.conn.addActor(aActor); + }, + + /** + * Remove the specified actor from the default actor pool. + * + * @param BreakpointActor aActor + * The actor object. + */ + removeFromParentPool: function(aActor) { + this.conn.removeActor(aActor); + } +} + +RootActor.prototype.requestTypes = { + "listTabs": RootActor.prototype.onListTabs, + "echo": RootActor.prototype.onEcho +}; diff --git a/scripting/javascript/bindings/js/debugger/actors/script.js b/scripting/javascript/bindings/js/debugger/actors/script.js new file mode 100644 index 000000000000..2e65052e2935 --- /dev/null +++ b/scripting/javascript/bindings/js/debugger/actors/script.js @@ -0,0 +1,2946 @@ +/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; js-indent-level: 2; -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/** + * JSD2 actors. + */ +/** + * Creates a ThreadActor. + * + * ThreadActors manage a JSInspector object and manage execution/inspection + * of debuggees. + * + * @param aHooks object + * An object with preNest and postNest methods for calling when entering + * and exiting a nested event loop, addToParentPool and + * removeFromParentPool methods for handling the lifetime of actors that + * will outlive the thread, like breakpoints. + * @param aGlobal object [optional] + * An optional (for content debugging only) reference to the content + * window. + */ +function ThreadActor(aHooks, aGlobal) +{ + this._state = "detached"; + this._frameActors = []; + this._environmentActors = []; + this._hooks = aHooks; + this.global = aGlobal; + + this.findGlobals = this.globalManager.findGlobals.bind(this); + this.onNewGlobal = this.globalManager.onNewGlobal.bind(this); + this.onNewSource = this.onNewSource.bind(this); + + this._options = { + useSourceMaps: false + }; +} + +/** + * The breakpoint store must be shared across instances of ThreadActor so that + * page reloads don't blow away all of our breakpoints. + */ +ThreadActor._breakpointStore = {}; + +ThreadActor.prototype = { + actorPrefix: "context", + + get state() { return this._state; }, + get attached() this.state == "attached" || + this.state == "running" || + this.state == "paused", + + get _breakpointStore() { return ThreadActor._breakpointStore; }, + + get threadLifetimePool() { + if (!this._threadLifetimePool) { + this._threadLifetimePool = new ActorPool(this.conn); + this.conn.addActorPool(this._threadLifetimePool); + this._threadLifetimePool.objectActors = new WeakMap(); + } + return this._threadLifetimePool; + }, + + get sources() { + if (!this._sources) { + this._sources = new ThreadSources(this, this._options.useSourceMaps, + this._allowSource, this.onNewSource); + } + return this._sources; + }, + + clearDebuggees: function TA_clearDebuggees() { + if (this.dbg) { + this.dbg.removeAllDebuggees(); + } + this.conn.removeActorPool(this._threadLifetimePool || undefined); + this._threadLifetimePool = null; + this._sources = null; + }, + + /** + * Add a debuggee global to the Debugger object. + */ + addDebuggee: function TA_addDebuggee(aGlobal) { + try { + this.dbg.addDebuggee(aGlobal); + } catch (e) { + // Ignore attempts to add the debugger's compartment as a debuggee. + dumpn("Ignoring request to add the debugger's compartment as a debuggee"); + } + }, + + /** + * Initialize the Debugger. + */ + _initDebugger: function TA__initDebugger() { + this.dbg = new Debugger(); + this.dbg.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this); + this.dbg.onDebuggerStatement = this.onDebuggerStatement.bind(this); + this.dbg.onNewScript = this.onNewScript.bind(this); + this.dbg.onNewGlobalObject = this.globalManager.onNewGlobal.bind(this); + // Keep the debugger disabled until a client attaches. + this.dbg.enabled = this._state != "detached"; + }, + + /** + * Remove a debuggee global from the JSInspector. + */ + removeDebugee: function TA_removeDebuggee(aGlobal) { + try { + this.dbg.removeDebuggee(aGlobal); + } catch(ex) { + // XXX: This debuggee has code currently executing on the stack, + // we need to save this for later. + } + }, + + /** + * Add the provided window and all windows in its frame tree as debuggees. + */ + _addDebuggees: function TA__addDebuggees(aWindow) { + this.addDebuggee(aWindow); + let frames = aWindow.frames; + if (frames) { + for (let i = 0; i < frames.length; i++) { + this._addDebuggees(frames[i]); + } + } + }, + + /** + * An object that will be used by ThreadActors to tailor their behavior + * depending on the debugging context being required (chrome or content). + */ + globalManager: { + findGlobals: function TA_findGlobals() { + this._addDebuggees(this.global); + }, + + /** + * A function that the engine calls when a new global object has been + * created. + * + * @param aGlobal Debugger.Object + * The new global object that was created. + */ + onNewGlobal: function TA_onNewGlobal(aGlobal) { + // Content debugging only cares about new globals in the contant window, + // like iframe children. + if (aGlobal.hostAnnotations && + aGlobal.hostAnnotations.type == "document" && + aGlobal.hostAnnotations.element === this.global) { + this.addDebuggee(aGlobal); + // Notify the client. + this.conn.send({ + from: this.actorID, + type: "newGlobal", + // TODO: after bug 801084 lands see if we need to JSONify this. + hostAnnotations: aGlobal.hostAnnotations + }); + } + } + }, + + disconnect: function TA_disconnect() { + dumpn("in ThreadActor.prototype.disconnect"); + if (this._state == "paused") { + this.onResume(); + } + + this._state = "exited"; + + this.clearDebuggees(); + + if (!this.dbg) { + return; + } + this.dbg.enabled = false; + this.dbg = null; + }, + + /** + * Disconnect the debugger and put the actor in the exited state. + */ + exit: function TA_exit() { + this.disconnect(); + }, + + // Request handlers + onAttach: function TA_onAttach(aRequest) { + if (this.state === "exited") { + return { type: "exited" }; + } + + if (this.state !== "detached") { + return { error: "wrongState" }; + } + + this._state = "attached"; + + update(this._options, aRequest.options || {}); + + if (!this.dbg) { + this._initDebugger(); + } + this.findGlobals(); + this.dbg.enabled = true; + try { + // Put ourselves in the paused state. + let packet = this._paused(); + if (!packet) { + return { error: "notAttached" }; + } + packet.why = { type: "attached" }; + + this._restoreBreakpoints(); + + // Send the response to the attach request now (rather than + // returning it), because we're going to start a nested event loop + // here. + this.conn.send(packet); + + // Start a nested event loop. + this._nest(); + + // We already sent a response to this request, don't send one + // now. + return null; + } catch (e) { + reportError(e); + return { error: "notAttached", message: e.toString() }; + } + }, + + onDetach: function TA_onDetach(aRequest) { + this.disconnect(); + dumpn("ThreadActor.prototype.onDetach: returning 'detached' packet"); + return { + type: "detached" + }; + }, + + onReconfigure: function TA_onReconfigure(aRequest) { + if (this.state == "exited") { + return { error: "wrongState" }; + } + + update(this._options, aRequest.options || {}); + // Clear existing sources, so they can be recreated on next access. + this._sources = null; + + return {}; + }, + + /** + * Pause the debuggee, by entering a nested event loop, and return a 'paused' + * packet to the client. + * + * @param Debugger.Frame aFrame + * The newest debuggee frame in the stack. + * @param object aReason + * An object with a 'type' property containing the reason for the pause. + * @param function onPacket + * Hook to modify the packet before it is sent. Feel free to return a + * promise. + */ + _pauseAndRespond: function TA__pauseAndRespond(aFrame, aReason, + onPacket=function (k) k) { + try { + let packet = this._paused(aFrame); + if (!packet) { + return undefined; + } + packet.why = aReason; + resolve(onPacket(packet)).then(this.conn.send.bind(this.conn)); + _lockVM(aFrame, aFrame.script); + return this._nest(); + } catch(e) { + let msg = "Got an exception during TA__pauseAndRespond: " + e + + ": " + e.stack; + // Cu.reportError(msg); + dumpn(msg); + return undefined; + } + }, + + /** + * Handle a protocol request to resume execution of the debuggee. + */ + onResume: function TA_onResume(aRequest) { + if (this._state !== "paused") { + return { + error: "wrongState", + message: "Can't resume when debuggee isn't paused. Current state is '" + + this._state + "'" + }; + } + + // In case of multiple nested event loops (due to multiple debuggers open in + // different tabs or multiple debugger clients connected to the same tab) + // only allow resumption in a LIFO order. + // if (DebuggerServer.xpcInspector.eventLoopNestLevel > 1) { + // let lastNestRequestor = DebuggerServer.xpcInspector.lastNestRequestor; + // if (lastNestRequestor.connection != this.conn) { + // return { error: "wrongOrder", + // message: "trying to resume in the wrong order.", + // lastPausedUrl: lastNestRequestor.url }; + // } + // } + + if (aRequest && aRequest.forceCompletion) { + // TODO: remove this when Debugger.Frame.prototype.pop is implemented in + // bug 736733. + if (typeof this.frame.pop != "function") { + return { error: "notImplemented", + message: "forced completion is not yet implemented." }; + } + + this.dbg.getNewestFrame().pop(aRequest.completionValue); + let packet = this._resumed(); + // DebuggerServer.xpcInspector.exitNestedEventLoop(); + _unlockVM(); + return { type: "resumeLimit", frameFinished: aRequest.forceCompletion }; + } + + if (aRequest && aRequest.resumeLimit) { + // Bind these methods because some of the hooks are called with 'this' + // set to the current frame. + let pauseAndRespond = (aFrame, onPacket=function (k) k) => { + this._pauseAndRespond(aFrame, { type: "resumeLimit" }, onPacket); + }; + let createValueGrip = this.createValueGrip.bind(this); + + let startFrame = this.youngestFrame; + let startLine; + if (this.youngestFrame.script) { + let offset = this.youngestFrame.offset; + startLine = this.youngestFrame.script.getOffsetLine(offset); + } + + // Define the JS hook functions for stepping. + + let onEnterFrame = aFrame => { + if (this.sources.isBlackBoxed(aFrame.script.url)) { + return undefined; + } + return pauseAndRespond(aFrame); + }; + + let thread = this; + + let onPop = function TA_onPop(aCompletion) { + // onPop is called with 'this' set to the current frame. + if (thread.sources.isBlackBoxed(this.script.url)) { + return undefined; + } + + // Note that we're popping this frame; we need to watch for + // subsequent step events on its caller. + this.reportedPop = true; + + return pauseAndRespond(this, (aPacket) => { + aPacket.why.frameFinished = {}; + if (!aCompletion) { + aPacket.why.frameFinished.terminated = true; + } else if (aCompletion.hasOwnProperty("return")) { + aPacket.why.frameFinished.return = createValueGrip(aCompletion.return); + } else if (aCompletion.hasOwnProperty("yield")) { + aPacket.why.frameFinished.return = createValueGrip(aCompletion.yield); + } else { + aPacket.why.frameFinished.throw = createValueGrip(aCompletion.throw); + } + return aPacket; + }); + }; + + let onStep = function TA_onStep() { + // onStep is called with 'this' set to the current frame. + + if (thread.sources.isBlackBoxed(this.script.url)) { + return undefined; + } + + // If we've changed frame or line, then report that. + if (this !== startFrame || + (this.script && + this.script.getOffsetLine(this.offset) != startLine)) { + return pauseAndRespond(this); + } + + // Otherwise, let execution continue. + return undefined; + }; + + let steppingType = aRequest.resumeLimit.type; + if (["step", "next", "finish"].indexOf(steppingType) == -1) { + return { error: "badParameterType", + message: "Unknown resumeLimit type" }; + } + // Make sure there is still a frame on the stack if we are to continue + // stepping. + let stepFrame = this._getNextStepFrame(startFrame); + if (stepFrame) { + switch (steppingType) { + case "step": + this.dbg.onEnterFrame = onEnterFrame; + // Fall through. + case "next": + stepFrame.onStep = onStep; + stepFrame.onPop = onPop; + break; + case "finish": + stepFrame.onPop = onPop; + } + } + + } + + if (aRequest && aRequest.pauseOnExceptions) { + this.dbg.onExceptionUnwind = this.onExceptionUnwind.bind(this); + } + let packet = this._resumed(); + _unlockVM(); + // DebuggerServer.xpcInspector.exitNestedEventLoop(); + return packet; + }, + + /** + * Helper method that returns the next frame when stepping. + */ + _getNextStepFrame: function TA__getNextStepFrame(aFrame) { + let stepFrame = aFrame.reportedPop ? aFrame.older : aFrame; + if (!stepFrame || !stepFrame.script) { + stepFrame = null; + } + return stepFrame; + }, + + onClientEvaluate: function TA_onClientEvaluate(aRequest) { + if (this.state !== "paused") { + return { error: "wrongState", + message: "Debuggee must be paused to evaluate code." }; + }; + + let frame = this._requestFrame(aRequest.frame); + if (!frame) { + return { error: "unknownFrame", + message: "Evaluation frame not found" }; + } + + if (!frame.environment) { + return { error: "notDebuggee", + message: "cannot access the environment of this frame." }; + }; + + // We'll clobber the youngest frame if the eval causes a pause, so + // save our frame now to be restored after eval returns. + // XXX: or we could just start using dbg.getNewestFrame() now that it + // works as expected. + let youngest = this.youngestFrame; + + // Put ourselves back in the running state and inform the client. + let resumedPacket = this._resumed(); + this.conn.send(resumedPacket); + + // Run the expression. + // XXX: test syntax errors + let completion = frame.eval(aRequest.expression); + + // Put ourselves back in the pause state. + let packet = this._paused(youngest); + packet.why = { type: "clientEvaluated", + frameFinished: this.createProtocolCompletionValue(completion) }; + + // Return back to our previous pause's event loop. + return packet; + }, + + onFrames: function TA_onFrames(aRequest) { + if (this.state !== "paused") { + return { error: "wrongState", + message: "Stack frames are only available while the debuggee is paused."}; + } + + let start = aRequest.start ? aRequest.start : 0; + let count = aRequest.count; + + // Find the starting frame... + let frame = this.youngestFrame; + let i = 0; + while (frame && (i < start)) { + frame = frame.older; + i++; + } + + // Return request.count frames, or all remaining + // frames if count is not defined. + let frames = []; + let promises = []; + for (; frame && (!count || i < (start + count)); i++, frame=frame.older) { + let form = this._createFrameActor(frame).form(); + form.depth = i; + frames.push(form); + + let promise = this.sources.getOriginalLocation(form.where.url, + form.where.line) + .then(function (aOrigLocation) { + form.where = aOrigLocation; + }); + promises.push(promise); + } + + return all(promises).then(function () { + return { frames: frames }; + }); + }, + + onReleaseMany: function TA_onReleaseMany(aRequest) { + if (!aRequest.actors) { + return { error: "missingParameter", + message: "no actors were specified" }; + } + + let res; + for each (let actorID in aRequest.actors) { + let actor = this.threadLifetimePool.get(actorID); + if (!actor) { + if (!res) { + res = { error: "notReleasable", + message: "Only thread-lifetime actors can be released." }; + } + continue; + } + actor.onRelease(); + } + return res ? res : {}; + }, + + /** + * Handle a protocol request to set a breakpoint. + */ + onSetBreakpoint: function TA_onSetBreakpoint(aRequest) { + if (this.state !== "paused") { + return { error: "wrongState", + message: "Breakpoints can only be set while the debuggee is paused."}; + } + + // XXX: `originalColumn` is never used. See bug 827639. + let { url: originalSource, + line: originalLine, + column: originalColumn } = aRequest.location; + + let locationPromise = this.sources.getGeneratedLocation(originalSource, + originalLine) + return locationPromise.then((aLocation) => { + let line = aLocation.line; + if (this.dbg.findScripts({ url: aLocation.url }).length == 0 || + line < 0 || + line == null) { + return { error: "noScript" }; + } + + // Add the breakpoint to the store for later reuse, in case it belongs to a + // script that hasn't appeared yet. + if (!this._breakpointStore[aLocation.url]) { + this._breakpointStore[aLocation.url] = []; + } + let scriptBreakpoints = this._breakpointStore[aLocation.url]; + scriptBreakpoints[line] = { + url: aLocation.url, + line: line, + column: aLocation.column + }; + + let response = this._setBreakpoint(aLocation); + // If the original location of our generated location is different from + // the original location we attempted to set the breakpoint on, we will + // need to know so that we can set actualLocation on the response. + let originalLocation = this.sources.getOriginalLocation(aLocation.url, + aLocation.line); + + return all([response, originalLocation]) + .then(([aResponse, {url, line}]) => { + if (aResponse.actualLocation) { + let actualOrigLocation = this.sources.getOriginalLocation( + aResponse.actualLocation.url, aResponse.actualLocation.line); + return actualOrigLocation.then(function ({ url, line }) { + if (url !== originalSource || line !== originalLine) { + aResponse.actualLocation = { url: url, line: line }; + } + return aResponse; + }); + } + + if (url !== originalSource || line !== originalLine) { + aResponse.actualLocation = { url: url, line: line }; + } + + return aResponse; + }); + }); + }, + + /** + * Set a breakpoint using the jsdbg2 API. If the line on which the breakpoint + * is being set contains no code, then the breakpoint will slide down to the + * next line that has runnable code. In this case the server breakpoint cache + * will be updated, so callers that iterate over the breakpoint cache should + * take that into account. + * + * @param object aLocation + * The location of the breakpoint as specified in the protocol. + */ + _setBreakpoint: function TA__setBreakpoint(aLocation) { + let breakpoints = this._breakpointStore[aLocation.url]; + + // Get or create the breakpoint actor for the given location + let actor; + if (breakpoints[aLocation.line].actor) { + actor = breakpoints[aLocation.line].actor; + } else { + actor = breakpoints[aLocation.line].actor = new BreakpointActor(this, { + url: aLocation.url, + line: aLocation.line + }); + this._hooks.addToParentPool(actor); + } + + // Find all scripts matching the given location + let scripts = this.dbg.findScripts(aLocation); + if (scripts.length == 0) { + return { + error: "noScript", + actor: actor.actorID + }; + } + + /** + * For each script, if the given line has at least one entry point, set + * breakpoint on the bytecode offet for each of them. + */ + let found = false; + for (let script of scripts) { + let offsets = script.getLineOffsets(aLocation.line); + if (offsets.length > 0) { + for (let offset of offsets) { + script.setBreakpoint(offset, actor); + } + actor.addScript(script, this); + found = true; + } + } + if (found) { + return { + actor: actor.actorID + }; + } + + /** + * If we get here, no breakpoint was set. This is because the given line + * has no entry points, for example because it is empty. As a fallback + * strategy, we try to set the breakpoint on the smallest line greater + * than or equal to the given line that as at least one entry point. + */ + + // Find all innermost scripts matching the given location + let scripts = this.dbg.findScripts({ + url: aLocation.url, + line: aLocation.line, + innermost: true + }); + + /** + * For each innermost script, look for the smallest line greater than or + * equal to the given line that has one or more entry points. If found, set + * a breakpoint on the bytecode offset for each of its entry points. + */ + let actualLocation; + let found = false; + for (let script of scripts) { + let offsets = script.getAllOffsets(); + for (let line = aLocation.line; line < offsets.length; ++line) { + if (offsets[line]) { + for (let offset of offsets[line]) { + script.setBreakpoint(offset, actor); + } + actor.addScript(script, this); + if (!actualLocation) { + actualLocation = { + url: aLocation.url, + line: line, + column: 0 + }; + } + found = true; + break; + } + } + } + if (found) { + if (breakpoints[actualLocation.line] && + breakpoints[actualLocation.line].actor) { + /** + * We already have a breakpoint actor for the actual location, so + * actor we created earlier is now redundant. Delete it, update the + * breakpoint store, and return the actor for the actual location. + */ + actor.onDelete(); + delete breakpoints[aLocation.line]; + return { + actor: breakpoints[actualLocation.line].actor.actorID, + actualLocation: actualLocation + }; + } else { + /** + * We don't have a breakpoint actor for the actual location yet. + * Instead or creating a new actor, reuse the actor we created earlier, + * and update the breakpoint store. + */ + actor.location = actualLocation; + breakpoints[actualLocation.line] = breakpoints[aLocation.line]; + delete breakpoints[aLocation.line]; + // WARNING: This overwrites aLocation.line + breakpoints[actualLocation.line].line = actualLocation.line; + return { + actor: actor.actorID, + actualLocation: actualLocation + }; + } + } + + /** + * If we get here, no line matching the given line was found, so just + * epically. + */ + return { + error: "noCodeAtLineColumn", + actor: actor.actorID + }; + }, + + /** + * Get the script and source lists from the debugger. + * + * TODO bug 637572: we should be dealing with sources directly, not inferring + * them through scripts. + */ + _discoverSources: function TA__discoverSources() { + // Only get one script per url. + let scriptsByUrl = {}; + for (let s of this.dbg.findScripts()) { + scriptsByUrl[s.url] = s; + } + + return all([this.sources.sourcesForScript(scriptsByUrl[s]) + for (s of Object.keys(scriptsByUrl))]); + }, + + onSources: function TA_onSources(aRequest) { + return this._discoverSources().then(() => { + return { + sources: [s.form() for (s of this.sources.iter())] + }; + }); + }, + + /** + * Disassociate all breakpoint actors from their scripts and clear the + * breakpoint handlers. This method can be used when the thread actor intends + * to keep the breakpoint store, but needs to clear any actual breakpoints, + * e.g. due to a page navigation. This way the breakpoint actors' script + * caches won't hold on to the Debugger.Script objects leaking memory. + */ + disableAllBreakpoints: function () { + for (let url in this._breakpointStore) { + for (let line in this._breakpointStore[url]) { + let bp = this._breakpointStore[url][line]; + bp.actor.removeScripts(); + } + } + }, + + /** + * Handle a protocol request to pause the debuggee. + */ + onInterrupt: function TA_onInterrupt(aRequest) { + if (this.state == "exited") { + return { type: "exited" }; + } else if (this.state == "paused") { + // TODO: return the actual reason for the existing pause. + return { type: "paused", why: { type: "alreadyPaused" } }; + } else if (this.state != "running") { + return { error: "wrongState", + message: "Received interrupt request in " + this.state + + " state." }; + } + + try { + // Put ourselves in the paused state. + let packet = this._paused(); + if (!packet) { + return { error: "notInterrupted" }; + } + packet.why = { type: "interrupted" }; + + // Send the response to the interrupt request now (rather than + // returning it), because we're going to start a nested event loop + // here. + this.conn.send(packet); + + // Start a nested event loop. + this._nest(); + + // We already sent a response to this request, don't send one + // now. + return null; + } catch (e) { + reportError(e); + return { error: "notInterrupted", message: e.toString() }; + } + }, + + /** + * Return the Debug.Frame for a frame mentioned by the protocol. + */ + _requestFrame: function TA_requestFrame(aFrameID) { + if (!aFrameID) { + return this.youngestFrame; + } + + if (this._framePool.has(aFrameID)) { + return this._framePool.get(aFrameID).frame; + } + + return undefined; + }, + + _paused: function TA_paused(aFrame) { + // We don't handle nested pauses correctly. Don't try - if we're + // paused, just continue running whatever code triggered the pause. + // We don't want to actually have nested pauses (although we + // have nested event loops). If code runs in the debuggee during + // a pause, it should cause the actor to resume (dropping + // pause-lifetime actors etc) and then repause when complete. + + if (this.state === "paused") { + return undefined; + } + + // Clear stepping hooks. + this.dbg.onEnterFrame = undefined; + this.dbg.onExceptionUnwind = undefined; + if (aFrame) { + aFrame.onStep = undefined; + aFrame.onPop = undefined; + } + + this._state = "paused"; + + // Save the pause frame (if any) as the youngest frame for + // stack viewing. + this.youngestFrame = aFrame; + + // Create the actor pool that will hold the pause actor and its + // children. + dbg_assert(!this._pausePool); + this._pausePool = new ActorPool(this.conn); + this.conn.addActorPool(this._pausePool); + + // Give children of the pause pool a quick link back to the + // thread... + this._pausePool.threadActor = this; + + // Create the pause actor itself... + dbg_assert(!this._pauseActor); + this._pauseActor = new PauseActor(this._pausePool); + this._pausePool.addActor(this._pauseActor); + + // Update the list of frames. + let poppedFrames = this._updateFrames(); + + // Send off the paused packet and spin an event loop. + let packet = { from: this.actorID, + type: "paused", + actor: this._pauseActor.actorID }; + if (aFrame) { + packet.frame = this._createFrameActor(aFrame).form(); + } + + if (poppedFrames) { + packet.poppedFrames = poppedFrames; + } + + return packet; + }, + + _nest: function TA_nest() { + if (this._hooks.preNest) { + var nestData = this._hooks.preNest(); + } + + let requestor = Object.create(null); + requestor.url = this._hooks.url; + requestor.connection = this.conn; + // DebuggerServer.xpcInspector.enterNestedEventLoop(requestor); + + dbg_assert(this.state === "running"); + + if (this._hooks.postNest) { + this._hooks.postNest(nestData) + } + + // "continue" resumption value. + return undefined; + }, + + _resumed: function TA_resumed() { + this._state = "running"; + + // Drop the actors in the pause actor pool. + this.conn.removeActorPool(this._pausePool); + + this._pausePool = null; + this._pauseActor = null; + this.youngestFrame = null; + + return { from: this.actorID, type: "resumed" }; + }, + + /** + * Expire frame actors for frames that have been popped. + * + * @returns A list of actor IDs whose frames have been popped. + */ + _updateFrames: function TA_updateFrames() { + let popped = []; + + // Create the actor pool that will hold the still-living frames. + let framePool = new ActorPool(this.conn); + let frameList = []; + + for each (let frameActor in this._frameActors) { + if (frameActor.frame.live) { + framePool.addActor(frameActor); + frameList.push(frameActor); + } else { + popped.push(frameActor.actorID); + } + } + + // Remove the old frame actor pool, this will expire + // any actors that weren't added to the new pool. + if (this._framePool) { + this.conn.removeActorPool(this._framePool); + } + + this._frameActors = frameList; + this._framePool = framePool; + this.conn.addActorPool(framePool); + + return popped; + }, + + _createFrameActor: function TA_createFrameActor(aFrame) { + if (aFrame.actor) { + return aFrame.actor; + } + + let actor = new FrameActor(aFrame, this); + this._frameActors.push(actor); + this._framePool.addActor(actor); + aFrame.actor = actor; + + return actor; + }, + + /** + * Create and return an environment actor that corresponds to the provided + * Debugger.Environment. + * @param Debugger.Environment aEnvironment + * The lexical environment we want to extract. + * @param object aPool + * The pool where the newly-created actor will be placed. + * @return The EnvironmentActor for aEnvironment or undefined for host + * functions or functions scoped to a non-debuggee global. + */ + createEnvironmentActor: + function TA_createEnvironmentActor(aEnvironment, aPool) { + if (!aEnvironment) { + return undefined; + } + + if (aEnvironment.actor) { + return aEnvironment.actor; + } + + let actor = new EnvironmentActor(aEnvironment, this); + this._environmentActors.push(actor); + aPool.addActor(actor); + aEnvironment.actor = actor; + + return actor; + }, + + /** + * Create a grip for the given debuggee value. If the value is an + * object, will create an actor with the given lifetime. + */ + createValueGrip: function TA_createValueGrip(aValue, aPool=false) { + if (!aPool) { + aPool = this._pausePool; + } + let type = typeof(aValue); + + if (type === "string" && this._stringIsLong(aValue)) { + return this.longStringGrip(aValue, aPool); + } + + if (type === "boolean" || type === "string" || type === "number") { + return aValue; + } + + if (aValue === null) { + return { type: "null" }; + } + + if (aValue === undefined) { + return { type: "undefined" } + } + + if (typeof(aValue) === "object") { + return this.objectGrip(aValue, aPool); + } + + dbg_assert(false, "Failed to provide a grip for: " + aValue); + return null; + }, + + /** + * Return a protocol completion value representing the given + * Debugger-provided completion value. + */ + createProtocolCompletionValue: + function TA_createProtocolCompletionValue(aCompletion) { + let protoValue = {}; + if ("return" in aCompletion) { + protoValue.return = this.createValueGrip(aCompletion.return); + } else if ("yield" in aCompletion) { + protoValue.return = this.createValueGrip(aCompletion.yield); + } else if ("throw" in aCompletion) { + protoValue.throw = this.createValueGrip(aCompletion.throw); + } else { + protoValue.terminated = true; + } + return protoValue; + }, + + /** + * Create a grip for the given debuggee object. + * + * @param aValue Debugger.Object + * The debuggee object value. + * @param aPool ActorPool + * The actor pool where the new object actor will be added. + */ + objectGrip: function TA_objectGrip(aValue, aPool) { + if (!aPool.objectActors) { + aPool.objectActors = new WeakMap(); + } + + if (aPool.objectActors.has(aValue)) { + return aPool.objectActors.get(aValue).grip(); + } else if (this.threadLifetimePool.objectActors.has(aValue)) { + return this.threadLifetimePool.objectActors.get(aValue).grip(); + } + + let actor = new PauseScopedObjectActor(aValue, this); + aPool.addActor(actor); + aPool.objectActors.set(aValue, actor); + return actor.grip(); + }, + + /** + * Create a grip for the given debuggee object with a pause lifetime. + * + * @param aValue Debugger.Object + * The debuggee object value. + */ + pauseObjectGrip: function TA_pauseObjectGrip(aValue) { + if (!this._pausePool) { + throw "Object grip requested while not paused."; + } + + return this.objectGrip(aValue, this._pausePool); + }, + + /** + * Extend the lifetime of the provided object actor to thread lifetime. + * + * @param aActor object + * The object actor. + */ + threadObjectGrip: function TA_threadObjectGrip(aActor) { + // We want to reuse the existing actor ID, so we just remove it from the + // current pool's weak map and then let pool.addActor do the rest. + aActor.registeredPool.objectActors.delete(aActor.obj); + this.threadLifetimePool.addActor(aActor); + this.threadLifetimePool.objectActors.set(aActor.obj, aActor); + }, + + /** + * Handle a protocol request to promote multiple pause-lifetime grips to + * thread-lifetime grips. + * + * @param aRequest object + * The protocol request object. + */ + onThreadGrips: function OA_onThreadGrips(aRequest) { + if (this.state != "paused") { + return { error: "wrongState" }; + } + + if (!aRequest.actors) { + return { error: "missingParameter", + message: "no actors were specified" }; + } + + for (let actorID of aRequest.actors) { + let actor = this._pausePool.get(actorID); + if (actor) { + this.threadObjectGrip(actor); + } + } + return {}; + }, + + /** + * Create a grip for the given string. + * + * @param aString String + * The string we are creating a grip for. + * @param aPool ActorPool + * The actor pool where the new actor will be added. + */ + longStringGrip: function TA_longStringGrip(aString, aPool) { + if (!aPool.longStringActors) { + aPool.longStringActors = {}; + } + + if (aPool.longStringActors.hasOwnProperty(aString)) { + return aPool.longStringActors[aString].grip(); + } + + let actor = new LongStringActor(aString, this); + aPool.addActor(actor); + aPool.longStringActors[aString] = actor; + return actor.grip(); + }, + + /** + * Create a long string grip that is scoped to a pause. + * + * @param aString String + * The string we are creating a grip for. + */ + pauseLongStringGrip: function TA_pauseLongStringGrip (aString) { + return this.longStringGrip(aString, this._pausePool); + }, + + /** + * Create a long string grip that is scoped to a thread. + * + * @param aString String + * The string we are creating a grip for. + */ + threadLongStringGrip: function TA_pauseLongStringGrip (aString) { + return this.longStringGrip(aString, this._threadLifetimePool); + }, + + /** + * Returns true if the string is long enough to use a LongStringActor instead + * of passing the value directly over the protocol. + * + * @param aString String + * The string we are checking the length of. + */ + _stringIsLong: function TA__stringIsLong(aString) { + return aString.length >= DebuggerServer.LONG_STRING_LENGTH; + }, + + // JS Debugger API hooks. + + /** + * A function that the engine calls when a call to a debug event hook, + * breakpoint handler, watchpoint handler, or similar function throws some + * exception. + * + * @param aException exception + * The exception that was thrown in the debugger code. + */ + uncaughtExceptionHook: function TA_uncaughtExceptionHook(aException) { + dumpn("Got an exception: " + aException.message + "\n" + aException.stack); + }, + + /** + * A function that the engine calls when a debugger statement has been + * executed in the specified frame. + * + * @param aFrame Debugger.Frame + * The stack frame that contained the debugger statement. + */ + onDebuggerStatement: function TA_onDebuggerStatement(aFrame) { + if (this.sources.isBlackBoxed(aFrame.script.url)) { + return undefined; + } + return this._pauseAndRespond(aFrame, { type: "debuggerStatement" }); + }, + + /** + * A function that the engine calls when an exception has been thrown and has + * propagated to the specified frame. + * + * @param aFrame Debugger.Frame + * The youngest remaining stack frame. + * @param aValue object + * The exception that was thrown. + */ + onExceptionUnwind: function TA_onExceptionUnwind(aFrame, aValue) { + if (this.sources.isBlackBoxed(aFrame.script.url)) { + return undefined; + } + try { + let packet = this._paused(aFrame); + if (!packet) { + return undefined; + } + + packet.why = { type: "exception", + exception: this.createValueGrip(aValue) }; + this.conn.send(packet); + return this._nest(); + } catch(e) { + log("Got an exception during TA_onExceptionUnwind: " + e + + ": " + e.stack); + return undefined; + } + }, + + /** + * A function that the engine calls when a new script has been loaded into the + * scope of the specified debuggee global. + * + * @param aScript Debugger.Script + * The source script that has been loaded into a debuggee compartment. + * @param aGlobal Debugger.Object + * A Debugger.Object instance whose referent is the global object. + */ + onNewScript: function TA_onNewScript(aScript, aGlobal) { + this._addScript(aScript); + this.sources.sourcesForScript(aScript); + }, + + onNewSource: function TA_onNewSource(aSource) { + this.conn.send({ + from: this.actorID, + type: "newSource", + source: aSource.form() + }); + }, + + /** + * Check if scripts from the provided source URL are allowed to be stored in + * the cache. + * + * @param aSourceUrl String + * The url of the script's source that will be stored. + * @returns true, if the script can be added, false otherwise. + */ + _allowSource: function TA__allowSource(aSourceUrl) { + // Ignore anything we don't have a URL for (eval scripts, for example). + if (!aSourceUrl) + return false; + // Ignore XBL bindings for content debugging. + if (aSourceUrl.indexOf("chrome://") == 0) { + return false; + } + // Ignore about:* pages for content debugging. + if (aSourceUrl.indexOf("about:") == 0) { + return false; + } + return true; + }, + + /** + * Restore any pre-existing breakpoints to the scripts that we have access to. + */ + _restoreBreakpoints: function TA__restoreBreakpoints() { + for (let s of this.dbg.findScripts()) { + this._addScript(s); + } + }, + + /** + * Add the provided script to the server cache. + * + * @param aScript Debugger.Script + * The source script that will be stored. + * @returns true, if the script was added; false otherwise. + */ + _addScript: function TA__addScript(aScript) { + if (!this._allowSource(aScript.url)) { + return false; + } + + // Set any stored breakpoints. + let existing = this._breakpointStore[aScript.url]; + if (existing) { + let endLine = aScript.startLine + aScript.lineCount - 1; + // Iterate over the lines backwards, so that sliding breakpoints don't + // affect the loop. + for (let line = existing.length - 1; line >= aScript.startLine; line--) { + let bp = existing[line]; + // Only consider breakpoints that are not already associated with + // scripts, and limit search to the line numbers contained in the new + // script. + if (bp && !bp.actor.scripts.length && line <= endLine) { + this._setBreakpoint(bp); + } + } + } + return true; + }, + +}; + +ThreadActor.prototype.requestTypes = { + "attach": ThreadActor.prototype.onAttach, + "detach": ThreadActor.prototype.onDetach, + "reconfigure": ThreadActor.prototype.onReconfigure, + "resume": ThreadActor.prototype.onResume, + "clientEvaluate": ThreadActor.prototype.onClientEvaluate, + "frames": ThreadActor.prototype.onFrames, + "interrupt": ThreadActor.prototype.onInterrupt, + "releaseMany": ThreadActor.prototype.onReleaseMany, + "setBreakpoint": ThreadActor.prototype.onSetBreakpoint, + "sources": ThreadActor.prototype.onSources, + "threadGrips": ThreadActor.prototype.onThreadGrips +}; + + +/** + * Creates a PauseActor. + * + * PauseActors exist for the lifetime of a given debuggee pause. Used to + * scope pause-lifetime grips. + * + * @param ActorPool aPool + * The actor pool created for this pause. + */ +function PauseActor(aPool) +{ + this.pool = aPool; +} + +PauseActor.prototype = { + actorPrefix: "pause" +}; + + +/** + * A base actor for any actors that should only respond receive messages in the + * paused state. Subclasses may expose a `threadActor` which is used to help + * determine when we are in a paused state. Subclasses should set their own + * "constructor" property if they want better error messages. You should never + * instantiate a PauseScopedActor directly, only through subclasses. + */ +function PauseScopedActor() +{ +} + +/** + * A function decorator for creating methods to handle protocol messages that + * should only be received while in the paused state. + * + * @param aMethod Function + * The function we are decorating. + */ +PauseScopedActor.withPaused = function PSA_withPaused(aMethod) { + return function () { + if (this.isPaused()) { + return aMethod.apply(this, arguments); + } else { + return this._wrongState(); + } + }; +}; + +PauseScopedActor.prototype = { + + /** + * Returns true if we are in the paused state. + */ + isPaused: function PSA_isPaused() { + // When there is not a ThreadActor available (like in the webconsole) we + // have to be optimistic and assume that we are paused so that we can + // respond to requests. + return this.threadActor ? this.threadActor.state === "paused" : true; + }, + + /** + * Returns the wrongState response packet for this actor. + */ + _wrongState: function PSA_wrongState() { + return { + error: "wrongState", + message: this.constructor.name + + " actors can only be accessed while the thread is paused." + }; + } +}; + + +/** + * A SourceActor provides information about the source of a script. + * + * @param aUrl String + * The url of the source we are representing. + * @param aThreadActor ThreadActor + * The current thread actor. + * @param aSourceMap SourceMapConsumer + * Optional. The source map that introduced this source, if available. + */ +function SourceActor(aUrl, aThreadActor, aSourceMap=null) { + this._threadActor = aThreadActor; + this._url = aUrl; + this._sourceMap = aSourceMap; +} + +SourceActor.prototype = { + constructor: SourceActor, + actorPrefix: "source", + + get threadActor() this._threadActor, + get url() this._url, + + form: function SA_form() { + return { + actor: this.actorID, + url: this._url, + isBlackBoxed: this.threadActor.sources.isBlackBoxed(this.url) + // TODO bug 637572: introductionScript + }; + }, + + disconnect: function LSA_disconnect() { + if (this.registeredPool && this.registeredPool.sourceActors) { + delete this.registeredPool.sourceActors[this.actorID]; + } + }, + + /** + * Handler for the "source" packet. + */ + onSource: function SA_onSource(aRequest) { + let sourceContent = null; + if (this._sourceMap) { + sourceContent = this._sourceMap.sourceContentFor(this._url); + } + + if (sourceContent) { + return { + from: this.actorID, + source: this.threadActor.createValueGrip( + sourceContent, this.threadActor.threadLifetimePool) + }; + } + + // XXX bug 865252: Don't load from the cache if this is a source mapped + // source because we can't guarantee that the cache has the most up to date + // content for this source like we can if it isn't source mapped. + return fetch(this._url, { loadFromCache: !this._sourceMap }) + .then((aSource) => { + return this.threadActor.createValueGrip( + aSource, this.threadActor.threadLifetimePool); + }) + .then((aSourceGrip) => { + return { + from: this.actorID, + source: aSourceGrip + }; + }, (aError) => { + let msg = "Got an exception during SA_onSource: " + aError + + "\n" + aError.stack; + // Cu.reportError(msg); + dumpn(msg); + return { + "from": this.actorID, + "error": "loadSourceError", + "message": "Could not load the source for " + this._url + "." + }; + }); + }, + + /** + * Handler for the "blackbox" packet. + */ + onBlackBox: function SA_onBlackBox(aRequest) { + this.threadActor.sources.blackBox(this.url); + let packet = { + from: this.actorID + }; + if (this.threadActor.state == "paused" + && this.threadActor.youngestFrame + && this.threadActor.youngestFrame.script.url == this.url) { + packet.pausedInSource = true; + } + return packet; + }, + + /** + * Handler for the "unblackbox" packet. + */ + onUnblackBox: function SA_onUnblackBox(aRequest) { + this.threadActor.sources.unblackBox(this.url); + return { + from: this.actorID + }; + } +}; + +SourceActor.prototype.requestTypes = { + "source": SourceActor.prototype.onSource, + "blackbox": SourceActor.prototype.onBlackBox, + "unblackbox": SourceActor.prototype.onUnblackBox +}; + + +/** + * Creates an actor for the specified object. + * + * @param aObj Debugger.Object + * The debuggee object. + * @param aThreadActor ThreadActor + * The parent thread actor for this object. + */ +function ObjectActor(aObj, aThreadActor) +{ + this.obj = aObj; + this.threadActor = aThreadActor; +} + +ObjectActor.prototype = { + actorPrefix: "obj", + + /** + * Returns a grip for this actor for returning in a protocol message. + */ + grip: function OA_grip() { + let g = { + "type": "object", + "class": this.obj.class, + "actor": this.actorID, + "extensible": this.obj.isExtensible(), + "frozen": this.obj.isFrozen(), + "sealed": this.obj.isSealed() + }; + + // Add additional properties for functions. + if (this.obj.class === "Function") { + if (this.obj.name) { + g.name = this.obj.name; + } else if (this.obj.displayName) { + g.displayName = this.obj.displayName; + } + + // Check if the developer has added a de-facto standard displayName + // property for us to use. + let desc = this.obj.getOwnPropertyDescriptor("displayName"); + if (desc && desc.value && typeof desc.value == "string") { + g.userDisplayName = this.threadActor.createValueGrip(desc.value); + } + } + + return g; + }, + + /** + * Releases this actor from the pool. + */ + release: function OA_release() { + if (this.registeredPool.objectActors) { + this.registeredPool.objectActors.delete(this.obj); + } + this.registeredPool.removeActor(this); + }, + + /** + * Handle a protocol request to provide the names of the properties defined on + * the object and not its prototype. + * + * @param aRequest object + * The protocol request object. + */ + onOwnPropertyNames: function OA_onOwnPropertyNames(aRequest) { + return { from: this.actorID, + ownPropertyNames: this.obj.getOwnPropertyNames() }; + }, + + /** + * Handle a protocol request to provide the prototype and own properties of + * the object. + * + * @param aRequest object + * The protocol request object. + */ + onPrototypeAndProperties: function OA_onPrototypeAndProperties(aRequest) { + let ownProperties = Object.create(null); + let names; + try { + names = this.obj.getOwnPropertyNames(); + } catch (ex) { + // The above can throw if this.obj points to a dead object. + // TODO: we should use Cu.isDeadWrapper() - see bug 885800. + return { from: this.actorID, + prototype: this.threadActor.createValueGrip(null), + ownProperties: ownProperties, + safeGetterValues: Object.create(null) }; + } + for (let name of names) { + ownProperties[name] = this._propertyDescriptor(name); + } + return { from: this.actorID, + prototype: this.threadActor.createValueGrip(this.obj.proto), + ownProperties: ownProperties, + safeGetterValues: this._findSafeGetterValues(ownProperties) }; + }, + + /** + * Find the safe getter values for the current Debugger.Object, |this.obj|. + * + * @private + * @param object aOwnProperties + * The object that holds the list of known ownProperties for + * |this.obj|. + * @return object + * An object that maps property names to safe getter descriptors as + * defined by the remote debugging protocol. + */ + _findSafeGetterValues: function OA__findSafeGetterValues(aOwnProperties) + { + let safeGetterValues = Object.create(null); + let obj = this.obj; + let level = 0; + + while (obj) { + let getters = this._findSafeGetters(obj); + for (let name of getters) { + // Avoid overwriting properties from prototypes closer to this.obj. Also + // avoid providing safeGetterValues from prototypes if property |name| + // is already defined as an own property. + if (name in safeGetterValues || + (obj != this.obj && name in aOwnProperties)) { + continue; + } + + let desc = null, getter = null; + try { + desc = obj.getOwnPropertyDescriptor(name); + getter = desc.get; + } catch (ex) { + // The above can throw if the cache becomes stale. + } + if (!getter) { + obj._safeGetters = null; + continue; + } + + let result = getter.call(this.obj); + if (result && !("throw" in result)) { + let getterValue = undefined; + if ("return" in result) { + getterValue = result.return; + } else if ("yield" in result) { + getterValue = result.yield; + } + // WebIDL attributes specified with the LenientThis extended attribute + // return undefined and should be ignored. + if (getterValue !== undefined) { + safeGetterValues[name] = { + getterValue: this.threadActor.createValueGrip(getterValue), + getterPrototypeLevel: level, + enumerable: desc.enumerable, + writable: level == 0 ? desc.writable : true, + }; + } + } + } + + obj = obj.proto; + level++; + } + + return safeGetterValues; + }, + + /** + * Find the safe getters for a given Debugger.Object. Safe getters are native + * getters which are safe to execute. + * + * @private + * @param Debugger.Object aObject + * The Debugger.Object where you want to find safe getters. + * @return Set + * A Set of names of safe getters. This result is cached for each + * Debugger.Object. + */ + _findSafeGetters: function OA__findSafeGetters(aObject) + { + if (aObject._safeGetters) { + return aObject._safeGetters; + } + + let getters = new Set(); + for (let name of aObject.getOwnPropertyNames()) { + let desc = null; + try { + desc = aObject.getOwnPropertyDescriptor(name); + } catch (e) { + // Calling getOwnPropertyDescriptor on wrapped native prototypes is not + // allowed (bug 560072). + } + if (!desc || desc.value !== undefined || !("get" in desc)) { + continue; + } + + let fn = desc.get; + if (fn && fn.callable && fn.class == "Function" && + fn.script === undefined) { + getters.add(name); + } + } + + aObject._safeGetters = getters; + return getters; + }, + + /** + * Handle a protocol request to provide the prototype of the object. + * + * @param aRequest object + * The protocol request object. + */ + onPrototype: function OA_onPrototype(aRequest) { + return { from: this.actorID, + prototype: this.threadActor.createValueGrip(this.obj.proto) }; + }, + + /** + * Handle a protocol request to provide the property descriptor of the + * object's specified property. + * + * @param aRequest object + * The protocol request object. + */ + onProperty: function OA_onProperty(aRequest) { + if (!aRequest.name) { + return { error: "missingParameter", + message: "no property name was specified" }; + } + + return { from: this.actorID, + descriptor: this._propertyDescriptor(aRequest.name) }; + }, + + /** + * A helper method that creates a property descriptor for the provided object, + * properly formatted for sending in a protocol response. + * + * @param string aName + * The property that the descriptor is generated for. + */ + _propertyDescriptor: function OA_propertyDescriptor(aName) { + let desc; + try { + desc = this.obj.getOwnPropertyDescriptor(aName); + } catch (e) { + // Calling getOwnPropertyDescriptor on wrapped native prototypes is not + // allowed (bug 560072). Inform the user with a bogus, but hopefully + // explanatory, descriptor. + return { + configurable: false, + writable: false, + enumerable: false, + value: e.name + }; + } + + let retval = { + configurable: desc.configurable, + enumerable: desc.enumerable + }; + + if ("value" in desc) { + retval.writable = desc.writable; + retval.value = this.threadActor.createValueGrip(desc.value); + } else { + if ("get" in desc) { + retval.get = this.threadActor.createValueGrip(desc.get); + } + if ("set" in desc) { + retval.set = this.threadActor.createValueGrip(desc.set); + } + } + return retval; + }, + + /** + * Handle a protocol request to provide the source code of a function. + * + * @param aRequest object + * The protocol request object. + */ + onDecompile: function OA_onDecompile(aRequest) { + if (this.obj.class !== "Function") { + return { error: "objectNotFunction", + message: "decompile request is only valid for object grips " + + "with a 'Function' class." }; + } + + return { from: this.actorID, + decompiledCode: this.obj.decompile(!!aRequest.pretty) }; + }, + + /** + * Handle a protocol request to provide the parameters of a function. + * + * @param aRequest object + * The protocol request object. + */ + onParameterNames: function OA_onParameterNames(aRequest) { + if (this.obj.class !== "Function") { + return { error: "objectNotFunction", + message: "'parameterNames' request is only valid for object " + + "grips with a 'Function' class." }; + } + + return { parameterNames: this.obj.parameterNames }; + }, + + /** + * Handle a protocol request to release a thread-lifetime grip. + * + * @param aRequest object + * The protocol request object. + */ + onRelease: function OA_onRelease(aRequest) { + this.release(); + return {}; + }, +}; + +ObjectActor.prototype.requestTypes = { + "parameterNames": ObjectActor.prototype.onParameterNames, + "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties, + "prototype": ObjectActor.prototype.onPrototype, + "property": ObjectActor.prototype.onProperty, + "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames, + "decompile": ObjectActor.prototype.onDecompile, + "release": ObjectActor.prototype.onRelease, +}; + + +/** + * Creates a pause-scoped actor for the specified object. + * @see ObjectActor + */ +function PauseScopedObjectActor() +{ + ObjectActor.apply(this, arguments); +} + +PauseScopedObjectActor.prototype = Object.create(PauseScopedActor.prototype); + +update(PauseScopedObjectActor.prototype, ObjectActor.prototype); + +update(PauseScopedObjectActor.prototype, { + constructor: PauseScopedObjectActor, + + onOwnPropertyNames: + PauseScopedActor.withPaused(ObjectActor.prototype.onOwnPropertyNames), + + onPrototypeAndProperties: + PauseScopedActor.withPaused(ObjectActor.prototype.onPrototypeAndProperties), + + onPrototype: PauseScopedActor.withPaused(ObjectActor.prototype.onPrototype), + onProperty: PauseScopedActor.withPaused(ObjectActor.prototype.onProperty), + onDecompile: PauseScopedActor.withPaused(ObjectActor.prototype.onDecompile), + + onParameterNames: + PauseScopedActor.withPaused(ObjectActor.prototype.onParameterNames), + + /** + * Handle a protocol request to provide the lexical scope of a function. + * + * @param aRequest object + * The protocol request object. + */ + onScope: PauseScopedActor.withPaused(function OA_onScope(aRequest) { + if (this.obj.class !== "Function") { + return { error: "objectNotFunction", + message: "scope request is only valid for object grips with a" + + " 'Function' class." }; + } + + let envActor = this.threadActor.createEnvironmentActor(this.obj.environment, + this.registeredPool); + if (!envActor) { + return { error: "notDebuggee", + message: "cannot access the environment of this function." }; + } + + return { from: this.actorID, scope: envActor.form() }; + }), + + /** + * Handle a protocol request to promote a pause-lifetime grip to a + * thread-lifetime grip. + * + * @param aRequest object + * The protocol request object. + */ + onThreadGrip: PauseScopedActor.withPaused(function OA_onThreadGrip(aRequest) { + this.threadActor.threadObjectGrip(this); + return {}; + }), + + /** + * Handle a protocol request to release a thread-lifetime grip. + * + * @param aRequest object + * The protocol request object. + */ + onRelease: PauseScopedActor.withPaused(function OA_onRelease(aRequest) { + if (this.registeredPool !== this.threadActor.threadLifetimePool) { + return { error: "notReleasable", + message: "Only thread-lifetime actors can be released." }; + } + + this.release(); + return {}; + }), +}); + +update(PauseScopedObjectActor.prototype.requestTypes, { + "scope": PauseScopedObjectActor.prototype.onScope, + "threadGrip": PauseScopedObjectActor.prototype.onThreadGrip, +}); + + +/** + * Creates an actor for the specied "very long" string. "Very long" is specified + * at the server's discretion. + * + * @param aString String + * The string. + */ +function LongStringActor(aString) +{ + this.string = aString; + this.stringLength = aString.length; +} + +LongStringActor.prototype = { + + actorPrefix: "longString", + + disconnect: function LSA_disconnect() { + // Because longStringActors is not a weak map, we won't automatically leave + // it so we need to manually leave on disconnect so that we don't leak + // memory. + if (this.registeredPool && this.registeredPool.longStringActors) { + delete this.registeredPool.longStringActors[this.actorID]; + } + }, + + /** + * Returns a grip for this actor for returning in a protocol message. + */ + grip: function LSA_grip() { + return { + "type": "longString", + "initial": this.string.substring( + 0, DebuggerServer.LONG_STRING_INITIAL_LENGTH), + "length": this.stringLength, + "actor": this.actorID + }; + }, + + /** + * Handle a request to extract part of this actor's string. + * + * @param aRequest object + * The protocol request object. + */ + onSubstring: function LSA_onSubString(aRequest) { + return { + "from": this.actorID, + "substring": this.string.substring(aRequest.start, aRequest.end) + }; + }, + + /** + * Handle a request to release this LongStringActor instance. + */ + onRelease: function LSA_onRelease() { + // TODO: also check if registeredPool === threadActor.threadLifetimePool + // when the web console moves aray from manually releasing pause-scoped + // actors. + if (this.registeredPool.longStringActors) { + delete this.registeredPool.longStringActors[this.actorID]; + } + this.registeredPool.removeActor(this); + return {}; + }, +}; + +LongStringActor.prototype.requestTypes = { + "substring": LongStringActor.prototype.onSubstring, + "release": LongStringActor.prototype.onRelease +}; + + +/** + * Creates an actor for the specified stack frame. + * + * @param aFrame Debugger.Frame + * The debuggee frame. + * @param aThreadActor ThreadActor + * The parent thread actor for this frame. + */ +function FrameActor(aFrame, aThreadActor) +{ + this.frame = aFrame; + this.threadActor = aThreadActor; +} + +FrameActor.prototype = { + actorPrefix: "frame", + + /** + * A pool that contains frame-lifetime objects, like the environment. + */ + _frameLifetimePool: null, + get frameLifetimePool() { + if (!this._frameLifetimePool) { + this._frameLifetimePool = new ActorPool(this.conn); + this.conn.addActorPool(this._frameLifetimePool); + } + return this._frameLifetimePool; + }, + + /** + * Finalization handler that is called when the actor is being evicted from + * the pool. + */ + disconnect: function FA_disconnect() { + this.conn.removeActorPool(this._frameLifetimePool); + this._frameLifetimePool = null; + }, + + /** + * Returns a frame form for use in a protocol message. + */ + form: function FA_form() { + let form = { actor: this.actorID, + type: this.frame.type }; + if (this.frame.type === "call") { + form.callee = this.threadActor.createValueGrip(this.frame.callee); + } + + if (this.frame.environment) { + let envActor = this.threadActor + .createEnvironmentActor(this.frame.environment, + this.frameLifetimePool); + form.environment = envActor.form(); + } + form.this = this.threadActor.createValueGrip(this.frame.this); + form.arguments = this._args(); + if (this.frame.script) { + form.where = { url: this.frame.script.url, + line: this.frame.script.getOffsetLine(this.frame.offset) }; + form.isBlackBoxed = this.threadActor.sources.isBlackBoxed(this.frame.script.url) + } + + if (!this.frame.older) { + form.oldest = true; + } + + return form; + }, + + _args: function FA__args() { + if (!this.frame.arguments) { + return []; + } + + return [this.threadActor.createValueGrip(arg) + for each (arg in this.frame.arguments)]; + }, + + /** + * Handle a protocol request to pop this frame from the stack. + * + * @param aRequest object + * The protocol request object. + */ + onPop: function FA_onPop(aRequest) { + // TODO: remove this when Debugger.Frame.prototype.pop is implemented + if (typeof this.frame.pop != "function") { + return { error: "notImplemented", + message: "Popping frames is not yet implemented." }; + } + + while (this.frame != this.threadActor.dbg.getNewestFrame()) { + this.threadActor.dbg.getNewestFrame().pop(); + } + this.frame.pop(aRequest.completionValue); + + // TODO: return the watches property when frame pop watch actors are + // implemented. + return { from: this.actorID }; + } +}; + +FrameActor.prototype.requestTypes = { + "pop": FrameActor.prototype.onPop, +}; + + +/** + * Creates a BreakpointActor. BreakpointActors exist for the lifetime of their + * containing thread and are responsible for deleting breakpoints, handling + * breakpoint hits and associating breakpoints with scripts. + * + * @param ThreadActor aThreadActor + * The parent thread actor that contains this breakpoint. + * @param object aLocation + * The location of the breakpoint as specified in the protocol. + */ +function BreakpointActor(aThreadActor, aLocation) +{ + this.scripts = []; + this.threadActor = aThreadActor; + this.location = aLocation; +} + +BreakpointActor.prototype = { + actorPrefix: "breakpoint", + + /** + * Called when this same breakpoint is added to another Debugger.Script + * instance, in the case of a page reload. + * + * @param aScript Debugger.Script + * The new source script on which the breakpoint has been set. + * @param ThreadActor aThreadActor + * The parent thread actor that contains this breakpoint. + */ + addScript: function BA_addScript(aScript, aThreadActor) { + this.threadActor = aThreadActor; + this.scripts.push(aScript); + }, + + /** + * Remove the breakpoints from associated scripts and clear the script cache. + */ + removeScripts: function () { + for (let script of this.scripts) { + script.clearBreakpoint(this); + } + this.scripts = []; + }, + + /** + * A function that the engine calls when a breakpoint has been hit. + * + * @param aFrame Debugger.Frame + * The stack frame that contained the breakpoint. + */ + hit: function BA_hit(aFrame) { + if (this.threadActor.sources.isBlackBoxed(this.location.url)) { + return undefined; + } + + // TODO: add the rest of the breakpoints on that line (bug 676602). + let reason = { type: "breakpoint", actors: [ this.actorID ] }; + return this.threadActor._pauseAndRespond(aFrame, reason, (aPacket) => { + log("pause callback ..."); + + let { url, line } = aPacket.frame.where; + return this.threadActor.sources.getOriginalLocation(url, line) + .then(function (aOrigPosition) { + aPacket.frame.where = aOrigPosition; + return aPacket; + }); + }); + }, + + /** + * Handle a protocol request to remove this breakpoint. + * + * @param aRequest object + * The protocol request object. + */ + onDelete: function BA_onDelete(aRequest) { + // Remove from the breakpoint store. + let scriptBreakpoints = this.threadActor._breakpointStore[this.location.url]; + delete scriptBreakpoints[this.location.line]; + this.threadActor._hooks.removeFromParentPool(this); + // Remove the actual breakpoint from the associated scripts. + this.removeScripts(); + + return { from: this.actorID }; + } +}; + +BreakpointActor.prototype.requestTypes = { + "delete": BreakpointActor.prototype.onDelete +}; + + +/** + * Creates an EnvironmentActor. EnvironmentActors are responsible for listing + * the bindings introduced by a lexical environment and assigning new values to + * those identifier bindings. + * + * @param Debugger.Environment aEnvironment + * The lexical environment that will be used to create the actor. + * @param ThreadActor aThreadActor + * The parent thread actor that contains this environment. + */ +function EnvironmentActor(aEnvironment, aThreadActor) +{ + this.obj = aEnvironment; + this.threadActor = aThreadActor; +} + +EnvironmentActor.prototype = { + actorPrefix: "environment", + + /** + * Return an environment form for use in a protocol message. + */ + form: function EA_form() { + let form = { actor: this.actorID }; + + // What is this environment's type? + if (this.obj.type == "declarative") { + form.type = this.obj.callee ? "function" : "block"; + } else { + form.type = this.obj.type; + } + + // Does this environment have a parent? + if (this.obj.parent) { + form.parent = (this.threadActor + .createEnvironmentActor(this.obj.parent, + this.registeredPool) + .form()); + } + + // Does this environment reflect the properties of an object as variables? + if (this.obj.type == "object" || this.obj.type == "with") { + form.object = this.threadActor.createValueGrip(this.obj.object); + } + + // Is this the environment created for a function call? + if (this.obj.callee) { + form.function = this.threadActor.createValueGrip(this.obj.callee); + } + + // Shall we list this environment's bindings? + if (this.obj.type == "declarative") { + form.bindings = this._bindings(); + } + + return form; + }, + + /** + * Return the identifier bindings object as required by the remote protocol + * specification. + */ + _bindings: function EA_bindings() { + let bindings = { arguments: [], variables: {} }; + + // TODO: this part should be removed in favor of the commented-out part + // below when getVariableDescriptor lands (bug 725815). + if (typeof this.obj.getVariable != "function") { + //if (typeof this.obj.getVariableDescriptor != "function") { + return bindings; + } + + let parameterNames; + if (this.obj.callee) { + parameterNames = this.obj.callee.parameterNames; + } + for each (let name in parameterNames) { + let arg = {}; + // TODO: this part should be removed in favor of the commented-out part + // below when getVariableDescriptor lands (bug 725815). + let desc = { + value: this.obj.getVariable(name), + configurable: false, + writable: true, + enumerable: true + }; + + // let desc = this.obj.getVariableDescriptor(name); + let descForm = { + enumerable: true, + configurable: desc.configurable + }; + if ("value" in desc) { + descForm.value = this.threadActor.createValueGrip(desc.value); + descForm.writable = desc.writable; + } else { + descForm.get = this.threadActor.createValueGrip(desc.get); + descForm.set = this.threadActor.createValueGrip(desc.set); + } + arg[name] = descForm; + bindings.arguments.push(arg); + } + + for each (let name in this.obj.names()) { + if (bindings.arguments.some(function exists(element) { + return !!element[name]; + })) { + continue; + } + + // TODO: this part should be removed in favor of the commented-out part + // below when getVariableDescriptor lands. + let desc = { + configurable: false, + writable: true, + enumerable: true + }; + try { + desc.value = this.obj.getVariable(name); + } catch (e) { + // Avoid "Debugger scope is not live" errors for |arguments|, introduced + // in bug 746601. + if (name != "arguments") { + throw e; + } + } + //let desc = this.obj.getVariableDescriptor(name); + let descForm = { + enumerable: true, + configurable: desc.configurable + }; + if ("value" in desc) { + descForm.value = this.threadActor.createValueGrip(desc.value); + descForm.writable = desc.writable; + } else { + descForm.get = this.threadActor.createValueGrip(desc.get); + descForm.set = this.threadActor.createValueGrip(desc.set); + } + bindings.variables[name] = descForm; + } + + return bindings; + }, + + /** + * Handle a protocol request to change the value of a variable bound in this + * lexical environment. + * + * @param aRequest object + * The protocol request object. + */ + onAssign: function EA_onAssign(aRequest) { + // TODO: enable the commented-out part when getVariableDescriptor lands + // (bug 725815). + /*let desc = this.obj.getVariableDescriptor(aRequest.name); + + if (!desc.writable) { + return { error: "immutableBinding", + message: "Changing the value of an immutable binding is not " + + "allowed" }; + }*/ + + try { + this.obj.setVariable(aRequest.name, aRequest.value); + } catch (e) { + if (e instanceof Debugger.DebuggeeWouldRun) { + return { error: "threadWouldRun", + cause: e.cause ? e.cause : "setter", + message: "Assigning a value would cause the debuggee to run" }; + } + // This should never happen, so let it complain loudly if it does. + throw e; + } + return { from: this.actorID }; + }, + + /** + * Handle a protocol request to fully enumerate the bindings introduced by the + * lexical environment. + * + * @param aRequest object + * The protocol request object. + */ + onBindings: function EA_onBindings(aRequest) { + return { from: this.actorID, + bindings: this._bindings() }; + } +}; + +EnvironmentActor.prototype.requestTypes = { + "assign": EnvironmentActor.prototype.onAssign, + "bindings": EnvironmentActor.prototype.onBindings +}; + +/** + * Override the toString method in order to get more meaningful script output + * for debugging the debugger. + */ +Debugger.Script.prototype.toString = function() { + let output = ""; + if (this.url) { + output += this.url; + } + if (typeof this.startLine != "undefined") { + output += ":" + this.startLine; + if (this.lineCount && this.lineCount > 1) { + output += "-" + (this.startLine + this.lineCount - 1); + } + } + if (this.strictMode) { + output += ":strict"; + } + return output; +}; + +/** + * Helper property for quickly getting to the line number a stack frame is + * currently paused at. + */ +Object.defineProperty(Debugger.Frame.prototype, "line", { + configurable: true, + get: function() { + if (this.script) { + return this.script.getOffsetLine(this.offset); + } else { + return null; + } + } +}); + + +/** + * Creates an actor for handling chrome debugging. ChromeDebuggerActor is a + * thin wrapper over ThreadActor, slightly changing some of its behavior. + * + * @param aConnection object + * The DebuggerServerConnection with which this ChromeDebuggerActor + * is associated. (Currently unused, but required to make this + * constructor usable with addGlobalActor.) + * + * @param aHooks object + * An object with preNest and postNest methods for calling when entering + * and exiting a nested event loop and also addToParentPool and + * removeFromParentPool methods for handling the lifetime of actors that + * will outlive the thread, like breakpoints. + */ +function ChromeDebuggerActor(aConnection, aHooks) +{ + ThreadActor.call(this, aHooks); +} + +ChromeDebuggerActor.prototype = Object.create(ThreadActor.prototype); + +update(ChromeDebuggerActor.prototype, { + constructor: ChromeDebuggerActor, + + // A constant prefix that will be used to form the actor ID by the server. + actorPrefix: "chromeDebugger", + + /** + * Override the eligibility check for scripts and sources to make sure every + * script and source with a URL is stored when debugging chrome. + */ + _allowSource: function(aSourceURL) !!aSourceURL, + + /** + * An object that will be used by ThreadActors to tailor their behavior + * depending on the debugging context being required (chrome or content). + * The methods that this object provides must be bound to the ThreadActor + * before use. + */ + globalManager: { + findGlobals: function CDA_findGlobals() { + // Add every global known to the debugger as debuggee. + this.dbg.addAllGlobalsAsDebuggees(); + }, + + /** + * A function that the engine calls when a new global object has been + * created. + * + * @param aGlobal Debugger.Object + * The new global object that was created. + */ + onNewGlobal: function CDA_onNewGlobal(aGlobal) { + this.addDebuggee(aGlobal); + // Notify the client. + this.conn.send({ + from: this.actorID, + type: "newGlobal", + // TODO: after bug 801084 lands see if we need to JSONify this. + hostAnnotations: aGlobal.hostAnnotations + }); + } + } +}); + + +/** + * Manages the sources for a thread. Handles source maps, locations in the + * sources, etc for ThreadActors. + */ +function ThreadSources(aThreadActor, aUseSourceMaps, aAllowPredicate, + aOnNewSource) { + this._thread = aThreadActor; + this._useSourceMaps = aUseSourceMaps; + this._allow = aAllowPredicate; + this._onNewSource = aOnNewSource; + + // source map URL --> promise of SourceMapConsumer + this._sourceMaps = Object.create(null); + // generated source url --> promise of SourceMapConsumer + this._sourceMapsByGeneratedSource = Object.create(null); + // original source url --> promise of SourceMapConsumer + this._sourceMapsByOriginalSource = Object.create(null); + // source url --> SourceActor + this._sourceActors = Object.create(null); + // original url --> generated url + this._generatedUrlsByOriginalUrl = Object.create(null); +} + +/** + * Must be a class property because it needs to persist across reloads, same as + * the breakpoint store. + */ +ThreadSources._blackBoxedSources = new Set(); + +ThreadSources.prototype = { + /** + * Return the source actor representing |aURL|, creating one if none + * exists already. Returns null if |aURL| is not allowed by the 'allow' + * predicate. + * + * Right now this takes a URL, but in the future it should + * take a Debugger.Source. See bug 637572. + * + * @param String aURL + * The source URL. + * @param optional SourceMapConsumer aSourceMap + * The source map that introduced this source, if any. + * @returns a SourceActor representing the source at aURL or null. + */ + source: function TS_source(aURL, aSourceMap=null) { + if (!this._allow(aURL)) { + return null; + } + + if (aURL in this._sourceActors) { + return this._sourceActors[aURL]; + } + + let actor = new SourceActor(aURL, this._thread, aSourceMap); + this._thread.threadLifetimePool.addActor(actor); + this._sourceActors[aURL] = actor; + try { + this._onNewSource(actor); + } catch (e) { + reportError(e); + } + return actor; + }, + + /** + * Return a promise of an array of source actors representing all the + * sources of |aScript|. + * + * If source map handling is enabled and |aScript| has a source map, then + * use it to find all of |aScript|'s *original* sources; return a promise + * of an array of source actors for those. + */ + sourcesForScript: function TS_sourcesForScript(aScript) { + if (!this._useSourceMaps || !aScript.sourceMapURL) { + return resolve([this.source(aScript.url)].filter(isNotNull)); + } + + return this.sourceMap(aScript) + .then((aSourceMap) => { + return [ + this.source(s, aSourceMap) for (s of aSourceMap.sources) + ]; + }) + .then(null, (e) => { + reportError(e); + delete this._sourceMaps[this._normalize(aScript.sourceMapURL, aScript.url)]; + delete this._sourceMapsByGeneratedSource[aScript.url]; + return [this.source(aScript.url)]; + }) + .then(function (aSources) { + return aSources.filter(isNotNull); + }); + }, + + /** + * Return a promise of a SourceMapConsumer for the source map for + * |aScript|; if we already have such a promise extant, return that. + * |aScript| must have a non-null sourceMapURL. + */ + sourceMap: function TS_sourceMap(aScript) { + if (aScript.url in this._sourceMapsByGeneratedSource) { + return this._sourceMapsByGeneratedSource[aScript.url]; + } + dbg_assert(aScript.sourceMapURL); + let sourceMapURL = this._normalize(aScript.sourceMapURL, aScript.url); + let map = this._fetchSourceMap(sourceMapURL) + .then((aSourceMap) => { + for (let s of aSourceMap.sources) { + this._generatedUrlsByOriginalUrl[s] = aScript.url; + this._sourceMapsByOriginalSource[s] = resolve(aSourceMap); + } + return aSourceMap; + }); + this._sourceMapsByGeneratedSource[aScript.url] = map; + return map; + }, + + /** + * Return a promise of a SourceMapConsumer for the source map located at + * |aAbsSourceMapURL|, which must be absolute. If there is already such a + * promise extant, return it. + */ + _fetchSourceMap: function TS__fetchSourceMap(aAbsSourceMapURL) { + if (aAbsSourceMapURL in this._sourceMaps) { + return this._sourceMaps[aAbsSourceMapURL]; + } else { + let promise = fetch(aAbsSourceMapURL).then((rawSourceMap) => { + let map = new SourceMapConsumer(rawSourceMap); + let base = aAbsSourceMapURL.replace(/\/[^\/]+$/, '/'); + if (base.indexOf("data:") !== 0) { + map.sourceRoot = map.sourceRoot + ? this._normalize(map.sourceRoot, base) + : base; + } + return map; + }); + this._sourceMaps[aAbsSourceMapURL] = promise; + return promise; + } + }, + + /** + * Returns a promise of the location in the original source if the source is + * source mapped, otherwise a promise of the same location. + * + * TODO bug 637572: take/return a column + */ + getOriginalLocation: function TS_getOriginalLocation(aSourceUrl, aLine) { + if (aSourceUrl in this._sourceMapsByGeneratedSource) { + return this._sourceMapsByGeneratedSource[aSourceUrl] + .then(function (aSourceMap) { + let { source, line } = aSourceMap.originalPositionFor({ + source: aSourceUrl, + line: aLine, + column: Infinity + }); + return { + url: source, + line: line + }; + }); + } + + // No source map + return resolve({ + url: aSourceUrl, + line: aLine + }); + }, + + /** + * Returns a promise of the location in the generated source corresponding to + * the original source and line given. + * + * When we pass a script S representing generated code to |sourceMap|, + * above, that returns a promise P. The process of resolving P populates + * the tables this function uses; thus, it won't know that S's original + * source URLs map to S until P is resolved. + * + * TODO bug 637572: take/return a column + */ + getGeneratedLocation: function TS_getGeneratedLocation(aSourceUrl, aLine) { + if (aSourceUrl in this._sourceMapsByOriginalSource) { + return this._sourceMapsByOriginalSource[aSourceUrl] + .then((aSourceMap) => { + let { line } = aSourceMap.generatedPositionFor({ + source: aSourceUrl, + line: aLine, + column: Infinity + }); + return { + url: this._generatedUrlsByOriginalUrl[aSourceUrl], + line: line + }; + }); + } + + // No source map + return resolve({ + url: aSourceUrl, + line: aLine + }); + }, + + /** + * Returns true if URL for the given source is black boxed. + * + * @param aURL String + * The URL of the source which we are checking whether it is black + * boxed or not. + */ + isBlackBoxed: function TS_isBlackBoxed(aURL) { + return ThreadSources._blackBoxedSources.has(aURL); + }, + + /** + * Add the given source URL to the set of sources that are black boxed. If the + * thread is currently paused and we are black boxing the yougest frame's + * source, this will force a step. + * + * @param aURL String + * The URL of the source which we are black boxing. + */ + blackBox: function TS_blackBox(aURL) { + ThreadSources._blackBoxedSources.add(aURL); + }, + + /** + * Remove the given source URL to the set of sources that are black boxed. + * + * @param aURL String + * The URL of the source which we are no longer black boxing. + */ + unblackBox: function TS_unblackBox(aURL) { + ThreadSources._blackBoxedSources.delete(aURL); + }, + + /** + * Normalize multiple relative paths towards the base paths on the right. + */ + _normalize: function TS__normalize(...aURLs) { + dbg_assert(aURLs.length > 1); + let base = Services.io.newURI(aURLs.pop(), null, null); + let url; + while ((url = aURLs.pop())) { + base = Services.io.newURI(url, null, base); + } + return base.spec; + }, + + iter: function TS_iter() { + for (let url in this._sourceActors) { + yield this._sourceActors[url]; + } + } +}; + +// Utility functions. + +/** + * Utility function for updating an object with the properties of another + * object. + * + * @param aTarget Object + * The object being updated. + * @param aNewAttrs Object + * The new attributes being set on the target. + */ +function update(aTarget, aNewAttrs) { + for (let key in aNewAttrs) { + let desc = Object.getOwnPropertyDescriptor(aNewAttrs, key); + + if (desc) { + Object.defineProperty(aTarget, key, desc); + } + } +} + +/** + * Returns true if its argument is not null. + */ +function isNotNull(aThing) { + return aThing !== null; +} + +/** + * Performs a request to load the desired URL and returns a promise. + * + * @param aURL String + * The URL we will request. + * @returns Promise + * A promise of the document at that URL, as a string. + * + * XXX: It may be better to use nsITraceableChannel to get to the sources + * without relying on caching when we can (not for eval, etc.): + * http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/ + */ +function fetch(aURL, aOptions={ loadFromCache: true }) { + let deferred = defer(); + let scheme; + let url = aURL.split(" -> ").pop(); + let charset; + let filePath = url; + // try { + // scheme = Services.io.extractScheme(url); + // } catch (e) { + // In the xpcshell tests, the script url is the absolute path of the test + // file, which will make a malformed URI error be thrown. Add the file + // scheme prefix ourselves. + url = "file://" + url; + // scheme = Services.io.extractScheme(url); + // } + scheme = "file"; + + switch (scheme) { + case "file": + case "chrome": + case "resource": + try { + // NetUtil.asyncFetch(url, function onFetch(aStream, aStatus) { + // if (!Components.isSuccessCode(aStatus)) { + // deferred.reject("Request failed: " + url); + // return; + // } + let cc = globalDebuggee.cc; + let fileUtils = cc.FileUtils.getInstance(); + let source = fileUtils.getStringFromFile(filePath);//NetUtil.readInputStreamToString(aStream, aStream.available()); + if (!source) + { + deferred.reject("Request failed: " + url); + } + else + { + deferred.resolve(source); + } + // aStream.close(); + // }); + } catch (ex) { + deferred.reject("Request failed: " + url); + } + break; + + default: + let channel; + try { + channel = Services.io.newChannel(url, null, null); + } catch (e if e.name == "NS_ERROR_UNKNOWN_PROTOCOL") { + // On Windows xpcshell tests, c:/foo/bar can pass as a valid URL, but + // newChannel won't be able to handle it. + url = "file:///" + url; + channel = Services.io.newChannel(url, null, null); + } + let chunks = []; + let streamListener = { + onStartRequest: function(aRequest, aContext, aStatusCode) { + if (!Components.isSuccessCode(aStatusCode)) { + deferred.reject("Request failed: " + url); + } + }, + onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) { + chunks.push(NetUtil.readInputStreamToString(aStream, aCount)); + }, + onStopRequest: function(aRequest, aContext, aStatusCode) { + if (!Components.isSuccessCode(aStatusCode)) { + deferred.reject("Request failed: " + url); + return; + } + + charset = channel.contentCharset; + deferred.resolve(chunks.join("")); + } + }; + + channel.loadFlags = aOptions.loadFromCache + ? channel.LOAD_FROM_CACHE + : channel.LOAD_BYPASS_CACHE; + channel.asyncOpen(streamListener, null); + break; + } + + return deferred.promise.then(function (source) { + return convertToUnicode(source, charset); + }); +} + +/** + * Convert a given string, encoded in a given character set, to unicode. + * + * @param string aString + * A string. + * @param string aCharset + * A character set. + */ +function convertToUnicode(aString, aCharset=null) { + // Decoding primitives. + // let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] + // .createInstance(Ci.nsIScriptableUnicodeConverter); + // try { + // converter.charset = aCharset || "UTF-8"; + // return converter.ConvertToUnicode(aString); + // } catch(e) { + return aString; + // } +} + +/** + * Report the given error in the error console and to stdout. + */ +function reportError(aError) { + // Cu.reportError(aError); + dumpn(aError.message + ":\n" + aError.stack); +} diff --git a/scripting/javascript/bindings/js/debugger/actors/string.js b/scripting/javascript/bindings/js/debugger/actors/string.js new file mode 100644 index 000000000000..8fc1eda94ec6 --- /dev/null +++ b/scripting/javascript/bindings/js/debugger/actors/string.js @@ -0,0 +1,145 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +let {Cu} = require("chrome"); +let {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {}); + +let promise = require("sdk/core/promise"); +let {Class} = require("sdk/core/heritage"); + +let protocol = require("devtools/server/protocol"); +let {method, Arg, Option, RetVal} = protocol; + +exports.LongStringActor = protocol.ActorClass({ + typeName: "longstractor", + + initialize: function(conn, str) { + protocol.Actor.prototype.initialize.call(this, conn); + this.str = str; + this.short = (this.str.length < DebuggerServer.LONG_STRING_LENGTH); + }, + + destroy: function() { + this.str = null; + protocol.Actor.prototype.destroy.call(this); + }, + + form: function() { + if (this.short) { + return this.str; + } + return { + type: "longString", + actor: this.actorID, + length: this.str.length, + initial: this.str.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH) + } + }, + + substring: method(function(start, end) { + return promise.resolve(this.str.substring(start, end)); + }, { + request: { + start: Arg(0), + end: Arg(1) + }, + response: { substring: RetVal() }, + }), + + release: method(function() { }, { release: true }) +}); + +/** + * When a LongString on the server is short enough to be passed + * as a full string, the client will get a ShortLongString instead of + * a LongStringFront. Its API should match. + * + * I'm very proud of this name. + */ +exports.ShortLongString = Class({ + initialize: function(str) { + this.str = str; + }, + + get length() { return this.str.length; }, + get initial() { return this.str; }, + string: function() { return promise.resolve(this.str) }, + + substring: function(start, end) { + return promise.resolve(this.str.substring(start, end)); + }, + + release: function() { + this.str = null; + return promise.resolve(undefined); + } +}) + +exports.LongStringFront = protocol.FrontClass(exports.LongStringActor, { + initialize: function(client, form) { + // Don't give the form by default, because we're being tricky and it might just + // be a string. + protocol.Front.prototype.initialize.call(this, client, null); + this.form(form); + }, + + destroy: function() { + this.initial = null; + this.length = null; + this.strPromise = null; + protocol.Front.prototype.destroy.call(this); + }, + + form: function(form) { + this.actorID = form.actorID; + this.initial = form.initial; + this.length = form.length; + }, + + string: function() { + if (!this.strPromise) { + let promiseRest = (thusFar) => { + if (thusFar.length === this.length) + return promise.resolve(thusFar); + else { + return this.substring(thusFar.length, + thusFar.length + DebuggerServer.LONG_STRING_READ_LENGTH) + .then((next) => promiseRest(thusFar + next)); + } + } + + this.strPromise = promiseRest(this.initial); + } + return this.strPromise; + } +}); + +// The long string actor needs some custom marshalling, because it is sometimes +// returned as a primitive rather than a complete form. + +let stringActorType = protocol.types.getType("longstractor"); +protocol.types.addType("longstring", { + _actor: true, + write: (value, context, detail) => { + if (!(context instanceof protocol.Actor)) { + throw Error("Passing a longstring as an argument isn't supported."); + } + if (value.short) { + return value.str; + } else { + return stringActorType.write(value, context, detail); + } + }, + read: (value, context, detail) => { + if (context instanceof protocol.Actor) { + throw Error("Passing a longstring as an argument isn't supported."); + } + if (typeof(value) === "string") { + return exports.ShortLongString(value); + } + return stringActorType.read(value, context, detail); + } +}); diff --git a/scripting/javascript/bindings/js/debugger/actors/styleeditor.js b/scripting/javascript/bindings/js/debugger/actors/styleeditor.js new file mode 100644 index 000000000000..d4ef50186dc4 --- /dev/null +++ b/scripting/javascript/bindings/js/debugger/actors/styleeditor.js @@ -0,0 +1,740 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +let Cc = Components.classes; +let Ci = Components.interfaces; +let Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); +Cu.import("resource://gre/modules/FileUtils.jsm"); + +let TRANSITION_CLASS = "moz-styleeditor-transitioning"; +let TRANSITION_DURATION_MS = 500; +let TRANSITION_RULE = "\ +:root.moz-styleeditor-transitioning, :root.moz-styleeditor-transitioning * {\ +transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \ +transition-delay: 0ms !important;\ +transition-timing-function: ease-out !important;\ +transition-property: all !important;\ +}"; + +let LOAD_ERROR = "error-load"; + +/** + * Creates a StyleEditorActor. StyleEditorActor provides remote access to the + * built-in style editor module. + */ +function StyleEditorActor(aConnection, aParentActor) +{ + this.conn = aConnection; + this._onDocumentLoaded = this._onDocumentLoaded.bind(this); + this._onSheetLoaded = this._onSheetLoaded.bind(this); + + if (aParentActor instanceof BrowserTabActor && + aParentActor.browser instanceof Ci.nsIDOMWindow) { + this._window = aParentActor.browser; + } + else if (aParentActor instanceof BrowserTabActor && + aParentActor.browser instanceof Ci.nsIDOMElement) { + this._window = aParentActor.browser.contentWindow; + } + else { + this._window = Services.wm.getMostRecentWindow("navigator:browser"); + } + + // keep a map of sheets-to-actors so we don't create two actors for one sheet + this._sheets = new Map(); + + this._actorPool = new ActorPool(this.conn); + this.conn.addActorPool(this._actorPool); +} + +StyleEditorActor.prototype = { + /** + * Actor pool for all of the actors we send to the client. + */ + _actorPool: null, + + /** + * The debugger server connection instance. + */ + conn: null, + + /** + * The content window we work with. + */ + get win() this._window, + + /** + * The current content document of the window we work with. + */ + get doc() this._window.document, + + /** + * A window object, usually the browser window + */ + _window: null, + + actorPrefix: "styleEditor", + + form: function() + { + return { actor: this.actorID }; + }, + + /** + * Destroy the current StyleEditorActor instance. + */ + disconnect: function() + { + if (this._observer) { + this._observer.disconnect(); + delete this._observer; + } + + this._sheets.clear(); + + this.conn.removeActorPool(this._actorPool); + this._actorPool = null; + this.conn = this._window = null; + }, + + /** + * Release an actor from our actor pool. + */ + releaseActor: function(actor) + { + if (this._actorPool) { + this._actorPool.removeActor(actor.actorID); + } + }, + + /** + * Get the BaseURI for the document. + * + * @return {object} JSON message to with BaseURI + */ + onGetBaseURI: function() { + return { baseURI: this.doc.baseURIObject }; + }, + + /** + * Called when target navigates to a new document. + * Adds load listeners to document. + */ + onNewDocument: function() { + // delete previous document's actors + this._clearStyleSheetActors(); + + // Note: listening for load won't be necessary once + // https://bugzilla.mozilla.org/show_bug.cgi?id=839103 is fixed + if (this.doc.readyState == "complete") { + this._onDocumentLoaded(); + } + else { + this.win.addEventListener("load", this._onDocumentLoaded, false); + } + return {}; + }, + + /** + * Event handler for document loaded event. Add actor for each stylesheet + * and send an event notifying of the load + */ + _onDocumentLoaded: function(event) { + if (event) { + this.win.removeEventListener("load", this._onDocumentLoaded, false); + } + + let documents = [this.doc]; + var forms = []; + for (let doc of documents) { + let sheetForms = this._addStyleSheets(doc.styleSheets); + forms = forms.concat(sheetForms); + // Recursively handle style sheets of the documents in iframes. + for (let iframe of doc.getElementsByTagName("iframe")) { + documents.push(iframe.contentDocument); + } + } + + this.conn.send({ + from: this.actorID, + type: "documentLoad", + styleSheets: forms + }); + }, + + /** + * Add all the stylesheets to the map and create an actor + * for each one if not already created. Send event that there + * are new stylesheets. + * + * @param {[DOMStyleSheet]} styleSheets + * Stylesheets to add + * @return {[object]} + * Array of forms for each StyleSheetActor created + */ + _addStyleSheets: function(styleSheets) + { + let sheets = []; + for (let i = 0; i < styleSheets.length; i++) { + let styleSheet = styleSheets[i]; + sheets.push(styleSheet); + + // Get all sheets, including imported ones + let imports = this._getImported(styleSheet); + sheets = sheets.concat(imports); + } + + let forms = sheets.map((sheet) => { + let actor = this._createStyleSheetActor(sheet); + return actor.form(); + }); + + return forms; + }, + + /** + * Get all the stylesheets @imported from a stylesheet. + * + * @param {DOMStyleSheet} styleSheet + * Style sheet to search + * @return {array} + * All the imported stylesheets + */ + _getImported: function(styleSheet) { + let imported = []; + + for (let i = 0; i < styleSheet.cssRules.length; i++) { + let rule = styleSheet.cssRules[i]; + if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) { + // Associated styleSheet may be null if it has already been seen due to + // duplicate @imports for the same URL. + if (!rule.styleSheet) { + continue; + } + imported.push(rule.styleSheet); + + // recurse imports in this stylesheet as well + imported = imported.concat(this._getImported(rule.styleSheet)); + } + else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) { + // @import rules must precede all others except @charset + break; + } + } + return imported; + }, + + /** + * Create a new actor for a style sheet, if it hasn't + * already been created, and return it. + * + * @param {DOMStyleSheet} aStyleSheet + * The style sheet to create an actor for. + * @return {StyleSheetActor} + * The actor for this style sheet + */ + _createStyleSheetActor: function(aStyleSheet) + { + if (this._sheets.has(aStyleSheet)) { + return this._sheets.get(aStyleSheet); + } + let actor = new StyleSheetActor(aStyleSheet, this); + this._actorPool.addActor(actor); + this._sheets.set(aStyleSheet, actor); + return actor; + }, + + /** + * Clear all the current stylesheet actors in map. + */ + _clearStyleSheetActors: function() { + for (let actor in this._sheets) { + this.releaseActor(this._sheets[actor]); + } + this._sheets.clear(); + }, + + /** + * Get the actors of all the stylesheets in the current document. + * + * @return {object} JSON message with the stylesheet actors' forms + */ + onGetStyleSheets: function() { + let forms = this._addStyleSheets(this.doc.styleSheets); + return { "styleSheets": forms }; + }, + + /** + * Handler for style sheet loading event. Add + * a new actor for the sheet and notify. + * + * @param {Event} event + */ + _onSheetLoaded: function(event) { + let style = event.target; + style.removeEventListener("load", this._onSheetLoaded, false); + + let actor = this._createStyleSheetActor(style.sheet); + this._notifyStyleSheetsAdded([actor.form()]); + }, + + /** + * Create a new style sheet in the document with the given text. + * Return an actor for it. + * + * @param {object} request + * Debugging protocol request object, with 'text property' + * @return {object} + * Object with 'styelSheet' property for form on new actor. + */ + onNewStyleSheet: function(request) { + let parent = this.doc.documentElement; + let style = this.doc.createElementNS("http://www.w3.org/1999/xhtml", "style"); + style.setAttribute("type", "text/css"); + + if (request.text) { + style.appendChild(this.doc.createTextNode(request.text)); + } + parent.appendChild(style); + + let actor = this._createStyleSheetActor(style.sheet); + return { styleSheet: actor.form() }; + } +}; + +/** + * The request types this actor can handle. + */ +StyleEditorActor.prototype.requestTypes = { + "getStyleSheets": StyleEditorActor.prototype.onGetStyleSheets, + "newStyleSheet": StyleEditorActor.prototype.onNewStyleSheet, + "getBaseURI": StyleEditorActor.prototype.onGetBaseURI, + "newDocument": StyleEditorActor.prototype.onNewDocument +}; + + +function StyleSheetActor(aStyleSheet, aParentActor) { + this.styleSheet = aStyleSheet; + this.parentActor = aParentActor; + + // text and index are unknown until source load + this.text = null; + this._styleSheetIndex = -1; + + this._transitionRefCount = 0; + + this._onSourceLoad = this._onSourceLoad.bind(this); + this._notifyError = this._notifyError.bind(this); + + // if this sheet has an @import, then it's rules are loaded async + let ownerNode = this.styleSheet.ownerNode; + if (ownerNode) { + let onSheetLoaded = function(event) { + ownerNode.removeEventListener("load", onSheetLoaded, false); + this._notifyPropertyChanged("ruleCount"); + }.bind(this); + + ownerNode.addEventListener("load", onSheetLoaded, false); + } +} + +StyleSheetActor.prototype = { + actorPrefix: "stylesheet", + + toString: function() { + return "[StyleSheetActor " + this.actorID + "]"; + }, + + disconnect: function() { + this.parentActor.releaseActor(this); + }, + + /** + * Window of target + */ + get win() { + return this.parentActor._window; + }, + + /** + * Document of target. + */ + get doc() { + return this.win.document; + }, + + /** + * Retrieve the index (order) of stylesheet in the document. + * + * @return number + */ + get styleSheetIndex() + { + if (this._styleSheetIndex == -1) { + for (let i = 0; i < this.doc.styleSheets.length; i++) { + if (this.doc.styleSheets[i] == this.styleSheet) { + this._styleSheetIndex = i; + break; + } + } + } + return this._styleSheetIndex; + }, + + /** + * Get the current state of the actor + * + * @return {object} + * With properties of the underlying stylesheet, plus 'text', + * 'styleSheetIndex' and 'parentActor' if it's @imported + */ + form: function() { + let form = { + actor: this.actorID, // actorID is set when this actor is added to a pool + href: this.styleSheet.href, + disabled: this.styleSheet.disabled, + title: this.styleSheet.title, + styleSheetIndex: this.styleSheetIndex, + text: this.text + } + + // get parent actor if this sheet was @imported + let parent = this.styleSheet.parentStyleSheet; + if (parent) { + form.parentActor = this.parentActor._sheets.get(parent); + } + + try { + form.ruleCount = this.styleSheet.cssRules.length; + } + catch(e) { + // stylesheet had an @import rule that wasn't loaded yet + } + + return form; + }, + + /** + * Toggle the disabled property of the style sheet + * + * @return {object} + * 'disabled' - the disabled state after toggling. + */ + onToggleDisabled: function() { + this.styleSheet.disabled = !this.styleSheet.disabled; + this._notifyPropertyChanged("disabled"); + + return { disabled: this.styleSheet.disabled }; + }, + + /** + * Send an event notifying that a property of the stylesheet + * has changed. + * + * @param {string} property + * Name of the changed property + */ + _notifyPropertyChanged: function(property) { + this.conn.send({ + from: this.actorID, + type: "propertyChange-" + this.actorID, + property: property, + value: this.form()[property] + }) + }, + + /** + * Send an event notifying that an error has occured + * + * @param {string} message + * Error message + */ + _notifyError: function(message) { + this.conn.send({ + from: this.actorID, + type: "error-" + this.actorID, + errorMessage: message + }); + }, + + /** + * Handler for event when the style sheet's full text has been + * loaded from its source. + * + * @param {string} source + * Text of the style sheet + * @param {[type]} charset + * Optional charset of the source + */ + _onSourceLoad: function(source, charset) { + this.text = this._decodeCSSCharset(source, charset || ""); + + this.conn.send({ + from: this.actorID, + type: "sourceLoad-" + this.actorID, + source: this.text + }); + }, + + /** + * Fetch the source of the style sheet from its URL + */ + onFetchSource: function() { + if (!this.styleSheet.href) { + // this is an inline