jbarnette-johnson 1.0.0.200806240111

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. data/CHANGELOG +5 -0
  2. data/MANIFEST +385 -0
  3. data/MINGW32.mk +124 -0
  4. data/README.rdoc +51 -0
  5. data/Rakefile +166 -0
  6. data/bin/johnson +107 -0
  7. data/cross-compile.txt +38 -0
  8. data/ext/spidermonkey/context.c +122 -0
  9. data/ext/spidermonkey/context.h +19 -0
  10. data/ext/spidermonkey/conversions.c +286 -0
  11. data/ext/spidermonkey/conversions.h +18 -0
  12. data/ext/spidermonkey/debugger.c +208 -0
  13. data/ext/spidermonkey/debugger.h +9 -0
  14. data/ext/spidermonkey/extconf.rb +25 -0
  15. data/ext/spidermonkey/extensions.c +37 -0
  16. data/ext/spidermonkey/extensions.h +12 -0
  17. data/ext/spidermonkey/global.c +40 -0
  18. data/ext/spidermonkey/global.h +11 -0
  19. data/ext/spidermonkey/idhash.c +16 -0
  20. data/ext/spidermonkey/idhash.h +8 -0
  21. data/ext/spidermonkey/immutable_node.c.erb +522 -0
  22. data/ext/spidermonkey/immutable_node.h +22 -0
  23. data/ext/spidermonkey/jroot.h +187 -0
  24. data/ext/spidermonkey/js_land_proxy.c +609 -0
  25. data/ext/spidermonkey/js_land_proxy.h +20 -0
  26. data/ext/spidermonkey/ruby_land_proxy.c +537 -0
  27. data/ext/spidermonkey/ruby_land_proxy.h +17 -0
  28. data/ext/spidermonkey/runtime.c +304 -0
  29. data/ext/spidermonkey/runtime.h +25 -0
  30. data/ext/spidermonkey/spidermonkey.c +20 -0
  31. data/ext/spidermonkey/spidermonkey.h +29 -0
  32. data/js/johnson/browser.js +9 -0
  33. data/js/johnson/browser/env.js +687 -0
  34. data/js/johnson/browser/jquery.js +3444 -0
  35. data/js/johnson/browser/xmlsax.js +1564 -0
  36. data/js/johnson/browser/xmlw3cdom.js +4189 -0
  37. data/js/johnson/cli.js +30 -0
  38. data/js/johnson/prelude.js +80 -0
  39. data/js/johnson/template.js +29 -0
  40. data/lib/hoe.rb +748 -0
  41. data/lib/johnson.rb +46 -0
  42. data/lib/johnson/cli.rb +7 -0
  43. data/lib/johnson/cli/options.rb +56 -0
  44. data/lib/johnson/error.rb +4 -0
  45. data/lib/johnson/nodes.rb +7 -0
  46. data/lib/johnson/nodes/binary_node.rb +64 -0
  47. data/lib/johnson/nodes/for.rb +14 -0
  48. data/lib/johnson/nodes/for_in.rb +12 -0
  49. data/lib/johnson/nodes/function.rb +13 -0
  50. data/lib/johnson/nodes/list.rb +27 -0
  51. data/lib/johnson/nodes/node.rb +68 -0
  52. data/lib/johnson/nodes/ternary_node.rb +20 -0
  53. data/lib/johnson/parser.rb +21 -0
  54. data/lib/johnson/parser/syntax_error.rb +13 -0
  55. data/lib/johnson/runtime.rb +55 -0
  56. data/lib/johnson/spidermonkey/context.rb +10 -0
  57. data/lib/johnson/spidermonkey/debugger.rb +67 -0
  58. data/lib/johnson/spidermonkey/immutable_node.rb +280 -0
  59. data/lib/johnson/spidermonkey/js_land_proxy.rb +62 -0
  60. data/lib/johnson/spidermonkey/mutable_tree_visitor.rb +233 -0
  61. data/lib/johnson/spidermonkey/ruby_land_proxy.rb +52 -0
  62. data/lib/johnson/spidermonkey/runtime.rb +94 -0
  63. data/lib/johnson/version.rb +4 -0
  64. data/lib/johnson/visitable.rb +16 -0
  65. data/lib/johnson/visitors.rb +4 -0
  66. data/lib/johnson/visitors/dot_visitor.rb +167 -0
  67. data/lib/johnson/visitors/ecma_visitor.rb +315 -0
  68. data/lib/johnson/visitors/enumerating_visitor.rb +115 -0
  69. data/lib/johnson/visitors/sexp_visitor.rb +172 -0
  70. data/lib/rails/init.rb +37 -0
  71. data/test/assets/index.html +38 -0
  72. data/test/assets/jquery_test.html +186 -0
  73. data/test/helper.rb +58 -0
  74. data/test/johnson/browser_test.rb +38 -0
  75. data/test/johnson/conversions/array_test.rb +32 -0
  76. data/test/johnson/conversions/boolean_test.rb +17 -0
  77. data/test/johnson/conversions/callable_test.rb +34 -0
  78. data/test/johnson/conversions/file_test.rb +15 -0
  79. data/test/johnson/conversions/nil_test.rb +20 -0
  80. data/test/johnson/conversions/number_test.rb +34 -0
  81. data/test/johnson/conversions/regexp_test.rb +24 -0
  82. data/test/johnson/conversions/string_test.rb +26 -0
  83. data/test/johnson/conversions/struct_test.rb +15 -0
  84. data/test/johnson/conversions/symbol_test.rb +19 -0
  85. data/test/johnson/conversions/thread_test.rb +24 -0
  86. data/test/johnson/error_test.rb +9 -0
  87. data/test/johnson/extensions_test.rb +56 -0
  88. data/test/johnson/nodes/array_literal_test.rb +57 -0
  89. data/test/johnson/nodes/array_node_test.rb +26 -0
  90. data/test/johnson/nodes/binary_node_test.rb +61 -0
  91. data/test/johnson/nodes/bracket_access_test.rb +16 -0
  92. data/test/johnson/nodes/delete_test.rb +11 -0
  93. data/test/johnson/nodes/do_while_test.rb +12 -0
  94. data/test/johnson/nodes/dot_accessor_test.rb +15 -0
  95. data/test/johnson/nodes/export_test.rb +9 -0
  96. data/test/johnson/nodes/for_test.rb +54 -0
  97. data/test/johnson/nodes/function_test.rb +71 -0
  98. data/test/johnson/nodes/if_test.rb +41 -0
  99. data/test/johnson/nodes/import_test.rb +13 -0
  100. data/test/johnson/nodes/label_test.rb +19 -0
  101. data/test/johnson/nodes/object_literal_test.rb +110 -0
  102. data/test/johnson/nodes/return_test.rb +16 -0
  103. data/test/johnson/nodes/semi_test.rb +8 -0
  104. data/test/johnson/nodes/switch_test.rb +55 -0
  105. data/test/johnson/nodes/ternary_test.rb +25 -0
  106. data/test/johnson/nodes/throw_test.rb +9 -0
  107. data/test/johnson/nodes/try_node_test.rb +59 -0
  108. data/test/johnson/nodes/typeof_test.rb +11 -0
  109. data/test/johnson/nodes/unary_node_test.rb +23 -0
  110. data/test/johnson/nodes/void_test.rb +11 -0
  111. data/test/johnson/nodes/while_test.rb +26 -0
  112. data/test/johnson/nodes/with_test.rb +10 -0
  113. data/test/johnson/prelude_test.rb +56 -0
  114. data/test/johnson/runtime_test.rb +46 -0
  115. data/test/johnson/spidermonkey/context_test.rb +21 -0
  116. data/test/johnson/spidermonkey/immutable_node_test.rb +34 -0
  117. data/test/johnson/spidermonkey/js_land_proxy_test.rb +236 -0
  118. data/test/johnson/spidermonkey/ruby_land_proxy_test.rb +225 -0
  119. data/test/johnson/spidermonkey/runtime_test.rb +17 -0
  120. data/test/johnson/version_test.rb +13 -0
  121. data/test/johnson/visitors/dot_visitor_test.rb +39 -0
  122. data/test/johnson/visitors/enumerating_visitor_test.rb +12 -0
  123. data/test/johnson_test.rb +16 -0
  124. data/test/jquery_units/test.js +27 -0
  125. data/test/jquery_units/test_helper.js +197 -0
  126. data/test/jquery_units/units/ajax.js +795 -0
  127. data/test/jquery_units/units/core.js +1563 -0
  128. data/test/jquery_units/units/event.js +299 -0
  129. data/test/jquery_units/units/fx.js +427 -0
  130. data/test/jquery_units/units/offset.js +112 -0
  131. data/test/jquery_units/units/selector.js +224 -0
  132. data/test/jspec/helper.js +7 -0
  133. data/test/jspec/jspec.js +192 -0
  134. data/test/jspec/simple_spec.js +68 -0
  135. data/test/parser_test.rb +276 -0
  136. data/todo/.keep +0 -0
  137. metadata +501 -0
