Update change log and spec for wrt-plugins-tizen_0.4.20
[framework/web/wrt-plugins-tizen.git] / src / Common / StandaloneConsole / StandaloneConsole.cpp
1 //
2 // Tizen Web Device API
3 // Copyright (c) 2013 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 #include <stdio.h>
19 #include <dlog.h>
20 #include <JavaScriptCore/JavaScript.h>
21 #include "StandaloneConsole.h"
22 #include "JSConsole.h"
23 #include <Ecore.h>
24 #include <GlobalContextManager.h>
25 #include <string>
26 #include <vector>
27 #include <iostream>
28 #include <termios.h>
29
30 #undef LOG_TAG
31 #define LOG_TAG "TIZEN_DEVICEAPI"
32
33 using namespace std;
34 using namespace DeviceAPI::Common;
35
36 namespace DeviceAPI {
37 namespace Test {
38
39 struct _Command{
40     char * mLine;
41     StandaloneConsole *mConsole;
42     pthread_mutex_t *mLock;
43     _Command(const char * cmd, StandaloneConsole *console, pthread_mutex_t* lock){
44         mLine = strdup(cmd);
45         mConsole = console;
46         mLock = lock;
47     }
48     ~_Command(){
49         free(mLine);
50     }
51     void run(){
52         mConsole->RunLine(mLine);
53         pthread_mutex_unlock(mLock);
54     }
55 };
56
57 struct CallbackData{
58     JSObjectRef callback;
59     int id;
60     StandaloneConsole *console;
61 };
62
63 static Eina_Bool tick(void *data){
64     return true;
65 }
66
67 static Eina_Bool commandDispath(void *data){
68     _Command *cmd = (_Command*)data;
69     cmd->run();
70     delete cmd;
71     return false;
72 }
73
74 Eina_Bool StandaloneConsole::timerCb(void *data){
75     CallbackData *callback = (CallbackData*)data;
76     StandaloneConsole *console = callback->console;
77     map<int,int>::iterator itr;
78     itr = console->mTimerMap.find(callback->id);
79     if( itr == console->mTimerMap.end() ){
80         JSValueUnprotect(console->getGlobalContext(), callback->callback);
81         delete callback;
82         return false;
83     }
84     if( itr->second == 0){
85         console->mTimerMap.erase(itr);
86         JSValueUnprotect(console->getGlobalContext(), callback->callback);
87         delete callback;
88         return false;
89     }
90     if( callback->callback != NULL){
91         JSObjectCallAsFunction(console->getGlobalContext(), callback->callback, NULL, 0, 0, NULL);
92     }
93
94     if( itr->second == 2 ){
95         console->mTimerMap.erase(itr);
96         JSValueUnprotect(console->getGlobalContext(), callback->callback);
97         delete callback;
98         return false;
99     }
100
101     return true;
102 }
103
104
105 JSValueRef StandaloneConsole::alert(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
106     if( argumentCount < 1 )
107         return JSValueMakeUndefined(ctx);
108
109
110     //JSContextRef globalCtx = GlobalContextManager::getInstance()->getGlobalContext(ctx);
111     //printf(" local : %p, global : %p \n", ctx, globalCtx);
112
113     JSStringRef str = JSValueToStringCopy(ctx, arguments[0], NULL);
114     if(str == NULL){
115         return JSValueMakeUndefined(ctx);
116     }
117     int n = JSStringGetLength(str);
118     {
119         char cstr[n+1];
120         JSStringGetUTF8CString(str, cstr,n+1);
121         printf("<alert>%s\n", cstr);
122     }
123     return JSValueMakeUndefined(ctx);
124 }
125
126 JSValueRef StandaloneConsole::setInterval(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
127     static int id = 0;
128     StandaloneConsole *console = static_cast<StandaloneConsole*>(JSObjectGetPrivate(thisObject));
129     if( argumentCount < 2 ){
130         if( exception != NULL){
131
132         }
133         return JSValueMakeUndefined(ctx);
134     }
135     int handleid = id++;
136     double interval = JSValueToNumber(ctx, arguments[1], NULL);
137     interval = interval/1000;
138
139     console->mTimerMap.insert(pair<int,int>(handleid, 1));
140     CallbackData *data = new CallbackData();
141     JSValueProtect(console->getGlobalContext(), arguments[0]);
142     data->callback = JSValueToObject(ctx, arguments[0], NULL);
143     data->id = handleid;
144     data->console = console;
145
146     ecore_timer_add( interval, StandaloneConsole::timerCb , data);
147     return JSValueMakeNumber(ctx, handleid);
148
149 }
150
151 JSValueRef StandaloneConsole::setTimeout(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
152     static int id = 0;
153     StandaloneConsole *console = static_cast<StandaloneConsole*>(JSObjectGetPrivate(thisObject));
154
155     if( argumentCount < 2 ){
156         if( exception != NULL){
157
158         }
159         return JSValueMakeUndefined(ctx);
160     }
161     int handleid = id++;
162     double interval = JSValueToNumber(ctx, arguments[1], NULL);
163     interval = interval/1000;
164
165     console->mTimerMap.insert(pair<int,int>(handleid, 2));
166     CallbackData *data = new CallbackData();
167     JSValueProtect(console->getGlobalContext(), arguments[0]);
168     data->callback = JSValueToObject(ctx, arguments[0], NULL);
169     data->id = handleid;
170     data->console = console;
171
172     ecore_timer_add( interval, StandaloneConsole::timerCb , data);
173     return JSValueMakeNumber(ctx, handleid);
174
175 }
176
177
178 JSValueRef StandaloneConsole::clearInterval(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
179     StandaloneConsole *console = static_cast<StandaloneConsole*>(JSObjectGetPrivate(thisObject));
180     if(console == NULL) return JSValueMakeUndefined(ctx);
181     if( argumentCount < 1 ){
182         printf("error clearInterval\n");
183         if( exception != NULL){
184
185         }
186         return JSValueMakeUndefined(ctx);
187     }
188
189     int handleid = JSValueToNumber(ctx, arguments[0], NULL);
190     map<int,int>::iterator it;
191     it = console->mTimerMap.find(handleid);
192     if( it != console->mTimerMap.end())
193         console->mTimerMap[handleid] = 0;
194     return JSValueMakeUndefined(ctx);
195 }
196
197
198 static JSValueRef test(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception){
199     return JSValueMakeUndefined(ctx);
200 }
201
202
203 static void setProperty(JSContextRef ctx , JSObjectRef object, const char *name, JSValueRef value, JSPropertyAttributes attributes)
204 {
205         JSStringRef propertyName = JSStringCreateWithUTF8CString(name);
206         JSObjectSetProperty(ctx, object, propertyName, value,attributes, NULL );
207         JSStringRelease(propertyName);
208 }
209
210 static JSValueRef getProperty(JSContextRef ctx , JSObjectRef object, const char *name){
211         JSValueRef value;
212         JSStringRef propertyName = JSStringCreateWithUTF8CString(name);
213         value = JSObjectGetProperty(ctx, object, propertyName, NULL);
214         JSStringRelease(propertyName);
215         return value;
216 }
217
218 static char * toString(JSContextRef ctx , JSValueRef jsV){
219     JSValueRef exception = NULL;
220     JSStringRef jsStr = JSValueToStringCopy(ctx, jsV, &exception);
221     if( exception != NULL )
222         return NULL;
223     int n = JSStringGetMaximumUTF8CStringSize(jsStr);
224     char *buf = new char[n+1];
225     JSStringGetUTF8CString(jsStr, buf, n+1);
226     JSStringRelease(jsStr);
227     return buf;
228 }
229
230 StandaloneConsole::StandaloneConsole():mGlobalContext(NULL),mGlobalObject(NULL){
231 }
232
233 StandaloneConsole::~StandaloneConsole(){
234 }
235
236 void StandaloneConsole::initialize(){
237     // Function table
238     JSStaticFunction functions[] = {
239         { "alert", StandaloneConsole::alert , kJSPropertyAttributeNone },
240         { "setInterval", StandaloneConsole::setInterval , kJSPropertyAttributeNone },
241         { "setTimeout", StandaloneConsole::setTimeout , kJSPropertyAttributeNone },
242         { "clearInterval", StandaloneConsole::clearInterval , kJSPropertyAttributeNone },
243         { "clearTimeout", StandaloneConsole::clearInterval , kJSPropertyAttributeNone },
244         { "test", test, kJSPropertyAttributeNone },
245         { 0, 0, 0 }
246     };
247
248     // Global class
249     JSClassDefinition def = {
250         0,                                 // current (and only) version is 0
251         kJSClassAttributeNone,   //attributes
252         "global",                        //class name
253         NULL,                            // parent class
254         NULL,                            //static values
255         functions,                      // static functions
256         NULL,                            // initialize
257         NULL,                            //finalize
258         NULL,                            //hasProperty
259         NULL,                            //getProperty
260         NULL,                            //setProperty
261         NULL,                            //deleteProperty
262         NULL,                            //getPropertyNames
263         NULL,                            // callAsConstructor
264         NULL,                            // constructor
265         NULL,
266         NULL                             // convertToType
267     };
268
269     JSClassRef globalClass = JSClassCreate(&def);
270
271     mGlobalContext = JSGlobalContextCreate(globalClass);
272     mGlobalObject = JSContextGetGlobalObject(mGlobalContext);
273     JSObjectSetPrivate(mGlobalObject, this);
274     JSObjectRef console = JSConsole::createJSObject(mGlobalContext);
275     setProperty(mGlobalContext, mGlobalObject, "console", console, kJSPropertyAttributeReadOnly);
276
277
278     //is it ecore bug? event was not invoke, it was added in another thread
279     ecore_timer_add(0.001, tick, NULL);
280 }
281
282
283 JSObjectRef StandaloneConsole::getGlobalObject(){
284     return mGlobalObject;
285 }
286
287 JSContextRef StandaloneConsole::getGlobalContext(){
288     return mGlobalContext;
289 }
290
291
292 JSValueRef StandaloneConsole::RunLineEx(const char* line, JSValueRef *exception){
293     JSStringRef jsScript = JSStringCreateWithUTF8CString(line);
294     JSValueRef ret = JSEvaluateScript(mGlobalContext, jsScript, NULL, NULL, 0, exception);
295     JSStringRelease(jsScript);
296     return ret;
297 }
298
299 JSValueRef StandaloneConsole::RunScriptEx(const char* path, JSValueRef *exception){
300
301     FILE* f = fopen(path, "r");
302     if( f == NULL ){
303         return NULL;
304     }
305
306     fseek(f, 0, SEEK_END);
307     int length = ftell(f);
308     fseek(f, 0, SEEK_SET);
309
310     if( length > 0 )
311     {
312         char buff[length];
313         memset(buff, 0, length);
314         int r = fread(buff, 1, length, f);
315         fclose(f);
316
317         if( r != length ){
318             printf("error read\n");
319             return JSValueMakeUndefined(mGlobalContext);
320         }
321         return RunLineEx(buff, exception);
322     }
323     fclose(f);
324     return JSValueMakeUndefined(mGlobalContext);
325 }
326
327 void StandaloneConsole::RunLine(const char * line){
328     JSValueRef exception = NULL;
329     JSValueRef v = RunLineEx(line, &exception);
330     reportingResult(v,exception);
331 }
332
333 void StandaloneConsole::RunScript(const char * path){
334     JSValueRef exception = NULL;
335     JSValueRef v = RunScriptEx(path, &exception);
336     reportingResult(v,exception);
337 }
338
339 void StandaloneConsole::GarbageCollect(){
340     printf("GarbageCollect\n");
341     JSGarbageCollect(mGlobalContext);
342 }
343 void StandaloneConsole::reportingResult(JSValueRef v, JSValueRef exception){
344     if( exception != NULL ){
345         char *errStr = toString(mGlobalContext, exception);
346         if( errStr != NULL ){
347             printf("< error - %s\n", errStr);
348             delete[] errStr;
349         }
350         JSObjectRef errObj = JSValueToObject(mGlobalContext, exception, NULL);
351         if( errObj != NULL ){
352             JSValueRef stack = getProperty(mGlobalContext, errObj, "stack");
353             char *stackStr = NULL;
354             if( !JSValueIsUndefined(mGlobalContext, stack) && (stackStr = toString(mGlobalContext, stack )) != NULL){
355                 printf("stack:%s\n", stackStr);
356                 delete[] stackStr;
357             }
358         }
359     }else{
360         char *resultStr = toString(mGlobalContext, v);
361         if( resultStr != NULL ){
362             printf("< %s\n", resultStr);
363             delete[] resultStr;
364         }
365     }
366 }
367
368 JSObjectRef StandaloneConsole::registModule(const char * name, JSClassRef module, void * priv){
369     JSObjectRef obj = JSObjectMake(mGlobalContext, module, priv);
370     setProperty(mGlobalContext, mGlobalObject, name, obj, kJSPropertyAttributeReadOnly);
371     return obj;
372 }
373
374 void StandaloneConsole::appendModule(const char * name, JSObjectRef module){
375     setProperty(mGlobalContext, mGlobalObject, name, module, kJSPropertyAttributeReadOnly);
376 }
377
378
379 int getch(void)
380 {
381     int ch;
382     struct termios buf;
383     struct termios save;
384
385     tcgetattr(0, &save);
386     buf = save;
387     buf.c_lflag &= ~(ICANON|ECHO);
388     buf.c_cc[VMIN] = 1;
389     buf.c_cc[VTIME] = 0;
390     tcsetattr(0, TCSAFLUSH, &buf);
391     ch = getchar();
392     tcsetattr(0, TCSAFLUSH, &save);
393     return ch;
394 }
395
396 struct termios gSave;
397
398 void onExit(void)
399 {
400     tcsetattr(0, TCSAFLUSH, &gSave);
401 }
402
403 class LineBuffer{
404     vector<string> mHistory;
405     string mLine;
406     int mHistoryIndex;
407     char mCurrentPos;
408 public:
409     LineBuffer():mHistoryIndex(0), mCurrentPos(0){
410         tcgetattr(0, &gSave);
411         atexit(onExit);
412     }
413     ~LineBuffer(){
414         tcsetattr(0, TCSAFLUSH, &gSave);
415     }
416
417     void backSpace( int length ){
418         for( int i =0 ; i < length ; i++){
419             putchar('\b');
420             putchar(' ');
421             putchar('\b');
422         }
423     }
424     void applyHistory( unsigned int index ){
425         if( mHistory.size() > index ){
426             mLine = mHistory[index];
427             mCurrentPos = mLine.size();
428         }
429     }
430     bool checkSpecialKeys(int a){
431         if( a == 8 ){
432             if( mLine.size() != 0){
433                 mCurrentPos--;
434                 mLine.erase(mCurrentPos);
435             }
436             return true;
437         }
438         if( a == 27 ){
439             a = getch(); // 91
440             a = getch();
441             switch( a ){
442                 case 65:
443                     //UP
444                     if( mHistoryIndex > 0 ){
445                         applyHistory(--mHistoryIndex);
446                     }
447                     break;
448                 case 66:
449                     //DOWN
450                     if( (unsigned)mHistoryIndex < mHistory.size() ){
451                         applyHistory(++mHistoryIndex);
452                     }
453                     break;
454                 case 67:
455                     //RIGHT
456                     break;
457                 case 68:
458                     //LEFT
459                     break;
460                 case 51:
461                     getch();
462                     backSpace(1);
463                     break;
464                 default:
465                     getch();
466             }
467
468             return true;
469         }
470         return false;
471     }
472
473     string Prompt(const char * prompt){
474         printf("%s", prompt);
475         mCurrentPos = 0;
476         mLine.clear();
477         while(1){
478             int a = getch();
479             backSpace(mLine.size());
480             if( a == 10 )
481                 break;
482
483             if(!checkSpecialKeys(a)){
484                 mLine.insert(mCurrentPos,1, a);
485                 mCurrentPos++;
486             }
487             cout << mLine;
488         }
489         cout << mLine;
490         if( mLine.size() > 0 ){
491             mHistory.push_back(mLine);
492             mHistoryIndex = mHistory.size();
493         }
494         return mLine;
495     }
496
497 };
498
499
500
501 void StandaloneConsole::commandline(StandaloneConsole* console){
502     pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
503     pthread_mutex_lock(&lock);
504     printf("command line mode ( \"quit\" for exit  )\n");
505     LineBuffer linebuff;
506     while(1){
507         string line = linebuff.Prompt(">");
508         printf("\n");
509
510         if( line == "quit" )
511             break;
512         if( line == "gc" ){
513             console->GarbageCollect();
514             continue;
515         }
516         if( line.size() == 0 )
517             continue;
518         _Command *cmd = new _Command(line.c_str(), console, &lock);
519         // for thread safety
520         ecore_idler_add(commandDispath, cmd);
521         pthread_mutex_lock(&lock);
522     }
523 }
524
525
526 }
527 }
528