1 /*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Sablotron XSLT Processor.
13 *
14 * The Initial Developer of the Original Code is Ginger Alliance Ltd.
15 * Portions created by Ginger Alliance are Copyright (C) 2000-2002
16 * Ginger Alliance Ltd. All Rights Reserved.
17 *
18 * Contributor(s): Han Qi
19 *
20 * Alternatively, the contents of this file may be used under the
21 * terms of the GNU General Public License Version 2 or later (the
22 * "GPL"), in which case the provisions of the GPL are applicable
23 * instead of those above. If you wish to allow use of your
24 * version of this file only under the terms of the GPL and not to
25 * allow others to use your version of this file under the MPL,
26 * indicate your decision by deleting the provisions above and
27 * replace them with the notice and other provisions required by
28 * the GPL. If you do not delete the provisions above, a recipient
29 * may use your version of this file under either the MPL or the
30 * GPL.
31 */
32
33 #include "jsext.h"
34
35 #ifdef ENABLE_JS
36
37 #include "expr.h"
38 #include "error.h"
39 #include "context.h"
40 #include "guard.h"
41 #include "domprovider.h"
42 #include "jsdom.h"
43
44 /************* extern constants ******************/
45 const char* theArrayProtoRoot = "_array_prototype_root_";
46
47 /*************** JS error reporter ******************/
48
SJSErrorReporter(JSContext * cx,const char * message,JSErrorReport * report)49 void SJSErrorReporter(JSContext *cx, const char *message,
50 JSErrorReport *report)
51 {
52 JSContextItem *item = (JSContextItem*)JS_GetContextPrivate(cx);
53 if (item) {
54 if (item -> errInfo.message) delete item -> errInfo.message;
55 if (item -> errInfo.token) delete item -> errInfo.token;
56 item -> errInfo.message = NULL;
57 item -> errInfo.token = NULL;
58 if (message) {
59 item -> errInfo.message = new char[strlen(message) + 1];
60 strcpy(item -> errInfo.message, message);
61 }
62 if (report -> tokenptr) {
63 item -> errInfo.token = new char[strlen(report -> tokenptr) + 1];
64 strcpy(item -> errInfo.token, report -> tokenptr);
65 }
66 item -> errInfo.line = report -> lineno;
67 item -> errInfo.errNumber = report -> errorNumber;
68 }
69 }
70
71 /************************ MANAGER *******************/
72
73 JSRuntime_Sab* gJSRuntime;
74
getRuntime()75 JSRuntime_Sab* JSManager::getRuntime()
76 {
77 if (! gJSRuntime ) {
78 gJSRuntime = JS_NewRuntime(JS_RUNTIME_SIZE);
79 }
80 return gJSRuntime;
81 }
82
createContext(int size)83 JSContext_Sab* JSManager::createContext(int size /* =JS_CONTEXT_SIZE */)
84 {
85 return JS_NewContext(getRuntime(), size);
86 }
87
88
finalize()89 void JSManager::finalize()
90 {
91 if ( gJSRuntime ) JS_DestroyRuntime(gJSRuntime);
92 }
93
94 /******************************delegates etc. ******************/
95
JS_METHOD(jsglobalLog)96 JS_METHOD(jsglobalLog) {
97 JSContextItem *item = (JSContextItem*)JS_GetContextPrivate(cx);
98 Situation *sit = item -> proc -> recallSituation();
99 JSString *str = JS_ValueToString(cx, argv[0]);
100 char *msg = JS_GetStringBytes(str);
101 sit -> message(MT_LOG, L_JS_LOG, (const char*) msg, (const char*)NULL);
102 return TRUE;
103 }
104
105 /****************************************************************
106
107 JSContexts
108
109 ****************************************************************/
110
JSContextItem(JSContext_Sab * cx_,Str & uri_,Processor * proc_)111 JSContextItem::JSContextItem(JSContext_Sab *cx_, Str &uri_, Processor *proc_)
112 : cx(cx_), uri(uri_), proc(proc_)
113 {
114 //cls = NULL;
115 errInfo.message = NULL;
116 errInfo.token = NULL;
117 node = NULL;
118 domex = NULL;
119 domimpl = NULL;
120 nlclass = NULL;
121 array_proto = NULL;
122 };
123
~JSContextItem()124 JSContextItem::~JSContextItem()
125 {
126 names.freeall(FALSE);
127 if (errInfo.message) delete errInfo.message;
128 if (errInfo.token) delete errInfo.token;
129 if (cx)
130 {
131 #ifdef ENABLE_JS_THREADS
132 JS_ResumeRequest(cx);
133 #endif
134 if (array_proto) JS_RemoveRoot(cx, &array_proto);
135 #ifdef ENABLE_JS_THREADS
136 JS_EndRequest(cx);
137 #endif
138 JS_GC(cx);
139 JS_DestroyContext(cx);
140 }
141 //if (cls) delete cls;
142 }
143
144 //js class for global object
145 JSClass sabGlobalClass = {
146 "global",
147 JSCLASS_HAS_PRIVATE,
148 JS_PropertyStub, JS_PropertyStub,
149 JS_PropertyStub,
150 JS_PropertyStub,
151 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
152 JS_FinalizeStub, 0, 0, NULL, NULL, NULL, NULL, 0, 0
153 };
154
find(Str uri,Bool canCreate)155 JSContextItem* JSContextList::find(Str uri, Bool canCreate /*TRUE*/)
156 {
157 JSContextItem *item = NULL;
158 for (int i = 0; i < number(); i++)
159 {
160 if ((*this)[i] -> uri == uri) item = (*this)[i];
161 }
162 if (!item && canCreate)
163 {
164 JSContext_Sab *cx;
165 cx = JSManager::createContext();
166 #ifdef ENABLE_JS_THREADS
167 JS_BeginRequest(cx);
168 #endif
169 item = new JSContextItem(cx, uri, proc);
170 append(item);
171 //initalize context
172 //JSClass *cls = SJSGetGlobalClass();
173 //item -> cls = cls;
174 JSObject *global = JS_NewObject(cx, &sabGlobalClass, NULL, NULL);
175 JS_InitStandardClasses(cx, global);
176 //set debugging globals
177 JS_DefineFunction(cx, global, "log", jsglobalLog, 1, 0);
178 //private data and error handling
179 JS_SetContextPrivate(cx, (void*)item);
180 JS_SetErrorReporter(cx, SJSErrorReporter);
181 //create persistent objects
182 jsdom_delegateDOM(cx);
183 #ifdef ENABLE_JS_THREADS
184 JS_Suspendrequest(cx);
185 #endif
186 }
187 return item;
188 }
189
190 /************************************************************/
191 // JSExternalPrivate
192 /************************************************************/
193
JSExternalPrivate(void * p_,void * v_)194 JSExternalPrivate::JSExternalPrivate(void *p_, void *v_)
195 : priv(p_), value(v_), refcnt(1)
196 {
197 if (value)
198 {
199 JS_AddRoot((JSContext*)priv, &value);
200 }
201 };
202
~JSExternalPrivate()203 JSExternalPrivate::~JSExternalPrivate()
204 {
205 if (value)
206 {
207 JS_RemoveRoot((JSContext*)priv, &value);
208 }
209 }
210
211 /**************** ordinary functions */
212
sjs_instanceOf(JSContext_Sab * cx,JSObject_Sab * obj,JSClass_Sab * cls)213 Bool sjs_instanceOf(JSContext_Sab *cx, JSObject_Sab *obj, JSClass_Sab *cls)
214 {
215 JSObject *o = obj;
216 while (o)
217 {
218 JSClass *c = JS_GET_CLASS(cx, o);
219 if (c && c == cls) return TRUE;
220 o = JS_GetPrototype(cx, o);
221 }
222 return FALSE;
223 }
224
225 // const char* gClassName = "global";
226
227 // JSClass* SJSGetGlobalClass()
228 // {
229 // #ifdef HAVE_JSAPI_H
230 // JSClass *ret = new JSClass();
231 // ret -> name = gClassName;
232 // ret -> flags = 0;
233 // ret -> addProperty = JS_PropertyStub;
234 // ret -> delProperty = JS_PropertyStub;
235 // ret -> getProperty = JS_PropertyStub;
236 // ret -> setProperty = JS_PropertyStub;
237 // ret -> enumerate = JS_EnumerateStub;
238 // ret -> resolve = JS_ResolveStub;
239 // ret -> convert = JS_ConvertStub;
240 // ret -> finalize = JS_FinalizeStub;
241
242 // return ret;
243 // #else
244 // return NULL;
245 // #endif
246 // }
247
SJSEvaluate(JSContextItem & item,DStr & script)248 Bool SJSEvaluate(JSContextItem &item, DStr &script)
249 {
250 jsval rval;
251 JSBool status;
252 JSContext *cx = item.cx;
253
254 #ifdef ENABLE_JS_THREADS
255 JS_ResumeRequest(cx);
256 #endif
257
258 /*
259 int len = utf8StrLength((const char*) script);
260 wchar_t *ucscript = new wchar_t[len + 1];
261 utf8ToUtf16(ucscript, (const char*) script);
262
263 status = JS_EvaluateUCScript(cx, JS_GetGlobalObject(cx),
264 (jschar*)ucscript, len,
265 "stylesheet", 0, &rval);
266 delete ucscript;
267 */
268
269 char *scr = (char *) script;
270 status = JS_EvaluateScript(cx, JS_GetGlobalObject(cx),
271 scr, strlen(scr),
272 "stylesheet", 0, &rval);
273 if ( JS_IsExceptionPending(cx) )
274 {
275 JS_ClearPendingException(cx);
276 }
277
278 if (status) {
279 //read all functions
280 JSIdArray *arr = JS_Enumerate(cx, JS_GetGlobalObject(cx));
281 item.names.freeall(FALSE);
282 if (arr) {
283 for (int i = 0; i < arr -> length; i++) {
284 jsval propname;
285 jsid id = arr -> vector[i];
286 JS_IdToValue(cx, id, &propname);
287
288 JSString *str = JS_ValueToString(cx, propname);
289 jsval prop;
290 JS_GetProperty(cx, JS_GetGlobalObject(cx),
291 JS_GetStringBytes(str), &prop);
292
293 JSFunction *func = JS_ValueToFunction(cx, prop);
294 if (func) {
295 const char* fname = JS_GetFunctionName(func);
296 item.names.append(new Str(fname));
297 }
298 }
299 JS_DestroyIdArray(cx, arr);
300 }
301 }
302 else { //error occured
303
304 }
305
306 #ifdef ENABLE_JS_THREADS
307 JS_SuspendRequest(cx);
308 #endif
309
310 return (Bool)status;
311 }
312
313 /************** XSLTContext *******************/
314
315 struct XSLTContextPrivate {
316 Context *ctx;
317 Situation *situa;
318 };
319
ctxFinalize(JSContext * cx,JSObject * obj)320 void ctxFinalize(JSContext *cx, JSObject *obj)
321 {
322 XSLTContextPrivate *priv = (XSLTContextPrivate*)JS_GetPrivate(cx, obj);
323 if (priv) delete priv;
324 }
325
JS_PROP(ctxGetPosition)326 JS_PROP(ctxGetPosition)
327 {
328 XSLTContextPrivate *priv = (XSLTContextPrivate*)JS_GetPrivate(cx, obj);
329 if (priv) {
330 *rval = INT_TO_JSVAL(priv -> ctx -> getPosition() + 1);
331 return TRUE;
332 }
333 else {
334 return FALSE;
335 };
336 }
337
JS_PROP(ctxGetSize)338 JS_PROP(ctxGetSize)
339 {
340 XSLTContextPrivate *priv = (XSLTContextPrivate*)JS_GetPrivate(cx, obj);
341 if (priv) {
342 *rval = INT_TO_JSVAL(priv -> ctx -> getSize());
343 return TRUE;
344 }
345 else {
346 return FALSE;
347 };
348 }
349
JS_PROP(ctxGetContextNode)350 JS_PROP(ctxGetContextNode)
351 {
352 XSLTContextPrivate *priv = (XSLTContextPrivate*)JS_GetPrivate(cx, obj);
353 if (priv) {
354 JSObject *obj = jsdom_wrapNode(*(priv->situa), cx, priv->ctx->current());
355 *rval = OBJECT_TO_JSVAL(obj);
356 return TRUE;
357 } else {
358 return FALSE;
359 }
360 }
361
JS_PROP(ctxGetCurrentNode)362 JS_PROP(ctxGetCurrentNode)
363 {
364 XSLTContextPrivate *priv = (XSLTContextPrivate*)JS_GetPrivate(cx, obj);
365 if (priv) {
366 JSObject *obj = jsdom_wrapNode(*(priv->situa), cx,
367 priv->ctx->getCurrentNode());
368 *rval = OBJECT_TO_JSVAL(obj);
369 return TRUE;
370 } else {
371 return FALSE;
372 }
373 }
374
JS_PROP(ctxGetOwnerDocument)375 JS_PROP(ctxGetOwnerDocument)
376 {
377 XSLTContextPrivate *priv = (XSLTContextPrivate*)JS_GetPrivate(cx, obj);
378 if (priv) {
379 Tree &tree = toV(priv->ctx->current())->getOwner();
380 JSObject *obj = jsdom_wrapNode(*(priv -> situa), cx,
381 &(tree.getRoot()));
382 *rval = OBJECT_TO_JSVAL(obj);
383 return TRUE;
384 } else {
385 return FALSE;
386 }
387 }
388
JS_METHOD(ctxSystemProperty)389 JS_METHOD(ctxSystemProperty)
390 {
391 JSString *str = JS_NewStringCopyN(cx, "not supported", 13);
392 *rval = STRING_TO_JSVAL(str);
393 return TRUE;
394 }
395
JS_METHOD(ctxStringValue)396 JS_METHOD(ctxStringValue)
397 {
398 // if (argc > 0 && JSVAL_IS_OBJECT(argv[0])
399 // && sjs_instanceOf(cx, JSVAL_TO_OBJECT(argv[0]), &nodeClass))
400 // {
401 // JSObject *node = JSVAL_TO_OBJECT(argv[0]);
402 // NodePrivate *np = (NodePrivate*)JS_GetPrivate(cx, obj);
403 // sabassert(np);
404 // DStr val;
405 // np->situa->dom().constructStringValue(np->node, val);
406 // JSString *str = JS_NewStringCopyZ(cx, (char*)val);
407 // *rval = STRING_TO_JSVAL(str);
408 // return TRUE;
409 // } else {
410 // return FALSE;
411 // }
412 DOM_EX( 9 );
413 return TRUE;
414 }
415
416 JSClass ctxClass = {
417 "XSLTContext",
418 JSCLASS_HAS_PRIVATE,
419 JS_PropertyStub, JS_PropertyStub,
420 JS_PropertyStub, JS_PropertyStub,
421 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
422 ctxFinalize, 0, 0, NULL, NULL, NULL, NULL, 0, 0
423 };
424
425 JSPropertySpec ctxProps[] =
426 {
427 {"contextPosition", 0, PROP_OPT, ctxGetPosition, NULL},
428 {"contextSize", 0, PROP_OPT, ctxGetSize, NULL},
429 {"contextNode", 0, PROP_OPT, ctxGetContextNode, NULL},
430 {"currentNode", 0, PROP_OPT, ctxGetCurrentNode, NULL},
431 {"ownerDocument", 0, PROP_OPT, ctxGetOwnerDocument, NULL},
432 {NULL, 0, 0, 0, 0}
433 };
434
435 JSFunctionSpec ctxFunctions[] =
436 {
437 {"systemProperty", ctxSystemProperty, 0, 0, 0},
438 {"stringValue", ctxStringValue, 0, 0, 0},
439 {NULL, 0, 0, 0, 0}
440 };
441
sjs_PublishContext(Sit S,JSContext_Sab * cx,Context * c)442 void sjs_PublishContext(Sit S, JSContext_Sab *cx, Context *c)
443 {
444 JSObject *obj = JS_DefineObject(cx, JS_GetGlobalObject(cx), "XSLTContext",
445 &ctxClass, NULL,
446 JSPROP_ENUMERATE |
447 JSPROP_READONLY);
448
449 XSLTContextPrivate *priv = new XSLTContextPrivate;
450 priv -> ctx = c;
451 priv -> situa = &S;
452 JS_SetPrivate(cx, obj, priv);
453 JS_DefineProperties(cx, obj, ctxProps);
454 JS_DefineFunctions(cx, obj, ctxFunctions);
455 }
456
457 /************* function call ******************/
SJSCallFunction(Sit S,Context * c,JSContextItem & item,Str & name,ExprList & atoms,Expression & retxpr)458 Bool SJSCallFunction(Sit S, Context *c, JSContextItem &item, Str &name,
459 ExprList &atoms, Expression &retxpr)
460 {
461 JSContext *cx = item.cx;
462 sabassert(cx);
463
464 #ifdef ENABLE_JS_THREADS
465 JS_ResumeRequest(cx);
466 #endif
467
468 sjs_PublishContext(S, cx, c);
469
470 //get args
471 int argc = atoms.number();
472 jsval *args = new jsval[argc];
473 for (int i = 0; i < argc; i++) {
474 switch (atoms[i] -> type) {
475 case EX_NUMBER:
476 {
477 jsdouble d = (double)(atoms[i]->tonumber(S));
478 JS_NewDoubleValue(cx, d, &args[i]);
479 }; break;
480 case EX_BOOLEAN:
481 {
482 args[i] = BOOLEAN_TO_JSVAL(atoms[i]->tobool());
483 }; break;
484 case EX_STRING:
485 {
486 Str str;
487 atoms[i]->tostring(S, str);
488 char *p = (char*)str;
489 JSString *s = JS_NewStringCopyN(cx, p, strlen(p));
490 args[i] = STRING_TO_JSVAL(s);
491 }; break;
492 case EX_NODESET:
493 {
494 const Context &ctx = atoms[i]->tonodesetRef();
495 int num = ctx.getSize();
496 JSObject *arr = jsdom_createNodeList(cx, num);
497 args[i] = OBJECT_TO_JSVAL(arr);
498 //iterate
499 for (int j = 0; j < num; j++) {
500 NodeHandle node = ctx[j];
501 jsval val;// = new jsval;
502 val = OBJECT_TO_JSVAL(jsdom_wrapNode(S, cx, node));
503 JS_SetElement(cx, arr, j, &val);
504 }
505 }; break;
506 case EX_EXTERNAL:
507 {
508 External e;
509 e.assign(atoms[i]->toexternal(S));
510 JSObject *o = (JSObject*)e.getValue();
511 if ( o )
512 {
513 args[i] = OBJECT_TO_JSVAL(o);
514 }
515 else
516 {
517 args[i] = JSVAL_NULL;
518 }
519 }; break;
520 default:
521 {
522 //convert to string
523 Str str;
524 atoms[i]->tostring(S, str);
525 char *p = (char*)str;
526 JSString *s = JS_NewStringCopyN(cx, p, strlen(p));
527 args[i] = STRING_TO_JSVAL(s);
528 }
529 }
530 }
531
532 //call function
533 jsval rval;
534 JSBool status = JS_CallFunctionName(cx, JS_GetGlobalObject(cx),
535 (char*)name, argc, args, &rval);
536 if ( JS_IsExceptionPending(cx) )
537 {
538 JS_ClearPendingException(cx);
539 }
540
541 //remove XSLT context
542 JS_DeleteProperty(cx, JS_GetGlobalObject(cx), "XSLTContext");
543
544 delete[] args;
545
546 if (status) {
547 if (JSVAL_IS_VOID(rval))
548 {
549 //return an emtpy nodeset
550 GP( Context ) newc = new Context(c->getCurrentNode());
551 retxpr.setAtom(newc.keep());
552 }
553 if (JSVAL_IS_NULL(rval))
554 {
555 External e(cx, NULL);
556 retxpr.setAtom(e);
557 }
558 if (JSVAL_IS_OBJECT(rval))
559 {
560 JSObject *obj = JSVAL_TO_OBJECT(rval);
561 //node lists
562 if (sjs_instanceOf(cx, obj, &nlistClass))
563 {
564 jsuint len;
565 JS_GetArrayLength(cx, obj, &len);
566 GP( Context ) newc = new Context(c->getCurrentNode());
567 for (unsigned int i = 0; i < len; i++)
568 {
569 jsval jnode;
570 JS_GetElement(cx, obj, i, &jnode);
571 JSObject *onode = JSVAL_TO_OBJECT(jnode);
572 if (onode && sjs_instanceOf(cx, onode, &nodeClass))
573 {
574 NodePrivate *priv = (NodePrivate*)JS_GetPrivate(cx, onode);
575 sabassert(priv);
576 (*newc).append(priv -> node);
577 }
578 }
579 retxpr.setAtom(newc.keep());
580 }
581 //single nodes
582 else if (sjs_instanceOf(cx, obj, &nodeClass))
583 {
584 GP( Context ) newc = new Context(c->getCurrentNode());
585
586 NodePrivate *priv = (NodePrivate*)JS_GetPrivate(cx, obj);
587 sabassert(priv);
588 (*newc).append(priv -> node);
589
590 retxpr.setAtom(newc.keep());
591 }
592 //other objects
593 else
594 {
595 External e(cx, obj);
596 retxpr.setAtom(e);
597 }
598 }
599 //strings
600 else if (JSVAL_IS_STRING(rval))
601 {
602 JSString *str = JS_ValueToString(cx, rval);
603 Str rstr = (char*) JS_GetStringBytes(str);
604 retxpr.setAtom(rstr);
605 }
606 else if (JSVAL_IS_NUMBER(rval))
607 {
608 jsdouble d;
609 JS_ValueToNumber(cx, rval, &d);
610 Number num((double)d);
611 retxpr.setAtom(num);
612 }
613 else if (JSVAL_IS_BOOLEAN(rval))
614 {
615 retxpr.setAtom(JSVAL_TO_BOOLEAN(rval));
616 }
617 else
618 {
619 //all other types are treated as strings (shouldn't happen)
620 JSString *str = JS_ValueToString(cx, rval);
621 Str rstr = (char*) JS_GetStringBytes(str);
622 retxpr.setAtom(rstr);
623 }
624 }
625
626 #ifdef ENABLE_JS_THREADS
627 JS_SuspendRequest(cx);
628 #endif
629
630 //run GC
631 JS_MaybeGC(cx);
632 //JS_GC(cx);
633 return status;
634 }
635
636 #endif //ENABLE_JS
637