@@ -0,0 +1,17 @@
1
+ #ifndef JOHNSON_SPIDERMONKEY_RUBY_LAND_PROXY_H
2
+ #define JOHNSON_SPIDERMONKEY_RUBY_LAND_PROXY_H
3
+
4
+ #include "spidermonkey.h"
5
+ #include "runtime.h"
6
+
7
+ typedef struct {
8
+ void* key;
9
+ JohnsonRuntime* runtime;
10
+ } RubyLandProxy;
11
+
12
+ bool ruby_value_is_proxy(VALUE maybe_proxy);
13
+ JSBool unwrap_ruby_land_proxy(JohnsonRuntime* runtime, VALUE proxy, jsval* retval);
14
+ VALUE make_ruby_land_proxy(JohnsonRuntime* runtime, jsval value, const char const* root_name);
15
+ void init_Johnson_SpiderMonkey_Proxy(VALUE spidermonkey);
16
+
17
+ #endif
@@ -0,0 +1,304 @@
1
+ #include "runtime.h"
2
+ #include "global.h"
3
+ #include "idhash.h"
4
+ #include "conversions.h"
5
+ #include "jsdbgapi.h"
6
+ #include "jroot.h"
7
+ #include "ruby_land_proxy.h"
8
+
9
+ /*
10
+ * call-seq:
11
+ * global
12
+ *
13
+ * Returns the global object used for this context.
14
+ */
15
+ static VALUE global(VALUE self)
16
+ {
17
+ JohnsonRuntime* runtime;
18
+ Data_Get_Struct(self, JohnsonRuntime, runtime);
19
+ return convert_to_ruby(runtime, OBJECT_TO_JSVAL(runtime->global));
20
+ }
21
+
22
+ static JSTrapStatus trap_handler( JSContext *UNUSED(context),
23
+ JSScript *UNUSED(script),
24
+ jsbytecode *UNUSED(pc),
25
+ jsval *UNUSED(rval),
26
+ void *block_closure )
27
+ {
28
+ VALUE block = (VALUE)block_closure;
29
+ rb_funcall(block, rb_intern("call"), 0);
30
+ return JSTRAP_CONTINUE;
31
+ }
32
+
33
+ /*
34
+ * call-seq:
35
+ * set_trap(script, parsecode, block)
36
+ *
37
+ * Set the trap at +script+ and +parsecode+ to +block+
38
+ */
39
+ static VALUE set_trap(VALUE self, VALUE script, VALUE linenum, VALUE block)
40
+ {
41
+ JohnsonRuntime* runtime;
42
+ Data_Get_Struct(self, JohnsonRuntime, runtime);
43
+
44
+ JSContext * context = johnson_get_current_context(runtime);
45
+ jsval compiled_js;
46
+ if(!convert_to_js(runtime, script, &compiled_js))
47
+ rb_raise(rb_eRuntimeError, "Couldn't get compiled script.");
48
+ JSScript * js_script = (JSScript *)JS_GetPrivate(context, JSVAL_TO_OBJECT(compiled_js));
49
+
50
+ jsbytecode * pc = JS_LineNumberToPC(context, js_script, (uintN)NUM2INT(linenum));
51
+ return JS_SetTrap(context, js_script, pc, trap_handler, (void*)block) ? Qtrue : Qfalse;
52
+ }
53
+
54
+ /*
55
+ * call-seq:
56
+ * native_compile(script, filename, linenum)
57
+ *
58
+ * Compile +script+ with +filename+ using +linenum+
59
+ */
60
+ static VALUE native_compile(VALUE self, VALUE script, VALUE filename, VALUE linenum)
61
+ {
62
+ JohnsonRuntime* runtime;
63
+ Data_Get_Struct(self, JohnsonRuntime, runtime);
64
+
65
+ JSContext * context = johnson_get_current_context(runtime);
66
+ JohnsonContext * johnson_context = OUR_CONTEXT(context);
67
+
68
+ JSScript * compiled_js = JS_CompileScript(
69
+ context,
70
+ runtime->global,
71
+ StringValuePtr(script),
72
+ (size_t)StringValueLen(script),
73
+ StringValueCStr(filename),
74
+ (unsigned)NUM2INT(linenum)
75
+ );
76
+ if(compiled_js == NULL) {
77
+ if (JS_IsExceptionPending(context))
78
+ {
79
+ // If there's an exception pending here, it's a syntax error.
80
+ JS_GetPendingException(context, &johnson_context->ex);
81
+ JS_ClearPendingException(context);
82
+ }
83
+
84
+ if (johnson_context->ex) {
85
+ RAISE_JS_ERROR(self, johnson_context->ex);
86
+ return Qnil;
87
+ }
88
+ }
89
+
90
+ JSObject * script_object = JS_NewScriptObject(context, compiled_js);
91
+
92
+ PREPARE_RUBY_JROOTS(context, 1);
93
+ JROOT(script_object);
94
+ JRETURN_RUBY(make_ruby_land_proxy(runtime, OBJECT_TO_JSVAL(script_object), "JSScriptProxy"));
95
+ }
96
+
97
+ /*
98
+ * call-seq:
99
+ * evaluate_compiled_script(script)
100
+ *
101
+ * Evaluate +script+
102
+ */
103
+ static VALUE evaluate_compiled_script(VALUE self, VALUE compiled_script)
104
+ {
105
+ JohnsonRuntime* runtime;
106
+ Data_Get_Struct(self, JohnsonRuntime, runtime);
107
+
108
+ JSContext * context = johnson_get_current_context(runtime);
109
+ JohnsonContext * johnson_context = OUR_CONTEXT(context);
110
+
111
+ // clean things up first
112
+ johnson_context->ex = 0;
113
+ memset(johnson_context->msg, 0, MAX_EXCEPTION_MESSAGE_SIZE);
114
+
115
+ jsval compiled_js;
116
+ if(!convert_to_js(runtime, compiled_script, &compiled_js))
117
+ rb_raise(rb_eRuntimeError, "Script compilation failed");
118
+
119
+ JSScript * js_script = (JSScript *)JS_GetPrivate(context, JSVAL_TO_OBJECT(compiled_js));
120
+
121
+ jsval js;
122
+ JSBool ok = JS_ExecuteScript(context, runtime->global, js_script, &js);
123
+
124
+ if (!ok)
125
+ {
126
+ if (JS_IsExceptionPending(context))
127
+ {
128
+ // If there's an exception pending here, it's a syntax error.
129
+ JS_GetPendingException(context, &johnson_context->ex);
130
+ JS_ClearPendingException(context);
131
+ }
132
+
133
+ if (johnson_context->ex) {
134
+ RAISE_JS_ERROR(self, johnson_context->ex);
135
+ return Qnil;
136
+ }
137
+ }
138
+
139
+ return convert_to_ruby(runtime, js);
140
+ }
141
+
142
+ /*
143
+ * call-seq:
144
+ * gc_zeal=(level)
145
+ *
146
+ * Sets the GC zeal.
147
+ * 0 = normal, 1 = Very Frequent, 2 = Extremely Frequent
148
+ */
149
+ static VALUE
150
+ set_gc_zeal(VALUE self, VALUE zeal)
151
+ {
152
+ JohnsonRuntime* runtime;
153
+ Data_Get_Struct(self, JohnsonRuntime, runtime);
154
+
155
+ JSContext* context = johnson_get_current_context(runtime);
156
+
157
+ JS_SetGCZeal(context, NUM2INT(zeal));
158
+
159
+ return zeal;
160
+ }
161
+
162
+ /*
163
+ * call-seq:
164
+ * debugger=(debugger)
165
+ *
166
+ * Sets a debugger object
167
+ */
168
+ static VALUE
169
+ set_debugger(VALUE self, VALUE debugger)
170
+ {
171
+ JohnsonRuntime* runtime;
172
+ JSDebugHooks* debug_hooks;
173
+
174
+ rb_iv_set(self, "@debugger", debugger);
175
+ Data_Get_Struct(self, JohnsonRuntime, runtime);
176
+ Data_Get_Struct(debugger, JSDebugHooks, debug_hooks);
177
+
178
+ JSContext * context = johnson_get_current_context(runtime);
179
+
180
+ JS_SetInterrupt( runtime->js,
181
+ debug_hooks->interruptHandler,
182
+ debug_hooks->interruptHandlerData);
183
+ JS_SetNewScriptHook( runtime->js,
184
+ debug_hooks->newScriptHook,
185
+ debug_hooks->newScriptHookData);
186
+ JS_SetDestroyScriptHook( runtime->js,
187
+ debug_hooks->destroyScriptHook,
188
+ debug_hooks->destroyScriptHookData);
189
+ JS_SetDebuggerHandler( runtime->js,
190
+ debug_hooks->debuggerHandler,
191
+ debug_hooks->debuggerHandlerData);
192
+ JS_SetSourceHandler( runtime->js,
193
+ debug_hooks->sourceHandler,
194
+ debug_hooks->sourceHandlerData);
195
+ JS_SetExecuteHook( runtime->js,
196
+ debug_hooks->executeHook,
197
+ debug_hooks->executeHookData);
198
+ JS_SetCallHook( runtime->js,
199
+ debug_hooks->callHook,
200
+ debug_hooks->callHookData);
201
+ JS_SetObjectHook( runtime->js,
202
+ debug_hooks->objectHook,
203
+ debug_hooks->objectHookData);
204
+ JS_SetThrowHook( runtime->js,
205
+ debug_hooks->throwHook,
206
+ debug_hooks->throwHookData);
207
+ JS_SetDebugErrorHook( runtime->js,
208
+ debug_hooks->debugErrorHook,
209
+ debug_hooks->debugErrorHookData);
210
+
211
+ JS_SetContextDebugHooks(context, debug_hooks);
212
+
213
+ return debugger;
214
+ }
215
+
216
+ JSBool gc_callback(JSContext *context, JSGCStatus status)
217
+ {
218
+ if(status == JSGC_BEGIN) {
219
+ VALUE ruby_runtime = (VALUE)JS_GetRuntimePrivate(JS_GetRuntime(context));
220
+ if(rb_funcall(ruby_runtime, rb_intern("should_sm_gc?"), 0) == Qtrue)
221
+ return JS_TRUE;
222
+ }
223
+ return JS_FALSE;
224
+ }
225
+
226
+ static VALUE
227
+ initialize_native(VALUE self, VALUE UNUSED(options))
228
+ {
229
+ JohnsonRuntime* runtime;
230
+ Data_Get_Struct(self, JohnsonRuntime, runtime);
231
+
232
+ if ((runtime->js = JS_NewRuntime(0x100000))
233
+ && (runtime->jsids = create_id_hash())
234
+ && (runtime->rbids = create_id_hash())
235
+ )
236
+ {
237
+ JS_SetRuntimePrivate(runtime->js, (void *)self);
238
+ JS_SetGCCallbackRT(runtime->js, gc_callback);
239
+
240
+ JSContext* context = johnson_get_current_context(runtime);
241
+ if(
242
+ (runtime->global = JS_GetGlobalObject(context))
243
+ && (JS_AddNamedRoot(context, &(runtime->global), "runtime->global"))
244
+ ) {
245
+ return self;
246
+ }
247
+ }
248
+
249
+
250
+ if (runtime->rbids)
251
+ JS_HashTableDestroy(runtime->rbids);
252
+
253
+ if (runtime->jsids)
254
+ JS_HashTableDestroy(runtime->jsids);
255
+
256
+ if (runtime->js)
257
+ JS_DestroyRuntime(runtime->js);
258
+
259
+ rb_raise(rb_eRuntimeError, "Couldn't initialize the runtime!");
260
+ return Qnil;
261
+ }
262
+
263
+ JSContext* johnson_get_current_context(JohnsonRuntime * runtime)
264
+ {
265
+ JohnsonContext * context = NULL;
266
+ VALUE self = (VALUE)JS_GetRuntimePrivate(runtime->js);
267
+ Data_Get_Struct(rb_funcall(self, rb_intern("current_context"), 0), JohnsonContext, context);
268
+ return context->js;
269
+ }
270
+
271
+ static void deallocate(JohnsonRuntime* runtime)
272
+ {
273
+ JS_RemoveRoot(johnson_get_current_context(runtime), &(runtime->global));
274
+
275
+ JSContext *context;
276
+ JSContext *iterator = NULL;
277
+
278
+ while ((context = JS_ContextIterator(runtime->js, &iterator)) != NULL)
279
+ JS_DestroyContext(context);
280
+
281
+ JS_DestroyRuntime(runtime->js);
282
+ free(runtime);
283
+ }
284
+
285
+ static VALUE allocate(VALUE klass)
286
+ {
287
+ JohnsonRuntime* runtime = calloc(1, sizeof(JohnsonRuntime));
288
+ return Data_Wrap_Struct(klass, 0, deallocate, runtime);
289
+ }
290
+
291
+ void init_Johnson_SpiderMonkey_Runtime(VALUE spidermonkey)
292
+ {
293
+ VALUE klass = rb_define_class_under(spidermonkey, "Runtime", rb_cObject);
294
+
295
+ rb_define_alloc_func(klass, allocate);
296
+ rb_define_private_method(klass, "initialize_native", initialize_native, 1);
297
+
298
+ rb_define_method(klass, "global", global, 0);
299
+ rb_define_method(klass, "debugger=", set_debugger, 1);
300
+ rb_define_method(klass, "gc_zeal=", set_gc_zeal, 1);
301
+ rb_define_method(klass, "evaluate_compiled_script", evaluate_compiled_script, 1);
302
+ rb_define_private_method(klass, "native_compile", native_compile, 3);
303
+ rb_define_private_method(klass, "set_trap", set_trap, 3);
304
+ }
@@ -0,0 +1,25 @@
1
+ #ifndef JOHNSON_SPIDERMONKEY_RUNTIME_H
2
+ #define JOHNSON_SPIDERMONKEY_RUNTIME_H
3
+
4
+ #include "spidermonkey.h"
5
+
6
+ #define RAISE_JS_ERROR(rb_runtime, ex) \
7
+ do {\
8
+ JohnsonRuntime * _rt = NULL;\
9
+ Data_Get_Struct(rb_runtime, JohnsonRuntime, _rt);\
10
+ rb_funcall(CLASS_OF(rb_runtime), rb_intern("raise_js_exception"), 1,\
11
+ convert_to_ruby(_rt, ex)); \
12
+ } while(0)
13
+
14
+ typedef struct {
15
+ JSObject* global;
16
+ JSRuntime* js;
17
+
18
+ JSHashTable *jsids; // jsid -> rbid
19
+ JSHashTable *rbids; // rbid -> jsid
20
+ } JohnsonRuntime;
21
+
22
+ JSContext* johnson_get_current_context(JohnsonRuntime* runtime);
23
+ void init_Johnson_SpiderMonkey_Runtime(VALUE spidermonkey);
24
+
25
+ #endif
@@ -0,0 +1,20 @@
1
+ #include "spidermonkey.h"
2
+ #include "context.h"
3
+ #include "ruby_land_proxy.h"
4
+ #include "debugger.h"
5
+ #include "immutable_node.h"
6
+
7
+ void Init_spidermonkey()
8
+ {
9
+ VALUE johnson = rb_define_module("Johnson"); // FIXME: this belongs outside the extension
10
+ VALUE spidermonkey = rb_define_module_under(johnson, "SpiderMonkey");
11
+
12
+ init_Johnson_SpiderMonkey_Context(spidermonkey);
13
+ init_Johnson_SpiderMonkey_Proxy(spidermonkey);
14
+ init_Johnson_SpiderMonkey_Debugger(spidermonkey);
15
+ init_Johnson_SpiderMonkey_Immutable_Node(spidermonkey);
16
+ init_Johnson_SpiderMonkey_Runtime(spidermonkey);
17
+
18
+ rb_define_const(spidermonkey, "VERSION",
19
+ rb_obj_freeze(rb_str_new2(JS_GetImplementationVersion())));
20
+ }
@@ -0,0 +1,29 @@
1
+ #ifndef JOHNSON_SPIDERMONKEY_H
2
+ #define JOHNSON_SPIDERMONKEY_H
3
+
4
+ #include <assert.h>
5
+ #include <stdarg.h>
6
+ #include <stdbool.h>
7
+ #include <ruby.h>
8
+
9
+ #include "jsapi.h"
10
+ #include "jshash.h"
11
+ #include "jsregexp.h"
12
+
13
+ #include "jroot.h"
14
+
15
+ #ifndef StringValueLen
16
+ #define StringValueLen(v) (RSTRING(v)->len)
17
+ #endif
18
+
19
+ #ifndef UNUSED
20
+ # if defined(__GNUC__)
21
+ # define MAYBE_UNUSED(name) name __attribute__((unused))
22
+ # define UNUSED(name) MAYBE_UNUSED(UNUSED_ ## name)
23
+ # else
24
+ # define MAYBE_UNUSED(name) name
25
+ # define UNUSED(name) name
26
+ # endif
27
+ #endif
28
+
29
+ #endif
@@ -0,0 +1,9 @@
1
+ Ruby.require('net/http');
2
+ Johnson.require("johnson/browser/xmlw3cdom");
3
+ Johnson.require("johnson/browser/xmlsax");
4
+ Johnson.require("johnson/browser/env");
5
+
6
+ DOMElement.prototype.toString = function() {
7
+ return "<" + this.tagName + (this.className !== "" ? " class='" + this.className + "'" : "") +
8
+ (this.id !== "" ? " id='" + this.id + "'" : "") + ">";
9
+ };
@@ -0,0 +1,687 @@
1
+ /*
2
+ * Simulated browser environment for Rhino
3
+ * By John Resig <http://ejohn.org/>
4
+ * Copyright 2007 John Resig, under the MIT License
5
+ */
6
+
7
+ // The window Object
8
+ var window = this;
9
+
10
+ Ruby.require("uri");
11
+
12
+ print = function(txt) { Ruby.puts(txt); };
13
+
14
+ (function(){
15
+
16
+ // Browser Navigator
17
+
18
+ window.navigator = {
19
+ get userAgent(){
20
+ return "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3";
21
+ }
22
+ };
23
+
24
+ var fileToUrl = function(file) {
25
+ return Ruby.URI.parse("file://" + Ruby.File.expand_path(file));
26
+ };
27
+
28
+ var curLocation = fileToUrl(".");
29
+
30
+ window.__defineSetter__("location", function(url){
31
+ var xhr = new XMLHttpRequest();
32
+ xhr.open("GET", url);
33
+ xhr.onreadystatechange = function(){
34
+ curLocation = curLocation.merge(url);
35
+ window.document = xhr.responseXML;
36
+
37
+ if(window.document) {
38
+ var event = document.createEvent();
39
+ event.initEvent("load");
40
+ window.dispatchEvent( event );
41
+ }
42
+ };
43
+ xhr.send();
44
+ });
45
+
46
+ window.__defineGetter__("location", function(url){
47
+ return {
48
+ get protocol(){
49
+ return curLocation.scheme() + ":";
50
+ },
51
+ get href(){
52
+ return curLocation.toString();
53
+ },
54
+ toString: function(){
55
+ return this.href.toString();
56
+ }
57
+ };
58
+ });
59
+
60
+ // Timers
61
+
62
+ var timers = [];
63
+
64
+ window.setTimeout = function(fn, time){
65
+ var num;
66
+ return num = setInterval(function(){
67
+ fn();
68
+ clearInterval(num);
69
+ }, time);
70
+ };
71
+
72
+ window.setInterval = function(fn, time){
73
+ var num = timers.length;
74
+
75
+ timers[num] = new Ruby.Thread(function() {
76
+ while(true) {
77
+ Ruby.sleep(time);
78
+ fn();
79
+ }
80
+ });
81
+
82
+ return num;
83
+ };
84
+
85
+ window.clearInterval = function(num){
86
+ if ( timers[num] ) {
87
+ timers[num].kill();
88
+ delete timers[num];
89
+ }
90
+ };
91
+
92
+ // Window Events
93
+
94
+ var events = [{}];
95
+
96
+ window.addEventListener = function(type, fn){
97
+ if ( !this.uuid || this == window ) {
98
+ this.uuid = events.length;
99
+ events[this.uuid] = {};
100
+ }
101
+
102
+ if ( !events[this.uuid][type] )
103
+ events[this.uuid][type] = [];
104
+
105
+ if ( events[this.uuid][type].indexOf( fn ) < 0 )
106
+ events[this.uuid][type].push( fn );
107
+ };
108
+
109
+ window.removeEventListener = function(type, fn){
110
+ if ( !this.uuid || this == window ) {
111
+ this.uuid = events.length;
112
+ events[this.uuid] = {};
113
+ }
114
+
115
+ if ( !events[this.uuid][type] )
116
+ events[this.uuid][type] = [];
117
+
118
+ events[this.uuid][type] =
119
+ events[this.uuid][type].filter(function(f){
120
+ return f != fn;
121
+ });
122
+ };
123
+
124
+ window.dispatchEvent = function(event){
125
+ if ( event.type ) {
126
+ if ( this.uuid && events[this.uuid][event.type] ) {
127
+ var self = this;
128
+
129
+ events[this.uuid][event.type].forEach(function(fn){
130
+ fn.call( self, event );
131
+ });
132
+ }
133
+
134
+ if ( this["on" + event.type] )
135
+ this["on" + event.type].call( self, event );
136
+ }
137
+ };
138
+
139
+ // DOM Document
140
+
141
+ window.DOMDocument = function(file){
142
+ this._file = file;
143
+ var parser = new W3CDOMImplementation();
144
+ try {
145
+ this._dom = parser.loadXML(file);
146
+ } catch(e) {
147
+ Ruby.puts("*** wycats to fix: " + parser.translateErrCode(e.code));
148
+ throw parser.translateErrCode(e.code);
149
+ }
150
+
151
+ if ( !obj_nodes["key?"]( this._dom ) )
152
+ obj_nodes[this._dom] = this;
153
+ };
154
+
155
+ DOMDocument.prototype = {
156
+ nodeType: 1,
157
+ createTextNode: function(text){
158
+ return makeNode( this._dom.createTextNode(
159
+ text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) );
160
+ },
161
+ createElement: function(name){
162
+ return makeNode( this._dom.createElement(name.toLowerCase()) );
163
+ },
164
+ getElementsByTagName: function(name){
165
+ return new DOMNodeList( this._dom.getElementsByTagName(
166
+ name.toLowerCase()) );
167
+ },
168
+ getElementById: function(id){
169
+ return makeNode( this._dom.getElementById(id) );
170
+ },
171
+ get body(){
172
+ return this.getElementsByTagName("body")[0];
173
+ },
174
+ get documentElement(){
175
+ return makeNode( this._dom.getDocumentElement() );
176
+ },
177
+ get ownerDocument(){
178
+ return null;
179
+ },
180
+ addEventListener: window.addEventListener,
181
+ removeEventListener: window.removeEventListener,
182
+ dispatchEvent: window.dispatchEvent,
183
+ get nodeName() {
184
+ return "#document";
185
+ },
186
+ importNode: function(node, deep){
187
+ return makeNode( this._dom.importNode(node._dom, deep) );
188
+ },
189
+ toString: function(){
190
+ return "Document" + (typeof this._file == "string" ?
191
+ ": " + this._file : "");
192
+ },
193
+ get innerHTML(){
194
+ return this.documentElement.outerHTML;
195
+ },
196
+
197
+ get defaultView(){
198
+ return {
199
+ getComputedStyle: function(elem){
200
+ return {
201
+ getPropertyValue: function(prop){
202
+ prop = prop.replace(/\-(\w)/g,function(m,c){
203
+ return c.toUpperCase();
204
+ });
205
+ var val = elem.style[prop];
206
+
207
+ if ( prop === "opacity" && val === "" )
208
+ val = "1";
209
+
210
+ return val;
211
+ }
212
+ };
213
+ }
214
+ };
215
+ },
216
+
217
+ createEvent: function(){
218
+ return {
219
+ type: "",
220
+ initEvent: function(type){
221
+ this.type = type;
222
+ }
223
+ };
224
+ }
225
+ };
226
+
227
+ function getDocument(node){
228
+ return obj_nodes[node];
229
+ }
230
+
231
+ // DOM NodeList
232
+
233
+ window.DOMNodeList = function(list){
234
+ this._dom = list;
235
+ this.length = list.getLength();
236
+
237
+ for ( var i = 0; i < this.length; i++ ) {
238
+ var node = list.item(i);
239
+ this[i] = makeNode( node );
240
+ }
241
+ };
242
+
243
+ DOMNodeList.prototype = {
244
+ toString: function(){
245
+ return "[ " +
246
+ Array.prototype.join.call( this, ", " ) + " ]";
247
+ },
248
+ get outerHTML(){
249
+ return Array.prototype.map.call(
250
+ this, function(node){return node.outerHTML;}).join('');
251
+ }
252
+ };
253
+
254
+ // DOM Node
255
+
256
+ window.DOMNode = function(node){
257
+ this._dom = node;
258
+ };
259
+
260
+ DOMNode.prototype = {
261
+ get nodeType(){
262
+ return this._dom.getNodeType();
263
+ },
264
+ get nodeValue(){
265
+ return this._dom.getNodeValue();
266
+ },
267
+ get nodeName() {
268
+ return this._dom.getNodeName();
269
+ },
270
+ cloneNode: function(deep){
271
+ return makeNode( this._dom.cloneNode(deep) );
272
+ },
273
+ get ownerDocument(){
274
+ return getDocument( this._dom.getOwnerDocument() );
275
+ },
276
+ get documentElement(){
277
+ return makeNode( this._dom.getDocumentElement() );
278
+ },
279
+ get parentNode() {
280
+ return makeNode( this._dom.getParentNode() );
281
+ },
282
+ get nextSibling() {
283
+ return makeNode( this._dom.getNextSibling() );
284
+ },
285
+ get previousSibling() {
286
+ return makeNode( this._dom.getPreviousSibling() );
287
+ },
288
+ toString: function(){
289
+ return '"' + this.nodeValue + '"';
290
+ },
291
+ get outerHTML(){
292
+ return this.nodeValue;
293
+ }
294
+ };
295
+
296
+ // DOM Element
297
+
298
+ window.DOMElement = function(elem){
299
+ this._dom = elem;
300
+ this.style = {
301
+ get opacity(){ return this._opacity; },
302
+ set opacity(val){ this._opacity = val + ""; }
303
+ };
304
+
305
+ // Load CSS info
306
+ var styles = (this.getAttribute("style") || "").split(/\s*;\s*/);
307
+
308
+ for ( var i = 0; i < styles.length; i++ ) {
309
+ var style = styles[i].split(/\s*:\s*/);
310
+ if ( style.length == 2 )
311
+ this.style[ style[0] ] = style[1];
312
+ }
313
+ };
314
+
315
+ DOMElement.prototype = extend( new DOMNode(), {
316
+ get nodeName(){
317
+ return this.tagName.toUpperCase();
318
+ },
319
+ get tagName(){
320
+ return this._dom.getTagName().toUpperCase();
321
+ },
322
+ toString: function(){
323
+ return "<" + this.tagName + (this.id ? "#" + this.id : "" ) + ">";
324
+ },
325
+ get outerHTML(){
326
+ var ret = "<" + this.tagName, attr = this.attributes;
327
+
328
+ for ( var i in attr )
329
+ ret += " " + i + "='" + attr[i] + "'";
330
+
331
+ if ( this.childNodes.length || this.nodeName == "SCRIPT" )
332
+ ret += ">" + this.childNodes.outerHTML +
333
+ "</" + this.tagName + ">";
334
+ else
335
+ ret += "/>";
336
+
337
+ return ret;
338
+ },
339
+
340
+ get attributes(){
341
+ var attr = {}, attrs = this._dom.getAttributes();
342
+
343
+ for ( var i = 0; i < attrs.getLength(); i++ )
344
+ attr[ attrs.item(i).nodeName ] = attrs.item(i).nodeValue;
345
+
346
+ return attr;
347
+ },
348
+
349
+ get innerHTML(){
350
+ return this.childNodes.outerHTML;
351
+ },
352
+ set innerHTML(html){
353
+ html = html.replace(/<\/?([A-Z]+)/g, function(m){
354
+ return m.toLowerCase();
355
+ });
356
+
357
+ var nodes = this.ownerDocument.importNode(
358
+ new DOMDocument( html ).documentElement, true
359
+ ).childNodes;
360
+
361
+ while (this.firstChild)
362
+ this.removeChild( this.firstChild );
363
+
364
+ for ( var i = 0; i < nodes.length; i++ )
365
+ this.appendChild( nodes[i] );
366
+ },
367
+
368
+ get textContent(){
369
+ function nav(nodes){
370
+ var str = "";
371
+ for ( var i = 0; i < nodes.length; i++ ) {
372
+ if ( nodes[i].nodeType == 3 )
373
+ str += nodes[i].nodeValue;
374
+ else if ( nodes[i].nodeType == 1 )
375
+ str += nav(nodes[i].childNodes);
376
+ }
377
+ return str;
378
+ }
379
+
380
+ return nav(this.childNodes);
381
+ },
382
+ set textContent(text){
383
+ while (this.firstChild)
384
+ this.removeChild( this.firstChild );
385
+ this.appendChild( this.ownerDocument.createTextNode(text) );
386
+ },
387
+
388
+ style: {},
389
+ clientHeight: 0,
390
+ clientWidth: 0,
391
+ offsetHeight: 0,
392
+ offsetWidth: 0,
393
+
394
+ get disabled() {
395
+ var val = this.getAttribute("disabled");
396
+ return val != "false" && !!val;
397
+ },
398
+ set disabled(val) { return this.setAttribute("disabled",val); },
399
+
400
+ get checked() {
401
+ var val = this.getAttribute("checked");
402
+ return val != "false" && !!val;
403
+ },
404
+ set checked(val) { return this.setAttribute("checked",val); },
405
+
406
+ get selected() {
407
+ if ( !this._selectDone ) {
408
+ this._selectDone = true;
409
+
410
+ if ( this.nodeName == "OPTION" && !this.parentNode.getAttribute("multiple") ) {
411
+ var opt = this.parentNode.getElementsByTagName("option");
412
+
413
+ if ( this == opt[0] ) {
414
+ var select = true;
415
+
416
+ for ( var i = 1; i < opt.length; i++ ) {
417
+ if ( opt[i].selected ) {
418
+ select = false;
419
+ break;
420
+ }
421
+ }
422
+
423
+ if ( select )
424
+ this.selected = true;
425
+ }
426
+ }
427
+ }
428
+
429
+ var val = this.getAttribute("selected");
430
+ return val != "false" && !!val;
431
+ },
432
+ set selected(val) { return this.setAttribute("selected",val); },
433
+
434
+ get className() { return this.getAttribute("class") || ""; },
435
+ set className(val) {
436
+ return this.setAttribute("class",
437
+ val.replace(/(^\s*|\s*$)/g,""));
438
+ },
439
+
440
+ get type() { return this.getAttribute("type") || ""; },
441
+ set type(val) { return this.setAttribute("type",val); },
442
+
443
+ get value() { return this.getAttribute("value") || ""; },
444
+ set value(val) { return this.setAttribute("value",val); },
445
+
446
+ get src() { return this.getAttribute("src") || ""; },
447
+ set src(val) { return this.setAttribute("src",val); },
448
+
449
+ get id() { return this.getAttribute("id") || ""; },
450
+ set id(val) { return this.setAttribute("id",val); },
451
+
452
+ getAttribute: function(name){
453
+ return this._dom.hasAttribute(name) ?
454
+ new String( this._dom.getAttribute(name) ) :
455
+ null;
456
+ },
457
+ setAttribute: function(name,value){
458
+ this._dom.setAttribute(name,value);
459
+ },
460
+ removeAttribute: function(name){
461
+ this._dom.removeAttribute(name);
462
+ },
463
+
464
+ get childNodes(){
465
+ return new DOMNodeList( this._dom.getChildNodes() );
466
+ },
467
+ get firstChild(){
468
+ return makeNode( this._dom.getFirstChild() );
469
+ },
470
+ get lastChild(){
471
+ return makeNode( this._dom.getLastChild() );
472
+ },
473
+ appendChild: function(node){
474
+ this._dom.appendChild( node._dom );
475
+ },
476
+ insertBefore: function(node,before){
477
+ this._dom.insertBefore( node._dom, before ? before._dom : before );
478
+ },
479
+ removeChild: function(node){
480
+ this._dom.removeChild( node._dom );
481
+ },
482
+
483
+ getElementsByTagName: DOMDocument.prototype.getElementsByTagName,
484
+
485
+ addEventListener: window.addEventListener,
486
+ removeEventListener: window.removeEventListener,
487
+ dispatchEvent: window.dispatchEvent,
488
+
489
+ click: function(){
490
+ var event = document.createEvent();
491
+ event.initEvent("click");
492
+ this.dispatchEvent(event);
493
+ },
494
+ submit: function(){
495
+ var event = document.createEvent();
496
+ event.initEvent("submit");
497
+ this.dispatchEvent(event);
498
+ },
499
+ focus: function(){
500
+ var event = document.createEvent();
501
+ event.initEvent("focus");
502
+ this.dispatchEvent(event);
503
+ },
504
+ blur: function(){
505
+ var event = document.createEvent();
506
+ event.initEvent("blur");
507
+ this.dispatchEvent(event);
508
+ },
509
+ get elements(){
510
+ return this.getElementsByTagName("*");
511
+ },
512
+ get contentWindow(){
513
+ return this.nodeName == "IFRAME" ? {
514
+ document: this.contentDocument
515
+ } : null;
516
+ },
517
+ get contentDocument(){
518
+ if ( this.nodeName == "IFRAME" ) {
519
+ if ( !this._doc )
520
+ this._doc = new DOMDocument(
521
+ "<html><head><title></title></head><body></body></html>"
522
+ );
523
+ return this._doc;
524
+ } else
525
+ return null;
526
+ }
527
+ });
528
+
529
+ // Helper method for extending one object with another
530
+
531
+ function extend(a,b) {
532
+ for ( var i in b ) {
533
+ var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
534
+
535
+ if ( g || s ) {
536
+ if ( g )
537
+ a.__defineGetter__(i, g);
538
+ if ( s )
539
+ a.__defineSetter__(i, s);
540
+ } else
541
+ a[i] = b[i];
542
+ }
543
+ return a;
544
+ }
545
+
546
+ // Helper method for generating the right
547
+ // DOM objects based upon the type
548
+
549
+ var obj_nodes = new Ruby.Hash;
550
+
551
+ function makeNode(node){
552
+ if ( node ) {
553
+ if ( !obj_nodes['key?']( node ) )
554
+ obj_nodes[node] = node.getNodeType() ==
555
+ W3CDOMNode.ELEMENT_NODE ?
556
+ new DOMElement( node ) : new DOMNode( node );
557
+
558
+ return obj_nodes[node];
559
+ } else
560
+ return null;
561
+ }
562
+
563
+ // XMLHttpRequest
564
+ // Originally implemented by Yehuda Katz
565
+
566
+ window.XMLHttpRequest = function(){
567
+ this.headers = {};
568
+ this.responseHeaders = {};
569
+ };
570
+
571
+ XMLHttpRequest.prototype = {
572
+ open: function(method, url, async, user, password){
573
+ this.readyState = 1;
574
+ if (async)
575
+ this.async = true;
576
+ this.method = method || "GET";
577
+ this.url = url;
578
+ this.onreadystatechange();
579
+ },
580
+ setRequestHeader: function(header, value){
581
+ this.headers[header] = value;
582
+ },
583
+ getResponseHeader: function(header){ },
584
+ send: function(data){
585
+ var self = this;
586
+
587
+ function makeRequest(){
588
+ var url = curLocation.merge(self.url);
589
+ var connection;
590
+
591
+ if ( url.scheme == "file" ) {
592
+ if ( self.method == "PUT" ) {
593
+ var out = new Ruby.File(url.path);
594
+ var text = data || "";
595
+
596
+ out.puts( text );
597
+ out.flush();
598
+ out.close();
599
+ } else if ( self.method == "DELETE" ) {
600
+ var file = new Ruby.File(url.path());
601
+ file["delete"]();
602
+ } else if ( self.method == "GET" ) {
603
+ var file = Ruby.File.read(url.path);
604
+ connection = {
605
+ code: "200",
606
+ message: "Ok",
607
+ body: file,
608
+ }
609
+ handleResponse();
610
+ } else {
611
+ connection = Ruby.Net.HTTP.start(url.host, url.port, function(http) {
612
+ http.get(url.path);
613
+ });
614
+ handleResponse();
615
+ }
616
+ } else {
617
+ var http = Ruby.Net.HTTP.new(url.host, url.port);
618
+ var request = new Ruby.Net.HTTP.Get(url.path);
619
+ for (var header in self.headers)
620
+ request.add_field(header, self.headers[header]);
621
+
622
+ var connection = http.request(request);
623
+ connection.each_header(function(k,v) {
624
+ self.responseHeaders[k] = v;
625
+ });
626
+
627
+ handleResponse();
628
+ }
629
+
630
+ function handleResponse(){
631
+ self.readyState = 4;
632
+ self.status = parseInt(connection.code) || undefined;
633
+ self.statusText = connection.message || "";
634
+
635
+ self.responseText = connection.body;
636
+
637
+ self.responseXML = null;
638
+
639
+ if ( self.responseText.match(/^\s*</) ) {
640
+ self.responseXML = new DOMDocument( self.responseText );
641
+ }
642
+ }
643
+
644
+ self.onreadystatechange();
645
+ }
646
+
647
+ if (this.async)
648
+ new Ruby.Thread(function() { makeRequest(); });
649
+ else
650
+ makeRequest();
651
+ },
652
+ abort: function(){},
653
+ onreadystatechange: function(){},
654
+ getResponseHeader: function(header){
655
+ if (this.readyState < 3)
656
+ throw new Error("INVALID_STATE_ERR");
657
+ else {
658
+ var returnedHeaders = [];
659
+ for (var rHeader in this.responseHeaders) {
660
+ if (rHeader.match(new Regexp(header, "i")))
661
+ returnedHeaders.push(this.responseHeaders[rHeader]);
662
+ }
663
+
664
+ if (returnedHeaders.length)
665
+ return returnedHeaders.join(", ");
666
+ }
667
+
668
+ return null;
669
+ },
670
+ getAllResponseHeaders: function(header){
671
+ if (this.readyState < 3)
672
+ throw new Error("INVALID_STATE_ERR");
673
+ else {
674
+ var returnedHeaders = [];
675
+
676
+ for (var aHeader in this.responseHeaders)
677
+ returnedHeaders.push( aHeader + ": " + this.responseHeaders[aHeader] );
678
+
679
+ return returnedHeaders.join("\r\n");
680
+ }
681
+ },
682
+ async: true,
683
+ readyState: 0,
684
+ responseText: "",
685
+ status: 0
686
+ };
687
+ })();