1 : /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 : /* vim: set ts=2 et sw=2 tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Web Workers.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * The Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2011
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Ben Turner <bent.mozilla@gmail.com> (Original Author)
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "mozilla/Util.h"
41 :
42 : #include "WorkerScope.h"
43 :
44 : #include "jsapi.h"
45 : #include "jsdbgapi.h"
46 :
47 : #include "nsTraceRefcnt.h"
48 : #include "xpcpublic.h"
49 :
50 : #include "ChromeWorkerScope.h"
51 : #include "Events.h"
52 : #include "EventTarget.h"
53 : #include "Exceptions.h"
54 : #include "File.h"
55 : #include "FileReaderSync.h"
56 : #include "ListenerManager.h"
57 : #include "Location.h"
58 : #include "Navigator.h"
59 : #include "Principal.h"
60 : #include "ScriptLoader.h"
61 : #include "Worker.h"
62 : #include "WorkerPrivate.h"
63 : #include "XMLHttpRequest.h"
64 : #ifdef ANDROID
65 : #include <android/log.h>
66 : #endif
67 :
68 : #include "WorkerInlines.h"
69 :
70 : #define PROPERTY_FLAGS \
71 : (JSPROP_ENUMERATE | JSPROP_SHARED)
72 :
73 : #define FUNCTION_FLAGS \
74 : JSPROP_ENUMERATE
75 :
76 : using namespace mozilla;
77 : USING_WORKERS_NAMESPACE
78 :
79 : namespace {
80 :
81 : class WorkerGlobalScope : public events::EventTarget
82 : {
83 : static JSClass sClass;
84 : static JSPropertySpec sProperties[];
85 : static JSFunctionSpec sFunctions[];
86 :
87 : enum
88 : {
89 : SLOT_wrappedScope = 0,
90 : SLOT_wrappedFunction
91 : };
92 :
93 : enum
94 : {
95 : SLOT_location = 0,
96 : SLOT_navigator,
97 :
98 : SLOT_COUNT
99 : };
100 :
101 : // Must be traced!
102 : jsval mSlots[SLOT_COUNT];
103 :
104 : enum
105 : {
106 : STRING_onerror = 0,
107 : STRING_onclose,
108 :
109 : STRING_COUNT
110 : };
111 :
112 : static const char* const sEventStrings[STRING_COUNT];
113 :
114 : protected:
115 : WorkerPrivate* mWorker;
116 :
117 : public:
118 : static JSClass*
119 0 : Class()
120 : {
121 0 : return &sClass;
122 : }
123 :
124 : static JSObject*
125 0 : InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
126 : {
127 : return JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct, 0,
128 0 : sProperties, sFunctions, NULL, NULL);
129 : }
130 :
131 : protected:
132 0 : WorkerGlobalScope(WorkerPrivate* aWorker)
133 0 : : mWorker(aWorker)
134 : {
135 0 : MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerGlobalScope);
136 0 : for (int32 i = 0; i < SLOT_COUNT; i++) {
137 0 : mSlots[i] = JSVAL_VOID;
138 : }
139 0 : }
140 :
141 0 : ~WorkerGlobalScope()
142 0 : {
143 0 : MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerGlobalScope);
144 0 : }
145 :
146 : void
147 0 : TraceInstance(JSTracer* aTrc)
148 : {
149 0 : for (int32 i = 0; i < SLOT_COUNT; i++) {
150 0 : JS_CALL_VALUE_TRACER(aTrc, mSlots[i], "WorkerGlobalScope instance slot");
151 : }
152 0 : mWorker->TraceInternal(aTrc);
153 0 : events::EventTarget::TraceInstance(aTrc);
154 0 : }
155 :
156 : void
157 0 : FinalizeInstance(JSContext* aCx)
158 : {
159 0 : events::EventTarget::FinalizeInstance(aCx);
160 0 : }
161 :
162 : private:
163 : static JSBool
164 0 : GetEventListener(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
165 : {
166 0 : JS_ASSERT(JSID_IS_INT(aIdval));
167 0 : JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
168 :
169 0 : const char* name = sEventStrings[JSID_TO_INT(aIdval)];
170 0 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
171 0 : if (!scope) {
172 0 : return false;
173 : }
174 :
175 0 : return scope->GetEventListenerOnEventTarget(aCx, name + 2, aVp);
176 : }
177 :
178 : static JSBool
179 0 : SetEventListener(JSContext* aCx, JSObject* aObj, jsid aIdval, JSBool aStrict,
180 : jsval* aVp)
181 : {
182 0 : JS_ASSERT(JSID_IS_INT(aIdval));
183 0 : JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
184 :
185 0 : const char* name = sEventStrings[JSID_TO_INT(aIdval)];
186 0 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
187 0 : if (!scope) {
188 0 : return false;
189 : }
190 :
191 0 : return scope->SetEventListenerOnEventTarget(aCx, name + 2, aVp);
192 : }
193 :
194 : static WorkerGlobalScope*
195 : GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
196 :
197 : static JSBool
198 0 : Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
199 : {
200 : JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
201 0 : sClass.name);
202 0 : return false;
203 : }
204 :
205 : static JSBool
206 0 : GetSelf(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
207 : {
208 0 : if (!GetInstancePrivate(aCx, aObj, "self")) {
209 0 : return false;
210 : }
211 :
212 0 : *aVp = OBJECT_TO_JSVAL(aObj);
213 0 : return true;
214 : }
215 :
216 : static JSBool
217 0 : GetLocation(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
218 : {
219 : WorkerGlobalScope* scope =
220 0 : GetInstancePrivate(aCx, aObj, sProperties[SLOT_location].name);
221 0 : if (!scope) {
222 0 : return false;
223 : }
224 :
225 0 : if (JSVAL_IS_VOID(scope->mSlots[SLOT_location])) {
226 : JSString* href, *protocol, *host, *hostname;
227 : JSString* port, *pathname, *search, *hash;
228 :
229 0 : WorkerPrivate::LocationInfo& info = scope->mWorker->GetLocationInfo();
230 :
231 : #define COPY_STRING(_jsstr, _cstr) \
232 : if (info. _cstr .IsEmpty()) { \
233 : _jsstr = NULL; \
234 : } \
235 : else { \
236 : if (!(_jsstr = JS_NewStringCopyN(aCx, info. _cstr .get(), \
237 : info. _cstr .Length()))) { \
238 : return false; \
239 : } \
240 : info. _cstr .Truncate(); \
241 : }
242 :
243 0 : COPY_STRING(href, mHref);
244 0 : COPY_STRING(protocol, mProtocol);
245 0 : COPY_STRING(host, mHost);
246 0 : COPY_STRING(hostname, mHostname);
247 0 : COPY_STRING(port, mPort);
248 0 : COPY_STRING(pathname, mPathname);
249 0 : COPY_STRING(search, mSearch);
250 0 : COPY_STRING(hash, mHash);
251 :
252 : #undef COPY_STRING
253 :
254 : JSObject* location = location::Create(aCx, href, protocol, host, hostname,
255 0 : port, pathname, search, hash);
256 0 : if (!location) {
257 0 : return false;
258 : }
259 :
260 0 : scope->mSlots[SLOT_location] = OBJECT_TO_JSVAL(location);
261 : }
262 :
263 0 : *aVp = scope->mSlots[SLOT_location];
264 0 : return true;
265 : }
266 :
267 : static JSBool
268 0 : UnwrapErrorEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
269 : {
270 0 : JS_ASSERT(JSVAL_IS_OBJECT(JS_CALLEE(aCx, aVp)));
271 0 : JS_ASSERT(aArgc == 1);
272 0 : JS_ASSERT(JSVAL_IS_OBJECT(JS_ARGV(aCx, aVp)[0]));
273 :
274 0 : JSObject* wrapper = JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp));
275 0 : JS_ASSERT(JS_ObjectIsFunction(aCx, wrapper));
276 :
277 0 : jsval scope = js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope);
278 0 : jsval listener = js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction);
279 :
280 0 : JS_ASSERT(JSVAL_IS_OBJECT(scope));
281 :
282 0 : JSObject* event = JSVAL_TO_OBJECT(JS_ARGV(aCx, aVp)[0]);
283 :
284 0 : jsval argv[3] = { JSVAL_VOID, JSVAL_VOID, JSVAL_VOID };
285 0 : if (!JS_GetProperty(aCx, event, "message", &argv[0]) ||
286 0 : !JS_GetProperty(aCx, event, "filename", &argv[1]) ||
287 0 : !JS_GetProperty(aCx, event, "lineno", &argv[2])) {
288 0 : return false;
289 : }
290 :
291 0 : jsval rval = JSVAL_VOID;
292 0 : if (!JS_CallFunctionValue(aCx, JSVAL_TO_OBJECT(scope), listener,
293 0 : ArrayLength(argv), argv, &rval)) {
294 0 : JS_ReportPendingException(aCx);
295 0 : return false;
296 : }
297 :
298 0 : if (JSVAL_IS_BOOLEAN(rval) && JSVAL_TO_BOOLEAN(rval) &&
299 0 : !JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL, &rval)) {
300 0 : return false;
301 : }
302 :
303 0 : return true;
304 : }
305 :
306 : static JSBool
307 0 : GetOnErrorListener(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
308 : {
309 0 : const char* name = sEventStrings[STRING_onerror];
310 0 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
311 0 : if (!scope) {
312 0 : return false;
313 : }
314 :
315 : jsval adaptor;
316 0 : if (!scope->GetEventListenerOnEventTarget(aCx, name + 2, &adaptor)) {
317 0 : return false;
318 : }
319 :
320 0 : if (JSVAL_IS_VOID(adaptor)) {
321 0 : *aVp = JSVAL_NULL;
322 0 : return true;
323 : }
324 :
325 0 : JS_ASSERT(JSVAL_IS_OBJECT(adaptor));
326 :
327 : jsval listener = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(adaptor),
328 0 : SLOT_wrappedFunction);
329 :
330 0 : *aVp = listener;
331 0 : return true;
332 : }
333 :
334 : static JSBool
335 0 : SetOnErrorListener(JSContext* aCx, JSObject* aObj, jsid aIdval,
336 : JSBool aStrict, jsval* aVp)
337 : {
338 0 : const char* name = sEventStrings[STRING_onerror];
339 0 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
340 0 : if (!scope) {
341 0 : return false;
342 : }
343 :
344 : JSFunction* adaptor = js::NewFunctionWithReserved(aCx, UnwrapErrorEvent, 1, 0,
345 0 : JS_GetGlobalObject(aCx), "unwrap");
346 0 : if (!adaptor) {
347 0 : return false;
348 : }
349 :
350 0 : JSObject* listener = JS_GetFunctionObject(adaptor);
351 0 : if (!listener) {
352 0 : return false;
353 : }
354 :
355 : js::SetFunctionNativeReserved(listener, SLOT_wrappedScope,
356 0 : OBJECT_TO_JSVAL(aObj));
357 0 : js::SetFunctionNativeReserved(listener, SLOT_wrappedFunction, *aVp);
358 :
359 0 : jsval val = OBJECT_TO_JSVAL(listener);
360 0 : return scope->SetEventListenerOnEventTarget(aCx, name + 2, &val);
361 : }
362 :
363 : static JSBool
364 0 : GetNavigator(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
365 : {
366 : WorkerGlobalScope* scope =
367 0 : GetInstancePrivate(aCx, aObj, sProperties[SLOT_navigator].name);
368 0 : if (!scope) {
369 0 : return false;
370 : }
371 :
372 0 : if (JSVAL_IS_VOID(scope->mSlots[SLOT_navigator])) {
373 0 : JSObject* navigator = navigator::Create(aCx);
374 0 : if (!navigator) {
375 0 : return false;
376 : }
377 :
378 0 : scope->mSlots[SLOT_navigator] = OBJECT_TO_JSVAL(navigator);
379 : }
380 :
381 0 : *aVp = scope->mSlots[SLOT_navigator];
382 0 : return true;
383 : }
384 :
385 : static JSBool
386 0 : Close(JSContext* aCx, unsigned aArgc, jsval* aVp)
387 : {
388 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
389 0 : if (!obj) {
390 0 : return false;
391 : }
392 :
393 0 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[0].name);
394 0 : if (!scope) {
395 0 : return false;
396 : }
397 :
398 0 : return scope->mWorker->CloseInternal(aCx);
399 : }
400 :
401 : static JSBool
402 0 : ImportScripts(JSContext* aCx, unsigned aArgc, jsval* aVp)
403 : {
404 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
405 0 : if (!obj) {
406 0 : return false;
407 : }
408 :
409 0 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[1].name);
410 0 : if (!scope) {
411 0 : return false;
412 : }
413 :
414 0 : if (aArgc && !scriptloader::Load(aCx, aArgc, JS_ARGV(aCx, aVp))) {
415 0 : return false;
416 : }
417 :
418 0 : return true;
419 : }
420 :
421 : static JSBool
422 0 : SetTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp)
423 : {
424 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
425 0 : if (!obj) {
426 0 : return false;
427 : }
428 :
429 0 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[2].name);
430 0 : if (!scope) {
431 0 : return false;
432 : }
433 :
434 : jsval dummy;
435 0 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &dummy)) {
436 0 : return false;
437 : }
438 :
439 0 : return scope->mWorker->SetTimeout(aCx, aArgc, aVp, false);
440 : }
441 :
442 : static JSBool
443 0 : ClearTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp)
444 : {
445 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
446 0 : if (!obj) {
447 0 : return false;
448 : }
449 :
450 0 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[3].name);
451 0 : if (!scope) {
452 0 : return false;
453 : }
454 :
455 : uint32_t id;
456 0 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "u", &id)) {
457 0 : return false;
458 : }
459 :
460 0 : return scope->mWorker->ClearTimeout(aCx, id);
461 : }
462 :
463 : static JSBool
464 0 : SetInterval(JSContext* aCx, unsigned aArgc, jsval* aVp)
465 : {
466 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
467 0 : if (!obj) {
468 0 : return false;
469 : }
470 :
471 0 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[4].name);
472 0 : if (!scope) {
473 0 : return false;
474 : }
475 :
476 : jsval dummy;
477 0 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &dummy)) {
478 0 : return false;
479 : }
480 :
481 0 : return scope->mWorker->SetTimeout(aCx, aArgc, aVp, true);
482 : }
483 :
484 : static JSBool
485 : ClearInterval(JSContext* aCx, unsigned aArgc, jsval* aVp)
486 : {
487 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
488 : if (!obj) {
489 : return false;
490 : }
491 :
492 : WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[5].name);
493 : if (!scope) {
494 : return false;
495 : }
496 :
497 : uint32_t id;
498 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "u", &id)) {
499 : return false;
500 : }
501 :
502 : return scope->mWorker->ClearTimeout(aCx, id);
503 : }
504 :
505 : static JSBool
506 0 : Dump(JSContext* aCx, unsigned aArgc, jsval* aVp)
507 : {
508 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
509 0 : if (!obj) {
510 0 : return false;
511 : }
512 :
513 0 : if (!GetInstancePrivate(aCx, obj, sFunctions[6].name)) {
514 0 : return false;
515 : }
516 :
517 0 : if (aArgc) {
518 0 : JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
519 0 : if (!str) {
520 0 : return false;
521 : }
522 :
523 0 : JSAutoByteString buffer(aCx, str);
524 0 : if (!buffer) {
525 0 : return false;
526 : }
527 :
528 : #ifdef ANDROID
529 : __android_log_print(ANDROID_LOG_INFO, "Gecko", buffer.ptr());
530 : #endif
531 0 : fputs(buffer.ptr(), stdout);
532 0 : fflush(stdout);
533 : }
534 :
535 0 : return true;
536 : }
537 :
538 : static JSBool
539 0 : AtoB(JSContext* aCx, unsigned aArgc, jsval* aVp)
540 : {
541 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
542 0 : if (!obj) {
543 0 : return false;
544 : }
545 :
546 0 : if (!GetInstancePrivate(aCx, obj, sFunctions[7].name)) {
547 0 : return false;
548 : }
549 :
550 : jsval string;
551 0 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &string)) {
552 0 : return false;
553 : }
554 :
555 : jsval result;
556 0 : if (!xpc::Base64Decode(aCx, string, &result)) {
557 0 : return false;
558 : }
559 :
560 0 : JS_SET_RVAL(aCx, aVp, result);
561 0 : return true;
562 : }
563 :
564 : static JSBool
565 0 : BtoA(JSContext* aCx, unsigned aArgc, jsval* aVp)
566 : {
567 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
568 0 : if (!obj) {
569 0 : return false;
570 : }
571 :
572 0 : if (!GetInstancePrivate(aCx, obj, sFunctions[8].name)) {
573 0 : return false;
574 : }
575 :
576 : jsval binary;
577 0 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &binary)) {
578 0 : return false;
579 : }
580 :
581 : jsval result;
582 0 : if (!xpc::Base64Encode(aCx, binary, &result)) {
583 0 : return false;
584 : }
585 :
586 0 : JS_SET_RVAL(aCx, aVp, result);
587 0 : return true;
588 : }
589 : };
590 :
591 : JSClass WorkerGlobalScope::sClass = {
592 : "WorkerGlobalScope",
593 : 0,
594 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
595 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
596 : JSCLASS_NO_OPTIONAL_MEMBERS
597 : };
598 :
599 : JSPropertySpec WorkerGlobalScope::sProperties[] = {
600 : { "location", SLOT_location, PROPERTY_FLAGS, GetLocation,
601 : js_GetterOnlyPropertyStub },
602 1464 : { sEventStrings[STRING_onerror], STRING_onerror, PROPERTY_FLAGS,
603 : GetOnErrorListener, SetOnErrorListener },
604 1464 : { sEventStrings[STRING_onclose], STRING_onclose, PROPERTY_FLAGS,
605 : GetEventListener, SetEventListener },
606 : { "navigator", SLOT_navigator, PROPERTY_FLAGS, GetNavigator,
607 : js_GetterOnlyPropertyStub },
608 : { "self", 0, PROPERTY_FLAGS, GetSelf, js_GetterOnlyPropertyStub },
609 : { 0, 0, 0, NULL, NULL }
610 2928 : };
611 :
612 : JSFunctionSpec WorkerGlobalScope::sFunctions[] = {
613 : JS_FN("close", Close, 0, FUNCTION_FLAGS),
614 : JS_FN("importScripts", ImportScripts, 1, FUNCTION_FLAGS),
615 : JS_FN("setTimeout", SetTimeout, 1, FUNCTION_FLAGS),
616 : JS_FN("clearTimeout", ClearTimeout, 1, FUNCTION_FLAGS),
617 : JS_FN("setInterval", SetInterval, 1, FUNCTION_FLAGS),
618 : JS_FN("clearInterval", ClearTimeout, 1, FUNCTION_FLAGS),
619 : JS_FN("dump", Dump, 1, FUNCTION_FLAGS),
620 : JS_FN("atob", AtoB, 1, FUNCTION_FLAGS),
621 : JS_FN("btoa", BtoA, 1, FUNCTION_FLAGS),
622 : JS_FS_END
623 : };
624 :
625 : const char* const WorkerGlobalScope::sEventStrings[STRING_COUNT] = {
626 : "onerror",
627 : "onclose"
628 : };
629 :
630 : class DedicatedWorkerGlobalScope : public WorkerGlobalScope
631 : {
632 : static JSClass sClass;
633 : static JSPropertySpec sProperties[];
634 : static JSFunctionSpec sFunctions[];
635 :
636 : enum
637 : {
638 : STRING_onmessage = 0,
639 :
640 : STRING_COUNT
641 : };
642 :
643 : static const char* const sEventStrings[STRING_COUNT];
644 :
645 : public:
646 : static JSClass*
647 0 : Class()
648 : {
649 0 : return &sClass;
650 : }
651 :
652 : static JSObject*
653 0 : InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
654 : {
655 : return JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct, 0,
656 0 : sProperties, sFunctions, NULL, NULL);
657 : }
658 :
659 : static JSBool
660 0 : InitPrivate(JSContext* aCx, JSObject* aObj, WorkerPrivate* aWorkerPrivate)
661 : {
662 0 : JS_ASSERT(JS_GetClass(aObj) == &sClass);
663 0 : JS_ASSERT(!GetJSPrivateSafeish<DedicatedWorkerGlobalScope>(aObj));
664 :
665 : DedicatedWorkerGlobalScope* priv =
666 0 : new DedicatedWorkerGlobalScope(aWorkerPrivate);
667 0 : SetJSPrivateSafeish(aObj, priv);
668 :
669 0 : return true;
670 : }
671 :
672 : protected:
673 0 : DedicatedWorkerGlobalScope(WorkerPrivate* aWorker)
674 0 : : WorkerGlobalScope(aWorker)
675 : {
676 0 : MOZ_COUNT_CTOR(mozilla::dom::workers::DedicatedWorkerGlobalScope);
677 0 : }
678 :
679 0 : ~DedicatedWorkerGlobalScope()
680 0 : {
681 0 : MOZ_COUNT_DTOR(mozilla::dom::workers::DedicatedWorkerGlobalScope);
682 0 : }
683 :
684 : using WorkerGlobalScope::TraceInstance;
685 : using WorkerGlobalScope::FinalizeInstance;
686 :
687 : private:
688 : static JSBool
689 0 : GetEventListener(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
690 : {
691 0 : JS_ASSERT(JSID_IS_INT(aIdval));
692 0 : JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
693 :
694 0 : const char* name = sEventStrings[JSID_TO_INT(aIdval)];
695 0 : DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
696 0 : if (!scope) {
697 0 : return false;
698 : }
699 :
700 0 : return scope->GetEventListenerOnEventTarget(aCx, name + 2, aVp);
701 : }
702 :
703 : static JSBool
704 0 : SetEventListener(JSContext* aCx, JSObject* aObj, jsid aIdval, JSBool aStrict,
705 : jsval* aVp)
706 : {
707 0 : JS_ASSERT(JSID_IS_INT(aIdval));
708 0 : JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
709 :
710 0 : const char* name = sEventStrings[JSID_TO_INT(aIdval)];
711 0 : DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
712 0 : if (!scope) {
713 0 : return false;
714 : }
715 :
716 0 : return scope->SetEventListenerOnEventTarget(aCx, name + 2, aVp);
717 : }
718 :
719 : static DedicatedWorkerGlobalScope*
720 0 : GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
721 : {
722 0 : JSClass* classPtr = JS_GetClass(aObj);
723 0 : if (classPtr == &sClass) {
724 0 : return GetJSPrivateSafeish<DedicatedWorkerGlobalScope>(aObj);
725 : }
726 :
727 : JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
728 : JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
729 0 : classPtr->name);
730 0 : return NULL;
731 : }
732 :
733 : static JSBool
734 0 : Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
735 : {
736 : JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
737 0 : sClass.name);
738 0 : return false;
739 : }
740 :
741 : static JSBool
742 0 : Resolve(JSContext* aCx, JSObject* aObj, jsid aId, unsigned aFlags,
743 : JSObject** aObjp)
744 : {
745 : JSBool resolved;
746 0 : if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
747 0 : return false;
748 : }
749 :
750 0 : *aObjp = resolved ? aObj : NULL;
751 0 : return true;
752 : }
753 :
754 : static void
755 0 : Finalize(JSContext* aCx, JSObject* aObj)
756 : {
757 0 : JS_ASSERT(JS_GetClass(aObj) == &sClass);
758 : DedicatedWorkerGlobalScope* scope =
759 0 : GetJSPrivateSafeish<DedicatedWorkerGlobalScope>(aObj);
760 0 : if (scope) {
761 0 : scope->FinalizeInstance(aCx);
762 0 : delete scope;
763 : }
764 0 : }
765 :
766 : static void
767 0 : Trace(JSTracer* aTrc, JSObject* aObj)
768 : {
769 0 : JS_ASSERT(JS_GetClass(aObj) == &sClass);
770 : DedicatedWorkerGlobalScope* scope =
771 0 : GetJSPrivateSafeish<DedicatedWorkerGlobalScope>(aObj);
772 0 : if (scope) {
773 0 : scope->TraceInstance(aTrc);
774 : }
775 0 : }
776 :
777 : static JSBool
778 0 : PostMessage(JSContext* aCx, unsigned aArgc, jsval* aVp)
779 : {
780 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
781 0 : if (!obj) {
782 0 : return false;
783 : }
784 :
785 0 : const char*& name = sFunctions[0].name;
786 0 : DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, name);
787 0 : if (!scope) {
788 0 : return false;
789 : }
790 :
791 : jsval message;
792 0 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &message)) {
793 0 : return false;
794 : }
795 :
796 0 : return scope->mWorker->PostMessageToParent(aCx, message);
797 : }
798 : };
799 :
800 : JSClass DedicatedWorkerGlobalScope::sClass = {
801 : "DedicatedWorkerGlobalScope",
802 : JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE,
803 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
804 : JS_EnumerateStub, reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub,
805 : Finalize, NULL, NULL, NULL, NULL, Trace
806 : };
807 :
808 : JSPropertySpec DedicatedWorkerGlobalScope::sProperties[] = {
809 1464 : { sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
810 : GetEventListener, SetEventListener },
811 : { 0, 0, 0, NULL, NULL }
812 1464 : };
813 :
814 : JSFunctionSpec DedicatedWorkerGlobalScope::sFunctions[] = {
815 : JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
816 : JS_FS_END
817 : };
818 :
819 : const char* const DedicatedWorkerGlobalScope::sEventStrings[STRING_COUNT] = {
820 : "onmessage",
821 : };
822 :
823 : WorkerGlobalScope*
824 0 : WorkerGlobalScope::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
825 : const char* aFunctionName)
826 : {
827 0 : JSClass* classPtr = JS_GetClass(aObj);
828 0 : if (classPtr == &sClass || classPtr == DedicatedWorkerGlobalScope::Class()) {
829 0 : return GetJSPrivateSafeish<WorkerGlobalScope>(aObj);
830 : }
831 :
832 : JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
833 0 : sClass.name, aFunctionName, classPtr->name);
834 0 : return NULL;
835 : }
836 :
837 : } /* anonymous namespace */
838 :
839 : BEGIN_WORKERS_NAMESPACE
840 :
841 : JSObject*
842 0 : CreateDedicatedWorkerGlobalScope(JSContext* aCx)
843 : {
844 0 : WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
845 0 : JS_ASSERT(worker);
846 :
847 : JSObject* global =
848 : JS_NewCompartmentAndGlobalObject(aCx, DedicatedWorkerGlobalScope::Class(),
849 0 : GetWorkerPrincipal());
850 0 : if (!global) {
851 0 : return NULL;
852 : }
853 :
854 0 : JSAutoEnterCompartment ac;
855 0 : if (!ac.enter(aCx, global)) {
856 0 : return NULL;
857 : }
858 :
859 : // Make the private slots now so that all our instance checks succeed.
860 0 : if (!DedicatedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
861 0 : return NULL;
862 : }
863 :
864 : // Proto chain should be:
865 : // global -> DedicatedWorkerGlobalScope
866 : // -> WorkerGlobalScope
867 : // -> EventTarget
868 : // -> Object
869 :
870 : JSObject* eventTargetProto =
871 0 : events::InitEventTargetClass(aCx, global, nsnull);
872 0 : if (!eventTargetProto) {
873 0 : return NULL;
874 : }
875 :
876 : JSObject* scopeProto =
877 0 : WorkerGlobalScope::InitClass(aCx, global, eventTargetProto);
878 0 : if (!scopeProto) {
879 0 : return NULL;
880 : }
881 :
882 : JSObject* dedicatedScopeProto =
883 0 : DedicatedWorkerGlobalScope::InitClass(aCx, global, scopeProto);
884 0 : if (!dedicatedScopeProto) {
885 0 : return NULL;
886 : }
887 :
888 0 : if (!JS_SetPrototype(aCx, global, dedicatedScopeProto)) {
889 0 : return NULL;
890 : }
891 :
892 : JSObject* workerProto = worker::InitClass(aCx, global, eventTargetProto,
893 0 : false);
894 0 : if (!workerProto) {
895 0 : return NULL;
896 : }
897 :
898 0 : if (worker->IsChromeWorker()) {
899 0 : if (!chromeworker::InitClass(aCx, global, workerProto, false) ||
900 0 : !chromeworker::DefineChromeWorkerFunctions(aCx, global)) {
901 0 : return NULL;
902 : }
903 : }
904 :
905 : // Init other classes we care about.
906 0 : if (!events::InitClasses(aCx, global, false) ||
907 0 : !file::InitClasses(aCx, global) ||
908 0 : !filereadersync::InitClass(aCx, global) ||
909 0 : !exceptions::InitClasses(aCx, global) ||
910 0 : !xhr::InitClasses(aCx, global, eventTargetProto) ||
911 0 : !location::InitClass(aCx, global) ||
912 0 : !navigator::InitClass(aCx, global)) {
913 0 : return NULL;
914 : }
915 :
916 0 : if (!JS_DefineProfilingFunctions(aCx, global)) {
917 0 : return NULL;
918 : }
919 :
920 0 : return global;
921 : }
922 :
923 : bool
924 0 : ClassIsWorkerGlobalScope(JSClass* aClass)
925 : {
926 0 : return WorkerGlobalScope::Class() == aClass ||
927 0 : DedicatedWorkerGlobalScope::Class() == aClass;
928 : }
929 :
930 4392 : END_WORKERS_NAMESPACE
|