1 : /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Web Workers.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * The Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2011
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Ben Turner <bent.mozilla@gmail.com> (Original Author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "Worker.h"
40 :
41 : #include "jsapi.h"
42 : #include "jsfriendapi.h"
43 :
44 : #include "EventTarget.h"
45 : #include "RuntimeService.h"
46 : #include "WorkerPrivate.h"
47 :
48 : #include "WorkerInlines.h"
49 :
50 : #define PROPERTY_FLAGS \
51 : (JSPROP_ENUMERATE | JSPROP_SHARED)
52 :
53 : #define FUNCTION_FLAGS \
54 : JSPROP_ENUMERATE
55 :
56 : USING_WORKERS_NAMESPACE
57 :
58 : namespace {
59 :
60 : class Worker
61 : {
62 : static JSClass sClass;
63 : static JSPropertySpec sProperties[];
64 : static JSFunctionSpec sFunctions[];
65 :
66 : enum
67 : {
68 : STRING_onerror = 0,
69 : STRING_onmessage,
70 :
71 : STRING_COUNT
72 : };
73 :
74 : static const char* const sEventStrings[STRING_COUNT];
75 :
76 : protected:
77 : enum {
78 : // The constructor function holds a WorkerPrivate* in its first reserved
79 : // slot.
80 : CONSTRUCTOR_SLOT_PARENT = 0
81 : };
82 :
83 : public:
84 : static JSClass*
85 0 : Class()
86 : {
87 0 : return &sClass;
88 : }
89 :
90 : static JSObject*
91 147 : InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
92 : bool aMainRuntime)
93 : {
94 : JSObject* proto = js::InitClassWithReserved(aCx, aObj, aParentProto, &sClass, Construct,
95 147 : 0, sProperties, sFunctions, NULL, NULL);
96 147 : if (!proto) {
97 0 : return NULL;
98 : }
99 :
100 147 : if (!aMainRuntime) {
101 0 : WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
102 0 : parent->AssertIsOnWorkerThread();
103 :
104 0 : JSObject* constructor = JS_GetConstructor(aCx, proto);
105 0 : if (!constructor)
106 0 : return NULL;
107 : js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
108 0 : PRIVATE_TO_JSVAL(parent));
109 : }
110 :
111 147 : return proto;
112 : }
113 :
114 : static void
115 0 : ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers)
116 : {
117 0 : JS_ASSERT(!JS_IsExceptionPending(aCx));
118 :
119 0 : WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
120 0 : JS_ASSERT(worker);
121 :
122 0 : if (aSaveEventHandlers) {
123 0 : for (int index = 0; index < STRING_COUNT; index++) {
124 0 : const char* name = sEventStrings[index];
125 : jsval listener;
126 0 : if (!worker->GetEventListenerOnEventTarget(aCx, name + 2, &listener) ||
127 : !JS_DefineProperty(aCx, aObj, name, listener, NULL, NULL,
128 0 : (PROPERTY_FLAGS & ~JSPROP_SHARED))) {
129 0 : JS_ClearPendingException(aCx);
130 : }
131 : }
132 : }
133 :
134 0 : SetJSPrivateSafeish(aObj, NULL);
135 0 : }
136 :
137 : static WorkerPrivate*
138 : GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
139 :
140 : protected:
141 : static JSBool
142 0 : ConstructInternal(JSContext* aCx, unsigned aArgc, jsval* aVp,
143 : bool aIsChromeWorker)
144 : {
145 0 : if (!aArgc) {
146 0 : JS_ReportError(aCx, "Constructor requires at least one argument!");
147 0 : return false;
148 : }
149 :
150 0 : JSString* scriptURL = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
151 0 : if (!scriptURL) {
152 0 : return false;
153 : }
154 :
155 : jsval priv = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
156 0 : CONSTRUCTOR_SLOT_PARENT);
157 :
158 : RuntimeService* runtimeService;
159 : WorkerPrivate* parent;
160 :
161 0 : if (JSVAL_IS_VOID(priv)) {
162 0 : runtimeService = RuntimeService::GetOrCreateService();
163 0 : if (!runtimeService) {
164 0 : JS_ReportError(aCx, "Failed to create runtime service!");
165 0 : return false;
166 : }
167 0 : parent = NULL;
168 : }
169 : else {
170 0 : runtimeService = RuntimeService::GetService();
171 0 : parent = static_cast<WorkerPrivate*>(JSVAL_TO_PRIVATE(priv));
172 0 : parent->AssertIsOnWorkerThread();
173 : }
174 :
175 0 : JSObject* obj = JS_NewObject(aCx, &sClass, nsnull, nsnull);
176 0 : if (!obj) {
177 0 : return false;
178 : }
179 :
180 : WorkerPrivate* worker = WorkerPrivate::Create(aCx, obj, parent, scriptURL,
181 0 : aIsChromeWorker);
182 0 : if (!worker) {
183 0 : return false;
184 : }
185 :
186 : // Worker now owned by the JS object.
187 0 : SetJSPrivateSafeish(obj, worker);
188 :
189 0 : if (!runtimeService->RegisterWorker(aCx, worker)) {
190 0 : return false;
191 : }
192 :
193 0 : JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
194 0 : return true;
195 : }
196 :
197 : private:
198 : // No instance of this class should ever be created so these are explicitly
199 : // left without an implementation to prevent linking in case someone tries to
200 : // make one.
201 : Worker();
202 : ~Worker();
203 :
204 : static JSBool
205 0 : GetEventListener(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
206 : {
207 0 : JS_ASSERT(JSID_IS_INT(aIdval));
208 0 : JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
209 :
210 0 : const char* name = sEventStrings[JSID_TO_INT(aIdval)];
211 0 : WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
212 0 : if (!worker) {
213 0 : return !JS_IsExceptionPending(aCx);
214 : }
215 :
216 0 : return worker->GetEventListenerOnEventTarget(aCx, name + 2, aVp);
217 : }
218 :
219 : static JSBool
220 0 : SetEventListener(JSContext* aCx, JSObject* aObj, jsid aIdval, JSBool aStrict,
221 : jsval* aVp)
222 : {
223 0 : JS_ASSERT(JSID_IS_INT(aIdval));
224 0 : JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
225 :
226 0 : const char* name = sEventStrings[JSID_TO_INT(aIdval)];
227 0 : WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
228 0 : if (!worker) {
229 0 : return !JS_IsExceptionPending(aCx);
230 : }
231 :
232 0 : return worker->SetEventListenerOnEventTarget(aCx, name + 2, aVp);
233 : }
234 :
235 : static JSBool
236 0 : Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
237 : {
238 0 : return ConstructInternal(aCx, aArgc, aVp, false);
239 : }
240 :
241 : static void
242 147 : Finalize(JSContext* aCx, JSObject* aObj)
243 : {
244 147 : JS_ASSERT(JS_GetClass(aObj) == &sClass);
245 147 : WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
246 147 : if (worker) {
247 0 : worker->FinalizeInstance(aCx, true);
248 : }
249 147 : }
250 :
251 : static void
252 65 : Trace(JSTracer* aTrc, JSObject* aObj)
253 : {
254 65 : JS_ASSERT(JS_GetClass(aObj) == &sClass);
255 65 : WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
256 65 : if (worker) {
257 0 : worker->TraceInstance(aTrc);
258 : }
259 65 : }
260 :
261 : static JSBool
262 0 : Terminate(JSContext* aCx, unsigned aArgc, jsval* aVp)
263 : {
264 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
265 0 : if (!obj) {
266 0 : return false;
267 : }
268 :
269 0 : const char*& name = sFunctions[0].name;
270 0 : WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
271 0 : if (!worker) {
272 0 : return !JS_IsExceptionPending(aCx);
273 : }
274 :
275 0 : return worker->Terminate(aCx);
276 : }
277 :
278 : static JSBool
279 0 : PostMessage(JSContext* aCx, unsigned aArgc, jsval* aVp)
280 : {
281 0 : JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
282 0 : if (!obj) {
283 0 : return false;
284 : }
285 :
286 0 : const char*& name = sFunctions[1].name;
287 0 : WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
288 0 : if (!worker) {
289 0 : return !JS_IsExceptionPending(aCx);
290 : }
291 :
292 : jsval message;
293 0 : if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &message)) {
294 0 : return false;
295 : }
296 :
297 0 : return worker->PostMessage(aCx, message);
298 : }
299 : };
300 :
301 : JSClass Worker::sClass = {
302 : "Worker",
303 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
304 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
305 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
306 : NULL, NULL, NULL, NULL, Trace,
307 : };
308 :
309 : JSPropertySpec Worker::sProperties[] = {
310 1464 : { sEventStrings[STRING_onerror], STRING_onerror, PROPERTY_FLAGS,
311 : GetEventListener, SetEventListener },
312 1464 : { sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
313 : GetEventListener, SetEventListener },
314 : { 0, 0, 0, NULL, NULL }
315 2928 : };
316 :
317 : JSFunctionSpec Worker::sFunctions[] = {
318 : JS_FN("terminate", Terminate, 0, FUNCTION_FLAGS),
319 : JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
320 : JS_FS_END
321 : };
322 :
323 : const char* const Worker::sEventStrings[STRING_COUNT] = {
324 : "onerror",
325 : "onmessage"
326 : };
327 :
328 : class ChromeWorker : public Worker
329 : {
330 : static JSClass sClass;
331 :
332 : public:
333 : static JSClass*
334 0 : Class()
335 : {
336 0 : return &sClass;
337 : }
338 :
339 : static JSObject*
340 147 : InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
341 : bool aMainRuntime)
342 : {
343 : JSObject* proto = js::InitClassWithReserved(aCx, aObj, aParentProto, &sClass, Construct,
344 147 : 0, NULL, NULL, NULL, NULL);
345 147 : if (!proto) {
346 0 : return NULL;
347 : }
348 :
349 147 : if (!aMainRuntime) {
350 0 : WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
351 0 : parent->AssertIsOnWorkerThread();
352 :
353 0 : JSObject* constructor = JS_GetConstructor(aCx, proto);
354 0 : if (!constructor)
355 0 : return NULL;
356 : js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
357 0 : PRIVATE_TO_JSVAL(parent));
358 : }
359 :
360 147 : return proto;
361 : }
362 :
363 : static void
364 0 : ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers)
365 : {
366 0 : Worker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers);
367 0 : }
368 :
369 : private:
370 : // No instance of this class should ever be created so these are explicitly
371 : // left without an implementation to prevent linking in case someone tries to
372 : // make one.
373 : ChromeWorker();
374 : ~ChromeWorker();
375 :
376 : static WorkerPrivate*
377 : GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
378 : {
379 : if (aObj) {
380 : JSClass* classPtr = JS_GetClass(aObj);
381 : if (classPtr == &sClass) {
382 : return GetJSPrivateSafeish<WorkerPrivate>(aObj);
383 : }
384 : }
385 :
386 : return Worker::GetInstancePrivate(aCx, aObj, aFunctionName);
387 : }
388 :
389 : static JSBool
390 0 : Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
391 : {
392 0 : return ConstructInternal(aCx, aArgc, aVp, true);
393 : }
394 :
395 : static void
396 147 : Finalize(JSContext* aCx, JSObject* aObj)
397 : {
398 147 : JS_ASSERT(JS_GetClass(aObj) == &sClass);
399 147 : WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
400 147 : if (worker) {
401 0 : worker->FinalizeInstance(aCx, true);
402 : }
403 147 : }
404 :
405 : static void
406 65 : Trace(JSTracer* aTrc, JSObject* aObj)
407 : {
408 65 : JS_ASSERT(JS_GetClass(aObj) == &sClass);
409 65 : WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
410 65 : if (worker) {
411 0 : worker->TraceInstance(aTrc);
412 : }
413 65 : }
414 : };
415 :
416 : JSClass ChromeWorker::sClass = {
417 : "ChromeWorker",
418 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
419 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
420 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
421 : NULL, NULL, NULL, NULL, Trace
422 : };
423 :
424 : WorkerPrivate*
425 0 : Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
426 : const char* aFunctionName)
427 : {
428 0 : JSClass* classPtr = JS_GetClass(aObj);
429 0 : if (classPtr == &sClass || classPtr == ChromeWorker::Class()) {
430 0 : return GetJSPrivateSafeish<WorkerPrivate>(aObj);
431 : }
432 :
433 : JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
434 0 : sClass.name, aFunctionName, classPtr->name);
435 0 : return NULL;
436 : }
437 :
438 : } // anonymous namespace
439 :
440 : BEGIN_WORKERS_NAMESPACE
441 :
442 : namespace worker {
443 :
444 : JSObject*
445 147 : InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
446 : bool aMainRuntime)
447 : {
448 147 : return Worker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
449 : }
450 :
451 : void
452 0 : ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers)
453 : {
454 0 : JSClass* clasp = JS_GetClass(aObj);
455 0 : JS_ASSERT(clasp == Worker::Class() || clasp == ChromeWorker::Class());
456 :
457 0 : if (clasp == ChromeWorker::Class()) {
458 0 : ChromeWorker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers);
459 : }
460 : else {
461 0 : Worker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers);
462 : }
463 0 : }
464 :
465 : } // namespace worker
466 :
467 : WorkerCrossThreadDispatcher*
468 0 : GetWorkerCrossThreadDispatcher(JSContext* aCx, jsval aWorker)
469 : {
470 0 : if (JSVAL_IS_PRIMITIVE(aWorker)) {
471 0 : return NULL;
472 : }
473 :
474 : WorkerPrivate* w =
475 : Worker::GetInstancePrivate(aCx, JSVAL_TO_OBJECT(aWorker),
476 0 : "GetWorkerCrossThreadDispatcher");
477 0 : if (!w) {
478 0 : return NULL;
479 : }
480 0 : return w->GetCrossThreadDispatcher();
481 : }
482 :
483 :
484 : namespace chromeworker {
485 :
486 : bool
487 147 : InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
488 : bool aMainRuntime)
489 : {
490 147 : return !!ChromeWorker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
491 : }
492 :
493 : } // namespace chromeworker
494 :
495 : bool
496 0 : ClassIsWorker(JSClass* aClass)
497 : {
498 0 : return Worker::Class() == aClass || ChromeWorker::Class() == aClass;
499 : }
500 :
501 4392 : END_WORKERS_NAMESPACE
|