js/jsd/jsd_xpc.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:4cefd6c9a044
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "jsfriendapi.h"
8 #include "jsd_xpc.h"
9 #include "xpcpublic.h"
10
11 #include "js/GCAPI.h"
12 #include "js/OldDebugAPI.h"
13
14 #include "nsIXPConnect.h"
15 #include "mozilla/ModuleUtils.h"
16 #include "nsIServiceManager.h"
17 #include "nsIScriptGlobalObject.h"
18 #include "nsIObserver.h"
19 #include "nsIObserverService.h"
20 #include "nsICategoryManager.h"
21 #include "nsIJSRuntimeService.h"
22 #include "nsIThreadInternal.h"
23 #include "nsIScriptError.h"
24 #include "nsTArray.h"
25 #include "nsThreadUtils.h"
26 #include "nsMemory.h"
27 #include "jsdebug.h"
28 #include "nsReadableUtils.h"
29 #include "nsCRT.h"
30 #include "nsCycleCollectionParticipant.h"
31 #include "mozilla/Attributes.h"
32
33 /* XXX DOM dependency */
34 #include "nsIScriptContext.h"
35 #include "nsPIDOMWindow.h"
36 #include "nsDOMJSUtils.h"
37 #include "SandboxPrivate.h"
38 #include "nsJSPrincipals.h"
39 #include "nsContentUtils.h"
40 #include "mozilla/dom/ScriptSettings.h"
41
42 using mozilla::AutoSafeJSContext;
43 using mozilla::AutoPushJSContext;
44 using mozilla::dom::AutoNoJSAPI;
45
46 /*
47 * defining CAUTIOUS_SCRIPTHOOK makes jsds disable GC while calling out to the
48 * script hook. This was a hack to avoid some js engine problems that should
49 * be fixed now (see Mozilla bug 77636).
50 */
51 #undef CAUTIOUS_SCRIPTHOOK
52
53 #ifdef DEBUG_verbose
54 # define DEBUG_COUNT(name, count) \
55 { if ((count % 10) == 0) printf (name ": %i\n", count); }
56 # define DEBUG_CREATE(name, count) {count++; DEBUG_COUNT ("+++++ " name,count)}
57 # define DEBUG_DESTROY(name, count) {count--; DEBUG_COUNT ("----- " name,count)}
58 #else
59 # define DEBUG_CREATE(name, count)
60 # define DEBUG_DESTROY(name, count)
61 #endif
62
63 #define ASSERT_VALID_CONTEXT { if (!mCx) return NS_ERROR_NOT_AVAILABLE; }
64 #define ASSERT_VALID_EPHEMERAL { if (!mValid) return NS_ERROR_NOT_AVAILABLE; }
65
66 #define JSDSERVICE_CID \
67 { /* f1299dc2-1dd1-11b2-a347-ee6b7660e048 */ \
68 0xf1299dc2, \
69 0x1dd1, \
70 0x11b2, \
71 {0xa3, 0x47, 0xee, 0x6b, 0x76, 0x60, 0xe0, 0x48} \
72 }
73
74 #define JSDASO_CID \
75 { /* 2fd6b7f6-eb8c-4f32-ad26-113f2c02d0fe */ \
76 0x2fd6b7f6, \
77 0xeb8c, \
78 0x4f32, \
79 {0xad, 0x26, 0x11, 0x3f, 0x2c, 0x02, 0xd0, 0xfe} \
80 }
81
82 #define JSDS_MAJOR_VERSION 1
83 #define JSDS_MINOR_VERSION 2
84
85 #define NS_CATMAN_CTRID "@mozilla.org/categorymanager;1"
86 #define NS_JSRT_CTRID "@mozilla.org/js/xpc/RuntimeService;1"
87
88 #define AUTOREG_CATEGORY "xpcom-autoregistration"
89 #define APPSTART_CATEGORY "app-startup"
90 #define JSD_AUTOREG_ENTRY "JSDebugger Startup Observer"
91 #define JSD_STARTUP_ENTRY "JSDebugger Startup Observer"
92
93 static void
94 jsds_GCSliceCallbackProc (JSRuntime *rt, JS::GCProgress progress, const JS::GCDescription &desc);
95
96 /*******************************************************************************
97 * global vars
98 ******************************************************************************/
99
100 const char implementationString[] = "Mozilla JavaScript Debugger Service";
101
102 const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
103 const char jsdARObserverCtrID[] = "@mozilla.org/js/jsd/app-start-observer;2";
104 const char jsdASObserverCtrID[] = "service,@mozilla.org/js/jsd/app-start-observer;2";
105
106 #ifdef DEBUG_verbose
107 uint32_t gScriptCount = 0;
108 uint32_t gValueCount = 0;
109 uint32_t gPropertyCount = 0;
110 uint32_t gContextCount = 0;
111 uint32_t gFrameCount = 0;
112 #endif
113
114 static jsdService *gJsds = 0;
115 static JS::GCSliceCallback gPrevGCSliceCallback = jsds_GCSliceCallbackProc;
116 static bool gGCRunning = false;
117
118 static struct DeadScript {
119 PRCList links;
120 JSDContext *jsdc;
121 jsdIScript *script;
122 } *gDeadScripts = nullptr;
123
124 enum PatternType {
125 ptIgnore = 0U,
126 ptStartsWith = 1U,
127 ptEndsWith = 2U,
128 ptContains = 3U,
129 ptEquals = 4U
130 };
131
132 static struct FilterRecord {
133 PRCList links;
134 jsdIFilter *filterObject;
135 nsCString urlPattern;
136 PatternType patternType;
137 uint32_t startLine;
138 uint32_t endLine;
139 } *gFilters = nullptr;
140
141 static struct LiveEphemeral *gLiveValues = nullptr;
142 static struct LiveEphemeral *gLiveProperties = nullptr;
143 static struct LiveEphemeral *gLiveContexts = nullptr;
144 static struct LiveEphemeral *gLiveStackFrames = nullptr;
145
146 /*******************************************************************************
147 * utility functions for ephemeral lists
148 *******************************************************************************/
149 already_AddRefed<jsdIEphemeral>
150 jsds_FindEphemeral (LiveEphemeral **listHead, void *key)
151 {
152 if (!*listHead)
153 return nullptr;
154
155 LiveEphemeral *lv_record =
156 reinterpret_cast<LiveEphemeral *>
157 (PR_NEXT_LINK(&(*listHead)->links));
158 do
159 {
160 if (lv_record->key == key)
161 {
162 nsCOMPtr<jsdIEphemeral> ret = lv_record->value;
163 return ret.forget();
164 }
165 lv_record = reinterpret_cast<LiveEphemeral *>
166 (PR_NEXT_LINK(&lv_record->links));
167 }
168 while (lv_record != *listHead);
169
170 return nullptr;
171 }
172
173 void
174 jsds_InvalidateAllEphemerals (LiveEphemeral **listHead)
175 {
176 LiveEphemeral *lv_record =
177 reinterpret_cast<LiveEphemeral *>
178 (PR_NEXT_LINK(&(*listHead)->links));
179 do
180 {
181 LiveEphemeral *next =
182 reinterpret_cast<LiveEphemeral *>
183 (PR_NEXT_LINK(&lv_record->links));
184 lv_record->value->Invalidate();
185 lv_record = next;
186 }
187 while (*listHead);
188 }
189
190 void
191 jsds_InsertEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
192 {
193 if (*listHead) {
194 /* if the list exists, add to it */
195 PR_APPEND_LINK(&item->links, &(*listHead)->links);
196 } else {
197 /* otherwise create the list */
198 PR_INIT_CLIST(&item->links);
199 *listHead = item;
200 }
201 }
202
203 void
204 jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
205 {
206 LiveEphemeral *next = reinterpret_cast<LiveEphemeral *>
207 (PR_NEXT_LINK(&item->links));
208
209 if (next == item)
210 {
211 /* if the current item is also the next item, we're the only element,
212 * null out the list head */
213 NS_ASSERTION (*listHead == item,
214 "How could we not be the head of a one item list?");
215 *listHead = nullptr;
216 }
217 else if (item == *listHead)
218 {
219 /* otherwise, if we're currently the list head, change it */
220 *listHead = next;
221 }
222
223 PR_REMOVE_AND_INIT_LINK(&item->links);
224 }
225
226 /*******************************************************************************
227 * utility functions for filters
228 *******************************************************************************/
229 void
230 jsds_FreeFilter (FilterRecord *rec)
231 {
232 NS_IF_RELEASE (rec->filterObject);
233 PR_Free (rec);
234 }
235
236 /* copies appropriate |filter| attributes into |rec|.
237 * False return indicates failure, the contents of |rec| will not be changed.
238 */
239 bool
240 jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter)
241 {
242 NS_ASSERTION (rec, "jsds_SyncFilter without rec");
243 NS_ASSERTION (filter, "jsds_SyncFilter without filter");
244
245 uint32_t startLine;
246 nsresult rv = filter->GetStartLine(&startLine);
247 if (NS_FAILED(rv))
248 return false;
249
250 uint32_t endLine;
251 rv = filter->GetStartLine(&endLine);
252 if (NS_FAILED(rv))
253 return false;
254
255 nsAutoCString urlPattern;
256 rv = filter->GetUrlPattern (urlPattern);
257 if (NS_FAILED(rv))
258 return false;
259
260 uint32_t len = urlPattern.Length();
261 if (len) {
262 if (urlPattern[0] == '*') {
263 /* pattern starts with a *, shift all chars once to the left,
264 * including the trailing null. */
265 urlPattern = Substring(urlPattern, 1, len);
266
267 if (urlPattern[len - 2] == '*') {
268 /* pattern is in the format "*foo*", overwrite the final * with
269 * a null. */
270 urlPattern.Truncate(len - 2);
271 rec->patternType = ptContains;
272 } else {
273 /* pattern is in the format "*foo", just make a note of the
274 * new length. */
275 rec->patternType = ptEndsWith;
276 }
277 } else if (urlPattern[len - 1] == '*') {
278 /* pattern is in the format "foo*", overwrite the final * with a
279 * null. */
280 urlPattern.Truncate(len - 1);
281 rec->patternType = ptStartsWith;
282 } else {
283 /* pattern is in the format "foo". */
284 rec->patternType = ptEquals;
285 }
286 } else {
287 rec->patternType = ptIgnore;
288 }
289
290 /* we got everything we need without failing, now copy it into rec. */
291
292 if (rec->filterObject != filter) {
293 NS_IF_RELEASE(rec->filterObject);
294 NS_ADDREF(filter);
295 rec->filterObject = filter;
296 }
297
298 rec->startLine = startLine;
299 rec->endLine = endLine;
300
301 rec->urlPattern = urlPattern;
302
303 return true;
304
305 }
306
307 FilterRecord *
308 jsds_FindFilter (jsdIFilter *filter)
309 {
310 if (!gFilters)
311 return nullptr;
312
313 FilterRecord *current = gFilters;
314
315 do {
316 if (current->filterObject == filter)
317 return current;
318 current = reinterpret_cast<FilterRecord *>
319 (PR_NEXT_LINK(&current->links));
320 } while (current != gFilters);
321
322 return nullptr;
323 }
324
325 /* returns true if the hook should be executed. */
326 bool
327 jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state)
328 {
329 JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state);
330
331 if (!frame) {
332 NS_WARNING("No frame in threadstate");
333 return false;
334 }
335
336 JSDScript *script = JSD_GetScriptForStackFrame (jsdc, state, frame);
337 if (!script)
338 return true;
339
340 uintptr_t pc = JSD_GetPCForStackFrame (jsdc, state, frame);
341
342 nsCString url(JSD_GetScriptFilename (jsdc, script));
343 if (url.IsEmpty()) {
344 NS_WARNING ("Script with no filename");
345 return false;
346 }
347
348 if (!gFilters)
349 return true;
350
351 uint32_t currentLine = JSD_GetClosestLine (jsdc, script, pc);
352 uint32_t len = 0;
353 FilterRecord *currentFilter = gFilters;
354 do {
355 uint32_t flags = 0;
356
357 #ifdef DEBUG
358 nsresult rv =
359 #endif
360 currentFilter->filterObject->GetFlags(&flags);
361 NS_ASSERTION(NS_SUCCEEDED(rv), "Error getting flags for filter");
362
363 if (flags & jsdIFilter::FLAG_ENABLED) {
364 /* If there is no start line, or the start line is before
365 * or equal to the current */
366 if ((!currentFilter->startLine ||
367 currentFilter->startLine <= currentLine) &&
368 /* and there is no end line, or the end line is after
369 * or equal to the current */
370 (!currentFilter->endLine ||
371 currentFilter->endLine >= currentLine)) {
372 /* then we're going to have to compare the url. */
373 if (currentFilter->patternType == ptIgnore)
374 return !!(flags & jsdIFilter::FLAG_PASS);
375
376 if (!len)
377 len = url.Length();
378 nsCString urlPattern = currentFilter->urlPattern;
379 uint32_t patternLength = urlPattern.Length();
380 if (len >= patternLength) {
381 switch (currentFilter->patternType) {
382 case ptEquals:
383 if (urlPattern.Equals(url))
384 return !!(flags & jsdIFilter::FLAG_PASS);
385 break;
386 case ptStartsWith:
387 if (urlPattern.Equals(Substring(url, 0, patternLength)))
388 return !!(flags & jsdIFilter::FLAG_PASS);
389 break;
390 case ptEndsWith:
391 if (urlPattern.Equals(Substring(url, len - patternLength)))
392 return !!(flags & jsdIFilter::FLAG_PASS);
393 break;
394 case ptContains:
395 {
396 nsACString::const_iterator start, end;
397 url.BeginReading(start);
398 url.EndReading(end);
399 if (FindInReadable(currentFilter->urlPattern, start, end))
400 return !!(flags & jsdIFilter::FLAG_PASS);
401 }
402 break;
403 default:
404 NS_ERROR("Invalid pattern type");
405 }
406 }
407 }
408 }
409 currentFilter = reinterpret_cast<FilterRecord *>
410 (PR_NEXT_LINK(&currentFilter->links));
411 } while (currentFilter != gFilters);
412
413 return true;
414
415 }
416
417 /*******************************************************************************
418 * c callbacks
419 *******************************************************************************/
420
421 static void
422 jsds_NotifyPendingDeadScripts (JSRuntime *rt)
423 {
424 jsdService *jsds = gJsds;
425
426 nsCOMPtr<jsdIScriptHook> hook;
427 if (jsds) {
428 NS_ADDREF(jsds);
429 jsds->GetScriptHook (getter_AddRefs(hook));
430 jsds->DoPause(nullptr, true);
431 }
432
433 DeadScript *deadScripts = gDeadScripts;
434 gDeadScripts = nullptr;
435 while (deadScripts) {
436 DeadScript *ds = deadScripts;
437 /* get next deleted script */
438 deadScripts = reinterpret_cast<DeadScript *>
439 (PR_NEXT_LINK(&ds->links));
440 if (deadScripts == ds)
441 deadScripts = nullptr;
442
443 if (hook)
444 {
445 /* tell the user this script has been destroyed */
446 #ifdef CAUTIOUS_SCRIPTHOOK
447 JS_UNKEEP_ATOMS(rt);
448 #endif
449 hook->OnScriptDestroyed (ds->script);
450 #ifdef CAUTIOUS_SCRIPTHOOK
451 JS_KEEP_ATOMS(rt);
452 #endif
453 }
454
455 /* take it out of the circular list */
456 PR_REMOVE_LINK(&ds->links);
457
458 /* addref came from the FromPtr call in jsds_ScriptHookProc */
459 NS_RELEASE(ds->script);
460 /* free the struct! */
461 PR_Free(ds);
462 }
463
464 if (jsds) {
465 jsds->DoUnPause(nullptr, true);
466 NS_RELEASE(jsds);
467 }
468 }
469
470 static void
471 jsds_GCSliceCallbackProc (JSRuntime *rt, JS::GCProgress progress, const JS::GCDescription &desc)
472 {
473 if (progress == JS::GC_CYCLE_END || progress == JS::GC_SLICE_END) {
474 NS_ASSERTION(gGCRunning, "GC slice callback was missed");
475
476 while (gDeadScripts)
477 jsds_NotifyPendingDeadScripts (rt);
478
479 gGCRunning = false;
480 } else {
481 NS_ASSERTION(!gGCRunning, "should not re-enter GC");
482 gGCRunning = true;
483 }
484
485 if (gPrevGCSliceCallback)
486 (*gPrevGCSliceCallback)(rt, progress, desc);
487 }
488
489 static unsigned
490 jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message,
491 JSErrorReport *report, void *callerdata)
492 {
493 static bool running = false;
494
495 nsCOMPtr<jsdIErrorHook> hook;
496 gJsds->GetErrorHook(getter_AddRefs(hook));
497 if (!hook)
498 return JSD_ERROR_REPORTER_PASS_ALONG;
499
500 if (running)
501 return JSD_ERROR_REPORTER_PASS_ALONG;
502
503 running = true;
504
505 nsCOMPtr<jsdIValue> val;
506 if (JS_IsExceptionPending(cx)) {
507 JS::RootedValue jv(cx);
508 JS_GetPendingException(cx, &jv);
509 JSDValue *jsdv = JSD_NewValue (jsdc, jv);
510 val = dont_AddRef(jsdValue::FromPtr(jsdc, jsdv));
511 }
512
513 nsAutoCString fileName;
514 uint32_t line;
515 uint32_t pos;
516 uint32_t flags;
517 uint32_t errnum;
518 bool rval;
519 if (report) {
520 fileName.Assign(report->filename);
521 line = report->lineno;
522 pos = report->tokenptr - report->linebuf;
523 flags = report->flags;
524 errnum = report->errorNumber;
525 }
526 else
527 {
528 line = 0;
529 pos = 0;
530 flags = 0;
531 errnum = 0;
532 }
533
534 gJsds->DoPause(nullptr, true);
535 hook->OnError (nsDependentCString(message), fileName, line, pos, flags, errnum, val, &rval);
536 gJsds->DoUnPause(nullptr, true);
537
538 running = false;
539 if (!rval)
540 return JSD_ERROR_REPORTER_DEBUG;
541
542 return JSD_ERROR_REPORTER_PASS_ALONG;
543 }
544
545 static bool
546 jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
547 unsigned type, void* callerdata)
548 {
549 nsCOMPtr<jsdICallHook> hook;
550
551 switch (type)
552 {
553 case JSD_HOOK_TOPLEVEL_START:
554 case JSD_HOOK_TOPLEVEL_END:
555 gJsds->GetTopLevelHook(getter_AddRefs(hook));
556 break;
557
558 case JSD_HOOK_FUNCTION_CALL:
559 case JSD_HOOK_FUNCTION_RETURN:
560 gJsds->GetFunctionHook(getter_AddRefs(hook));
561 break;
562
563 default:
564 NS_ASSERTION (0, "Unknown hook type.");
565 }
566
567 if (!hook)
568 return true;
569
570 if (!jsds_FilterHook (jsdc, jsdthreadstate))
571 return false;
572
573 JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
574 nsCOMPtr<jsdIStackFrame> frame =
575 dont_AddRef(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, native_frame));
576 gJsds->DoPause(nullptr, true);
577 hook->OnCall(frame, type);
578 gJsds->DoUnPause(nullptr, true);
579 jsdStackFrame::InvalidateAll();
580
581 return true;
582 }
583
584 static uint32_t
585 jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
586 unsigned type, void* callerdata, jsval* rval)
587 {
588 nsCOMPtr<jsdIExecutionHook> hook(0);
589 uint32_t hook_rv = JSD_HOOK_RETURN_CONTINUE;
590 nsCOMPtr<jsdIValue> js_rv;
591
592 switch (type)
593 {
594 case JSD_HOOK_INTERRUPTED:
595 gJsds->GetInterruptHook(getter_AddRefs(hook));
596 break;
597 case JSD_HOOK_DEBUG_REQUESTED:
598 gJsds->GetDebugHook(getter_AddRefs(hook));
599 break;
600 case JSD_HOOK_DEBUGGER_KEYWORD:
601 gJsds->GetDebuggerHook(getter_AddRefs(hook));
602 break;
603 case JSD_HOOK_BREAKPOINT:
604 {
605 /* we can't pause breakpoints the way we pause the other
606 * execution hooks (at least, not easily.) Instead we bail
607 * here if the service is paused. */
608 uint32_t level;
609 gJsds->GetPauseDepth(&level);
610 if (!level)
611 gJsds->GetBreakpointHook(getter_AddRefs(hook));
612 }
613 break;
614 case JSD_HOOK_THROW:
615 {
616 hook_rv = JSD_HOOK_RETURN_CONTINUE_THROW;
617 gJsds->GetThrowHook(getter_AddRefs(hook));
618 if (hook) {
619 JSDValue *jsdv = JSD_GetException (jsdc, jsdthreadstate);
620 js_rv = dont_AddRef(jsdValue::FromPtr (jsdc, jsdv));
621 }
622 break;
623 }
624 default:
625 NS_ASSERTION (0, "Unknown hook type.");
626 }
627
628 if (!hook)
629 return hook_rv;
630
631 if (!jsds_FilterHook (jsdc, jsdthreadstate))
632 return JSD_HOOK_RETURN_CONTINUE;
633
634 JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
635 nsCOMPtr<jsdIStackFrame> frame =
636 dont_AddRef(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, native_frame));
637 gJsds->DoPause(nullptr, true);
638 jsdIValue *inout_rv = js_rv;
639 NS_IF_ADDREF(inout_rv);
640 hook->OnExecute (frame, type, &inout_rv, &hook_rv);
641 js_rv = inout_rv;
642 NS_IF_RELEASE(inout_rv);
643 gJsds->DoUnPause(nullptr, true);
644 jsdStackFrame::InvalidateAll();
645
646 if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
647 hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL) {
648 *rval = JSVAL_VOID;
649 if (js_rv) {
650 JSDValue *jsdv;
651 if (NS_SUCCEEDED(js_rv->GetJSDValue (&jsdv)))
652 *rval = JSD_GetValueWrappedJSVal(jsdc, jsdv);
653 }
654 }
655
656 return hook_rv;
657 }
658
659 static void
660 jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, bool creating,
661 void* callerdata)
662 {
663 #ifdef CAUTIOUS_SCRIPTHOOK
664 JSRuntime *rt = JS_GetRuntime(nsContentUtils::GetSafeJSContext());
665 #endif
666
667 if (creating) {
668 nsCOMPtr<jsdIScriptHook> hook;
669 gJsds->GetScriptHook(getter_AddRefs(hook));
670
671 /* a script is being created */
672 if (!hook) {
673 /* nobody cares, just exit */
674 return;
675 }
676
677 nsCOMPtr<jsdIScript> script =
678 dont_AddRef(jsdScript::FromPtr(jsdc, jsdscript));
679 #ifdef CAUTIOUS_SCRIPTHOOK
680 JS_UNKEEP_ATOMS(rt);
681 #endif
682 gJsds->DoPause(nullptr, true);
683 hook->OnScriptCreated (script);
684 gJsds->DoUnPause(nullptr, true);
685 #ifdef CAUTIOUS_SCRIPTHOOK
686 JS_KEEP_ATOMS(rt);
687 #endif
688 } else {
689 /* a script is being destroyed. even if there is no registered hook
690 * we'll still need to invalidate the jsdIScript record, in order
691 * to remove the reference held in the JSDScript private data. */
692 nsCOMPtr<jsdIScript> jsdis =
693 static_cast<jsdIScript *>(JSD_GetScriptPrivate(jsdscript));
694 if (!jsdis)
695 return;
696
697 jsdis->Invalidate();
698
699 if (!gGCRunning) {
700 nsCOMPtr<jsdIScriptHook> hook;
701 gJsds->GetScriptHook(getter_AddRefs(hook));
702 if (!hook)
703 return;
704
705 /* if GC *isn't* running, we can tell the user about the script
706 * delete now. */
707 #ifdef CAUTIOUS_SCRIPTHOOK
708 JS_UNKEEP_ATOMS(rt);
709 #endif
710
711 gJsds->DoPause(nullptr, true);
712 hook->OnScriptDestroyed (jsdis);
713 gJsds->DoUnPause(nullptr, true);
714 #ifdef CAUTIOUS_SCRIPTHOOK
715 JS_KEEP_ATOMS(rt);
716 #endif
717 } else {
718 /* if a GC *is* running, we've got to wait until it's done before
719 * we can execute any JS, so we queue the notification in a PRCList
720 * until GC tells us it's done. See jsds_GCCallbackProc(). */
721 DeadScript *ds = PR_NEW(DeadScript);
722 if (!ds)
723 return; /* NS_ERROR_OUT_OF_MEMORY */
724
725 ds->jsdc = jsdc;
726 ds->script = jsdis;
727 NS_ADDREF(ds->script);
728 if (gDeadScripts)
729 /* if the queue exists, add to it */
730 PR_APPEND_LINK(&ds->links, &gDeadScripts->links);
731 else {
732 /* otherwise create the queue */
733 PR_INIT_CLIST(&ds->links);
734 gDeadScripts = ds;
735 }
736 }
737 }
738 }
739
740 /*******************************************************************************
741 * reflected jsd data structures
742 *******************************************************************************/
743
744 /* Contexts */
745 /*
746 NS_IMPL_ISUPPORTS(jsdContext, jsdIContext, jsdIEphemeral);
747
748 NS_IMETHODIMP
749 jsdContext::GetJSDContext(JSDContext **_rval)
750 {
751 *_rval = mCx;
752 return NS_OK;
753 }
754 */
755
756 /* Objects */
757 NS_IMPL_ISUPPORTS(jsdObject, jsdIObject)
758
759 NS_IMETHODIMP
760 jsdObject::GetJSDContext(JSDContext **_rval)
761 {
762 *_rval = mCx;
763 return NS_OK;
764 }
765
766 NS_IMETHODIMP
767 jsdObject::GetJSDObject(JSDObject **_rval)
768 {
769 *_rval = mObject;
770 return NS_OK;
771 }
772
773 NS_IMETHODIMP
774 jsdObject::GetCreatorURL(nsACString &_rval)
775 {
776 _rval.Assign(JSD_GetObjectNewURL(mCx, mObject));
777 return NS_OK;
778 }
779
780 NS_IMETHODIMP
781 jsdObject::GetCreatorLine(uint32_t *_rval)
782 {
783 *_rval = JSD_GetObjectNewLineNumber(mCx, mObject);
784 return NS_OK;
785 }
786
787 NS_IMETHODIMP
788 jsdObject::GetConstructorURL(nsACString &_rval)
789 {
790 _rval.Assign(JSD_GetObjectConstructorURL(mCx, mObject));
791 return NS_OK;
792 }
793
794 NS_IMETHODIMP
795 jsdObject::GetConstructorLine(uint32_t *_rval)
796 {
797 *_rval = JSD_GetObjectConstructorLineNumber(mCx, mObject);
798 return NS_OK;
799 }
800
801 NS_IMETHODIMP
802 jsdObject::GetValue(jsdIValue **_rval)
803 {
804 JSDValue *jsdv = JSD_GetValueForObject (mCx, mObject);
805
806 *_rval = jsdValue::FromPtr (mCx, jsdv);
807 return NS_OK;
808 }
809
810 /* Properties */
811 NS_IMPL_ISUPPORTS(jsdProperty, jsdIProperty, jsdIEphemeral)
812
813 jsdProperty::jsdProperty (JSDContext *aCx, JSDProperty *aProperty) :
814 mCx(aCx), mProperty(aProperty)
815 {
816 DEBUG_CREATE ("jsdProperty", gPropertyCount);
817 mValid = (aCx && aProperty);
818 mLiveListEntry.value = this;
819 jsds_InsertEphemeral (&gLiveProperties, &mLiveListEntry);
820 }
821
822 jsdProperty::~jsdProperty ()
823 {
824 DEBUG_DESTROY ("jsdProperty", gPropertyCount);
825 if (mValid)
826 Invalidate();
827 }
828
829 NS_IMETHODIMP
830 jsdProperty::Invalidate()
831 {
832 ASSERT_VALID_EPHEMERAL;
833 mValid = false;
834 jsds_RemoveEphemeral (&gLiveProperties, &mLiveListEntry);
835 JSD_DropProperty (mCx, mProperty);
836 return NS_OK;
837 }
838
839 void
840 jsdProperty::InvalidateAll()
841 {
842 if (gLiveProperties)
843 jsds_InvalidateAllEphemerals (&gLiveProperties);
844 }
845
846 NS_IMETHODIMP
847 jsdProperty::GetJSDContext(JSDContext **_rval)
848 {
849 *_rval = mCx;
850 return NS_OK;
851 }
852
853 NS_IMETHODIMP
854 jsdProperty::GetJSDProperty(JSDProperty **_rval)
855 {
856 *_rval = mProperty;
857 return NS_OK;
858 }
859
860 NS_IMETHODIMP
861 jsdProperty::GetIsValid(bool *_rval)
862 {
863 *_rval = mValid;
864 return NS_OK;
865 }
866
867 NS_IMETHODIMP
868 jsdProperty::GetAlias(jsdIValue **_rval)
869 {
870 JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
871
872 *_rval = jsdValue::FromPtr (mCx, jsdv);
873 return NS_OK;
874 }
875
876 NS_IMETHODIMP
877 jsdProperty::GetFlags(uint32_t *_rval)
878 {
879 *_rval = JSD_GetPropertyFlags (mCx, mProperty);
880 return NS_OK;
881 }
882
883 NS_IMETHODIMP
884 jsdProperty::GetName(jsdIValue **_rval)
885 {
886 JSDValue *jsdv = JSD_GetPropertyName (mCx, mProperty);
887
888 *_rval = jsdValue::FromPtr (mCx, jsdv);
889 return NS_OK;
890 }
891
892 NS_IMETHODIMP
893 jsdProperty::GetValue(jsdIValue **_rval)
894 {
895 JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
896
897 *_rval = jsdValue::FromPtr (mCx, jsdv);
898 return NS_OK;
899 }
900
901 /* Scripts */
902 NS_IMPL_ISUPPORTS(jsdScript, jsdIScript, jsdIEphemeral)
903
904 static NS_IMETHODIMP
905 AssignToJSString(JSDContext *aCx, nsACString *x, JSString *str_)
906 {
907 if (!str_) {
908 x->SetLength(0);
909 return NS_OK;
910 }
911 JS::RootedString str(JSD_GetJSRuntime(aCx), str_);
912 AutoSafeJSContext cx;
913 JSAutoCompartment ac(cx, JSD_GetDefaultGlobal(aCx)); // Just in case.
914 size_t length = JS_GetStringEncodingLength(cx, str);
915 if (length == size_t(-1))
916 return NS_ERROR_FAILURE;
917 x->SetLength(uint32_t(length));
918 if (x->Length() != uint32_t(length))
919 return NS_ERROR_OUT_OF_MEMORY;
920 JS_EncodeStringToBuffer(cx, str, x->BeginWriting(), length);
921 return NS_OK;
922 }
923
924 jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(false),
925 mTag(0),
926 mCx(aCx),
927 mScript(aScript),
928 mFileName(0),
929 mFunctionName(0),
930 mBaseLineNumber(0),
931 mLineExtent(0),
932 mPPLineMap(0),
933 mFirstPC(0)
934 {
935 DEBUG_CREATE ("jsdScript", gScriptCount);
936
937 if (mScript) {
938 /* copy the script's information now, so we have it later, when it
939 * gets destroyed. */
940 JSD_LockScriptSubsystem(mCx);
941 mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript));
942 mFunctionName = new nsCString();
943 if (mFunctionName) {
944 JSString *str = JSD_GetScriptFunctionId(mCx, mScript);
945 if (str)
946 AssignToJSString(mCx, mFunctionName, str);
947 }
948 mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript);
949 mLineExtent = JSD_GetScriptLineExtent(mCx, mScript);
950 mFirstPC = JSD_GetClosestPC(mCx, mScript, 0);
951 JSD_UnlockScriptSubsystem(mCx);
952
953 mValid = true;
954 }
955 }
956
957 jsdScript::~jsdScript ()
958 {
959 DEBUG_DESTROY ("jsdScript", gScriptCount);
960 delete mFileName;
961 delete mFunctionName;
962
963 if (mPPLineMap)
964 PR_Free(mPPLineMap);
965
966 /* Invalidate() needs to be called to release an owning reference to
967 * ourselves, so if we got here without being invalidated, something
968 * has gone wrong with our ref count. */
969 NS_ASSERTION (!mValid, "Script destroyed without being invalidated.");
970 }
971
972 /*
973 * This method populates a line <-> pc map for a pretty printed version of this
974 * script. It does this by decompiling, and then recompiling the script. The
975 * resulting script is scanned for the line map, and then left as GC fodder.
976 */
977 PCMapEntry *
978 jsdScript::CreatePPLineMap()
979 {
980 AutoSafeJSContext cx;
981 JSAutoCompartment ac(cx, JSD_GetDefaultGlobal (mCx)); // Just in case.
982 JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
983 if (!obj)
984 return nullptr;
985 JS::RootedFunction fun(cx, JSD_GetJSFunction (mCx, mScript));
986 JS::RootedScript script(cx); /* In JSD compartment */
987 uint32_t baseLine;
988 JS::RootedString jsstr(cx);
989 size_t length;
990 const jschar *chars;
991
992 if (fun) {
993 unsigned nargs;
994
995 {
996 JSAutoCompartment ac(cx, JS_GetFunctionObject(fun));
997 nargs = JS_GetFunctionArgumentCount(cx, fun);
998 if (nargs > 12)
999 return nullptr;
1000 jsstr = JS_DecompileFunctionBody (cx, fun, 4);
1001 if (!jsstr)
1002 return nullptr;
1003
1004 if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length)))
1005 return nullptr;
1006 }
1007
1008 JS::Anchor<JSString *> kungFuDeathGrip(jsstr);
1009 static const char *const argnames[] = {
1010 "arg1", "arg2", "arg3", "arg4",
1011 "arg5", "arg6", "arg7", "arg8",
1012 "arg9", "arg10", "arg11", "arg12"
1013 };
1014 JS::CompileOptions options(cx);
1015 options.setFileAndLine("x-jsd:ppbuffer?type=function", 3);
1016 fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames, chars,
1017 length, options);
1018 if (!fun || !(script = JS_GetFunctionScript(cx, fun)))
1019 return nullptr;
1020 baseLine = 3;
1021 } else {
1022 script = JSD_GetJSScript(mCx, mScript);
1023 JSString *jsstr;
1024
1025 {
1026 JSAutoCompartment ac(cx, script);
1027
1028 jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
1029 if (!jsstr)
1030 return nullptr;
1031
1032 if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length)))
1033 return nullptr;
1034 }
1035
1036 JS::Anchor<JSString *> kungFuDeathGrip(jsstr);
1037 JS::CompileOptions options(cx);
1038 options.setFileAndLine("x-jsd:ppbuffer?type=script", 1);
1039 script = JS_CompileUCScript(cx, obj, chars, length, options);
1040 if (!script)
1041 return nullptr;
1042 baseLine = 1;
1043 }
1044
1045 uint32_t scriptExtent = JS_GetScriptLineExtent (cx, script);
1046 jsbytecode* firstPC = JS_LineNumberToPC (cx, script, 0);
1047 /* allocate worst case size of map (number of lines in script + 1
1048 * for our 0 record), we'll shrink it with a realloc later. */
1049 PCMapEntry *lineMap =
1050 static_cast<PCMapEntry *>
1051 (PR_Malloc((scriptExtent + 1) * sizeof (PCMapEntry)));
1052 uint32_t lineMapSize = 0;
1053
1054 if (lineMap) {
1055 for (uint32_t line = baseLine; line < scriptExtent + baseLine; ++line) {
1056 jsbytecode* pc = JS_LineNumberToPC (cx, script, line);
1057 if (line == JS_PCToLineNumber (cx, script, pc)) {
1058 lineMap[lineMapSize].line = line;
1059 lineMap[lineMapSize].pc = pc - firstPC;
1060 ++lineMapSize;
1061 }
1062 }
1063 if (scriptExtent != lineMapSize) {
1064 lineMap =
1065 static_cast<PCMapEntry *>
1066 (PR_Realloc(mPPLineMap = lineMap,
1067 lineMapSize * sizeof(PCMapEntry)));
1068 if (!lineMap) {
1069 PR_Free(mPPLineMap);
1070 lineMapSize = 0;
1071 }
1072 }
1073 }
1074
1075 mPCMapSize = lineMapSize;
1076 return mPPLineMap = lineMap;
1077 }
1078
1079 uint32_t
1080 jsdScript::PPPcToLine (uint32_t aPC)
1081 {
1082 if (!mPPLineMap && !CreatePPLineMap())
1083 return 0;
1084 uint32_t i;
1085 for (i = 1; i < mPCMapSize; ++i) {
1086 if (mPPLineMap[i].pc > aPC)
1087 return mPPLineMap[i - 1].line;
1088 }
1089
1090 return mPPLineMap[mPCMapSize - 1].line;
1091 }
1092
1093 uint32_t
1094 jsdScript::PPLineToPc (uint32_t aLine)
1095 {
1096 if (!mPPLineMap && !CreatePPLineMap())
1097 return 0;
1098 uint32_t i;
1099 for (i = 1; i < mPCMapSize; ++i) {
1100 if (mPPLineMap[i].line > aLine)
1101 return mPPLineMap[i - 1].pc;
1102 }
1103
1104 return mPPLineMap[mPCMapSize - 1].pc;
1105 }
1106
1107 NS_IMETHODIMP
1108 jsdScript::GetJSDContext(JSDContext **_rval)
1109 {
1110 ASSERT_VALID_EPHEMERAL;
1111 *_rval = mCx;
1112 return NS_OK;
1113 }
1114
1115 NS_IMETHODIMP
1116 jsdScript::GetJSDScript(JSDScript **_rval)
1117 {
1118 ASSERT_VALID_EPHEMERAL;
1119 *_rval = mScript;
1120 return NS_OK;
1121 }
1122
1123 NS_IMETHODIMP
1124 jsdScript::GetVersion (int32_t *_rval)
1125 {
1126 ASSERT_VALID_EPHEMERAL;
1127 AutoSafeJSContext cx;
1128 JS::RootedScript script(cx, JSD_GetJSScript(mCx, mScript));
1129 JSAutoCompartment ac(cx, script);
1130 *_rval = static_cast<int32_t>(JS_GetScriptVersion(cx, script));
1131 return NS_OK;
1132 }
1133
1134 NS_IMETHODIMP
1135 jsdScript::GetTag(uint32_t *_rval)
1136 {
1137 if (!mTag)
1138 mTag = ++jsdScript::LastTag;
1139
1140 *_rval = mTag;
1141 return NS_OK;
1142 }
1143
1144 NS_IMETHODIMP
1145 jsdScript::Invalidate()
1146 {
1147 ASSERT_VALID_EPHEMERAL;
1148 mValid = false;
1149
1150 /* release the addref we do in FromPtr */
1151 jsdIScript *script = static_cast<jsdIScript *>
1152 (JSD_GetScriptPrivate(mScript));
1153 NS_ASSERTION (script == this, "That's not my script!");
1154 NS_RELEASE(script);
1155 JSD_SetScriptPrivate(mScript, nullptr);
1156 return NS_OK;
1157 }
1158
1159 void
1160 jsdScript::InvalidateAll ()
1161 {
1162 JSDContext *cx;
1163 if (NS_FAILED(gJsds->GetJSDContext (&cx)))
1164 return;
1165
1166 JSDScript *script;
1167 JSDScript *iter = nullptr;
1168
1169 JSD_LockScriptSubsystem(cx);
1170 while((script = JSD_IterateScripts(cx, &iter)) != nullptr) {
1171 nsCOMPtr<jsdIScript> jsdis =
1172 static_cast<jsdIScript *>(JSD_GetScriptPrivate(script));
1173 if (jsdis)
1174 jsdis->Invalidate();
1175 }
1176 JSD_UnlockScriptSubsystem(cx);
1177 }
1178
1179 NS_IMETHODIMP
1180 jsdScript::GetIsValid(bool *_rval)
1181 {
1182 *_rval = mValid;
1183 return NS_OK;
1184 }
1185
1186 NS_IMETHODIMP
1187 jsdScript::SetFlags(uint32_t flags)
1188 {
1189 ASSERT_VALID_EPHEMERAL;
1190 JSD_SetScriptFlags(mCx, mScript, flags);
1191 return NS_OK;
1192 }
1193
1194 NS_IMETHODIMP
1195 jsdScript::GetFlags(uint32_t *_rval)
1196 {
1197 ASSERT_VALID_EPHEMERAL;
1198 *_rval = JSD_GetScriptFlags(mCx, mScript);
1199 return NS_OK;
1200 }
1201
1202 NS_IMETHODIMP
1203 jsdScript::GetFileName(nsACString &_rval)
1204 {
1205 _rval.Assign(*mFileName);
1206 return NS_OK;
1207 }
1208
1209 NS_IMETHODIMP
1210 jsdScript::GetFunctionName(nsACString &_rval)
1211 {
1212 _rval.Assign(*mFunctionName);
1213 return NS_OK;
1214 }
1215
1216 NS_IMETHODIMP
1217 jsdScript::GetParameterNames(uint32_t* count, char16_t*** paramNames)
1218 {
1219 ASSERT_VALID_EPHEMERAL;
1220 AutoSafeJSContext cx;
1221 JS::RootedFunction fun(cx, JSD_GetJSFunction (mCx, mScript));
1222 if (!fun) {
1223 *count = 0;
1224 *paramNames = nullptr;
1225 return NS_OK;
1226 }
1227
1228 JSAutoCompartment ac(cx, JS_GetFunctionObject(fun));
1229
1230 unsigned nargs;
1231 if (!JS_FunctionHasLocalNames(cx, fun) ||
1232 (nargs = JS_GetFunctionArgumentCount(cx, fun)) == 0) {
1233 *count = 0;
1234 *paramNames = nullptr;
1235 return NS_OK;
1236 }
1237
1238 char16_t **ret =
1239 static_cast<char16_t**>(NS_Alloc(nargs * sizeof(char16_t*)));
1240 if (!ret)
1241 return NS_ERROR_OUT_OF_MEMORY;
1242
1243 void *mark;
1244 uintptr_t *names = JS_GetFunctionLocalNameArray(cx, fun, &mark);
1245 if (!names) {
1246 NS_Free(ret);
1247 return NS_ERROR_OUT_OF_MEMORY;
1248 }
1249
1250 nsresult rv = NS_OK;
1251 for (unsigned i = 0; i < nargs; ++i) {
1252 JSAtom *atom = JS_LocalNameToAtom(names[i]);
1253 if (!atom) {
1254 ret[i] = 0;
1255 } else {
1256 JSString *str = JS_AtomKey(atom);
1257 ret[i] = NS_strndup(JS_GetInternedStringChars(str), JS_GetStringLength(str));
1258 if (!ret[i]) {
1259 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret);
1260 rv = NS_ERROR_OUT_OF_MEMORY;
1261 break;
1262 }
1263 }
1264 }
1265 JS_ReleaseFunctionLocalNameArray(cx, mark);
1266 if (NS_FAILED(rv))
1267 return rv;
1268 *count = nargs;
1269 *paramNames = ret;
1270 return NS_OK;
1271 }
1272
1273 NS_IMETHODIMP
1274 jsdScript::GetFunctionObject(jsdIValue **_rval)
1275 {
1276 JS::RootedFunction fun(JSD_GetJSRuntime(mCx), JSD_GetJSFunction(mCx, mScript));
1277 if (!fun)
1278 return NS_ERROR_NOT_AVAILABLE;
1279
1280 AutoSafeJSContext jsContext;
1281 JS::RootedObject obj(jsContext, JS_GetFunctionObject(fun));
1282 if (!obj)
1283 return NS_ERROR_FAILURE;
1284
1285 JSDContext *cx;
1286 if (NS_FAILED(gJsds->GetJSDContext (&cx)))
1287 return NS_ERROR_NOT_INITIALIZED;
1288
1289 JSDValue *jsdv = JSD_NewValue(cx, OBJECT_TO_JSVAL(obj));
1290 if (!jsdv)
1291 return NS_ERROR_OUT_OF_MEMORY;
1292
1293 *_rval = jsdValue::FromPtr(cx, jsdv);
1294 if (!*_rval) {
1295 JSD_DropValue(cx, jsdv);
1296 return NS_ERROR_OUT_OF_MEMORY;
1297 }
1298
1299 return NS_OK;
1300 }
1301
1302 NS_IMETHODIMP
1303 jsdScript::GetFunctionSource(nsAString & aFunctionSource)
1304 {
1305 ASSERT_VALID_EPHEMERAL;
1306 AutoSafeJSContext cx_;
1307 JSContext *cx = cx_; // Appease the type system with Maybe<>s below.
1308 JS::RootedFunction fun(cx, JSD_GetJSFunction (mCx, mScript));
1309
1310 JSString *jsstr;
1311 mozilla::Maybe<JSAutoCompartment> ac;
1312 if (fun) {
1313 ac.construct(cx, JS_GetFunctionObject(fun));
1314 jsstr = JS_DecompileFunction (cx, fun, 4);
1315 } else {
1316 JS::RootedScript script(cx, JSD_GetJSScript (mCx, mScript));
1317 ac.construct(cx, script);
1318 jsstr = JS_DecompileScript (cx, script, "ppscript", 4);
1319 }
1320 if (!jsstr)
1321 return NS_ERROR_FAILURE;
1322
1323 size_t length;
1324 const jschar *chars = JS_GetStringCharsZAndLength(cx, jsstr, &length);
1325 if (!chars)
1326 return NS_ERROR_FAILURE;
1327
1328 aFunctionSource = nsDependentString(chars, length);
1329 return NS_OK;
1330 }
1331
1332 NS_IMETHODIMP
1333 jsdScript::GetBaseLineNumber(uint32_t *_rval)
1334 {
1335 *_rval = mBaseLineNumber;
1336 return NS_OK;
1337 }
1338
1339 NS_IMETHODIMP
1340 jsdScript::GetLineExtent(uint32_t *_rval)
1341 {
1342 *_rval = mLineExtent;
1343 return NS_OK;
1344 }
1345
1346 NS_IMETHODIMP
1347 jsdScript::GetCallCount(uint32_t *_rval)
1348 {
1349 ASSERT_VALID_EPHEMERAL;
1350 *_rval = JSD_GetScriptCallCount (mCx, mScript);
1351 return NS_OK;
1352 }
1353
1354 NS_IMETHODIMP
1355 jsdScript::GetMaxRecurseDepth(uint32_t *_rval)
1356 {
1357 ASSERT_VALID_EPHEMERAL;
1358 *_rval = JSD_GetScriptMaxRecurseDepth (mCx, mScript);
1359 return NS_OK;
1360 }
1361
1362 NS_IMETHODIMP
1363 jsdScript::GetMinExecutionTime(double *_rval)
1364 {
1365 ASSERT_VALID_EPHEMERAL;
1366 *_rval = JSD_GetScriptMinExecutionTime (mCx, mScript);
1367 return NS_OK;
1368 }
1369
1370 NS_IMETHODIMP
1371 jsdScript::GetMaxExecutionTime(double *_rval)
1372 {
1373 ASSERT_VALID_EPHEMERAL;
1374 *_rval = JSD_GetScriptMaxExecutionTime (mCx, mScript);
1375 return NS_OK;
1376 }
1377
1378 NS_IMETHODIMP
1379 jsdScript::GetTotalExecutionTime(double *_rval)
1380 {
1381 ASSERT_VALID_EPHEMERAL;
1382 *_rval = JSD_GetScriptTotalExecutionTime (mCx, mScript);
1383 return NS_OK;
1384 }
1385
1386 NS_IMETHODIMP
1387 jsdScript::GetMinOwnExecutionTime(double *_rval)
1388 {
1389 ASSERT_VALID_EPHEMERAL;
1390 *_rval = JSD_GetScriptMinOwnExecutionTime (mCx, mScript);
1391 return NS_OK;
1392 }
1393
1394 NS_IMETHODIMP
1395 jsdScript::GetMaxOwnExecutionTime(double *_rval)
1396 {
1397 ASSERT_VALID_EPHEMERAL;
1398 *_rval = JSD_GetScriptMaxOwnExecutionTime (mCx, mScript);
1399 return NS_OK;
1400 }
1401
1402 NS_IMETHODIMP
1403 jsdScript::GetTotalOwnExecutionTime(double *_rval)
1404 {
1405 ASSERT_VALID_EPHEMERAL;
1406 *_rval = JSD_GetScriptTotalOwnExecutionTime (mCx, mScript);
1407 return NS_OK;
1408 }
1409
1410 NS_IMETHODIMP
1411 jsdScript::ClearProfileData()
1412 {
1413 ASSERT_VALID_EPHEMERAL;
1414 JSD_ClearScriptProfileData(mCx, mScript);
1415 return NS_OK;
1416 }
1417
1418 NS_IMETHODIMP
1419 jsdScript::PcToLine(uint32_t aPC, uint32_t aPcmap, uint32_t *_rval)
1420 {
1421 ASSERT_VALID_EPHEMERAL;
1422 if (aPcmap == PCMAP_SOURCETEXT) {
1423 *_rval = JSD_GetClosestLine (mCx, mScript, mFirstPC + aPC);
1424 } else if (aPcmap == PCMAP_PRETTYPRINT) {
1425 *_rval = PPPcToLine(aPC);
1426 } else {
1427 return NS_ERROR_INVALID_ARG;
1428 }
1429
1430 return NS_OK;
1431 }
1432
1433 NS_IMETHODIMP
1434 jsdScript::LineToPc(uint32_t aLine, uint32_t aPcmap, uint32_t *_rval)
1435 {
1436 ASSERT_VALID_EPHEMERAL;
1437 if (aPcmap == PCMAP_SOURCETEXT) {
1438 uintptr_t pc = JSD_GetClosestPC (mCx, mScript, aLine);
1439 *_rval = pc - mFirstPC;
1440 } else if (aPcmap == PCMAP_PRETTYPRINT) {
1441 *_rval = PPLineToPc(aLine);
1442 } else {
1443 return NS_ERROR_INVALID_ARG;
1444 }
1445
1446 return NS_OK;
1447 }
1448
1449 NS_IMETHODIMP
1450 jsdScript::EnableSingleStepInterrupts(bool enable)
1451 {
1452 ASSERT_VALID_EPHEMERAL;
1453
1454 /* Must have set interrupt hook before enabling */
1455 if (enable && !jsdService::GetService()->CheckInterruptHook())
1456 return NS_ERROR_NOT_INITIALIZED;
1457
1458 return (JSD_EnableSingleStepInterrupts(mCx, mScript, enable) ? NS_OK : NS_ERROR_FAILURE);
1459 }
1460
1461 NS_IMETHODIMP
1462 jsdScript::GetExecutableLines(uint32_t aPcmap, uint32_t aStartLine, uint32_t aMaxLines,
1463 uint32_t* aCount, uint32_t** aExecutableLines)
1464 {
1465 ASSERT_VALID_EPHEMERAL;
1466 if (aPcmap == PCMAP_SOURCETEXT) {
1467 uintptr_t start = JSD_GetClosestPC(mCx, mScript, 0);
1468 unsigned lastLine = JSD_GetScriptBaseLineNumber(mCx, mScript)
1469 + JSD_GetScriptLineExtent(mCx, mScript) - 1;
1470 uintptr_t end = JSD_GetClosestPC(mCx, mScript, lastLine + 1);
1471
1472 *aExecutableLines = static_cast<uint32_t*>(NS_Alloc((end - start + 1) * sizeof(uint32_t)));
1473 if (!JSD_GetLinePCs(mCx, mScript, aStartLine, aMaxLines, aCount, aExecutableLines,
1474 nullptr))
1475 return NS_ERROR_OUT_OF_MEMORY;
1476
1477 return NS_OK;
1478 }
1479
1480 if (aPcmap == PCMAP_PRETTYPRINT) {
1481 if (!mPPLineMap) {
1482 if (!CreatePPLineMap())
1483 return NS_ERROR_OUT_OF_MEMORY;
1484 }
1485
1486 nsTArray<uint32_t> lines;
1487 uint32_t i;
1488
1489 for (i = 0; i < mPCMapSize; ++i) {
1490 if (mPPLineMap[i].line >= aStartLine)
1491 break;
1492 }
1493
1494 for (; i < mPCMapSize && lines.Length() < aMaxLines; ++i) {
1495 lines.AppendElement(mPPLineMap[i].line);
1496 }
1497
1498 if (aCount)
1499 *aCount = lines.Length();
1500
1501 *aExecutableLines = static_cast<uint32_t*>(NS_Alloc(lines.Length() * sizeof(uint32_t)));
1502 if (!*aExecutableLines)
1503 return NS_ERROR_OUT_OF_MEMORY;
1504
1505 for (i = 0; i < lines.Length(); ++i)
1506 (*aExecutableLines)[i] = lines[i];
1507
1508 return NS_OK;
1509 }
1510
1511 return NS_ERROR_INVALID_ARG;
1512 }
1513
1514 NS_IMETHODIMP
1515 jsdScript::IsLineExecutable(uint32_t aLine, uint32_t aPcmap, bool *_rval)
1516 {
1517 ASSERT_VALID_EPHEMERAL;
1518 if (aPcmap == PCMAP_SOURCETEXT) {
1519 uintptr_t pc = JSD_GetClosestPC (mCx, mScript, aLine);
1520 *_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc));
1521 } else if (aPcmap == PCMAP_PRETTYPRINT) {
1522 if (!mPPLineMap && !CreatePPLineMap())
1523 return NS_ERROR_OUT_OF_MEMORY;
1524 *_rval = false;
1525 for (uint32_t i = 0; i < mPCMapSize; ++i) {
1526 if (mPPLineMap[i].line >= aLine) {
1527 *_rval = (mPPLineMap[i].line == aLine);
1528 break;
1529 }
1530 }
1531 } else {
1532 return NS_ERROR_INVALID_ARG;
1533 }
1534
1535 return NS_OK;
1536 }
1537
1538 NS_IMETHODIMP
1539 jsdScript::SetBreakpoint(uint32_t aPC)
1540 {
1541 ASSERT_VALID_EPHEMERAL;
1542 uintptr_t pc = mFirstPC + aPC;
1543 JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc, nullptr);
1544 return NS_OK;
1545 }
1546
1547 NS_IMETHODIMP
1548 jsdScript::ClearBreakpoint(uint32_t aPC)
1549 {
1550 ASSERT_VALID_EPHEMERAL;
1551 uintptr_t pc = mFirstPC + aPC;
1552 JSD_ClearExecutionHook (mCx, mScript, pc);
1553 return NS_OK;
1554 }
1555
1556 NS_IMETHODIMP
1557 jsdScript::ClearAllBreakpoints()
1558 {
1559 ASSERT_VALID_EPHEMERAL;
1560 JSD_LockScriptSubsystem(mCx);
1561 JSD_ClearAllExecutionHooksForScript (mCx, mScript);
1562 JSD_UnlockScriptSubsystem(mCx);
1563 return NS_OK;
1564 }
1565
1566 /* Contexts */
1567 NS_IMPL_ISUPPORTS(jsdContext, jsdIContext, jsdIEphemeral)
1568
1569 jsdIContext *
1570 jsdContext::FromPtr (JSDContext *aJSDCx, JSContext *aJSCx)
1571 {
1572 if (!aJSDCx || !aJSCx)
1573 return nullptr;
1574
1575 nsCOMPtr<jsdIContext> jsdicx;
1576 nsCOMPtr<jsdIEphemeral> eph =
1577 jsds_FindEphemeral (&gLiveContexts, static_cast<void *>(aJSCx));
1578 if (eph)
1579 {
1580 jsdicx = do_QueryInterface(eph);
1581 }
1582 else
1583 {
1584 nsCOMPtr<nsISupports> iscx;
1585 if (JS::ContextOptionsRef(aJSCx).privateIsNSISupports())
1586 iscx = static_cast<nsISupports *>(JS_GetContextPrivate(aJSCx));
1587 jsdicx = new jsdContext (aJSDCx, aJSCx, iscx);
1588 }
1589
1590 jsdIContext *ctx = nullptr;
1591 jsdicx.swap(ctx);
1592 return ctx;
1593 }
1594
1595 jsdContext::jsdContext (JSDContext *aJSDCx, JSContext *aJSCx,
1596 nsISupports *aISCx) : mValid(true),
1597 mScriptDisabledForWindowWithID(0),
1598 mTag(0),
1599 mJSDCx(aJSDCx),
1600 mJSCx(aJSCx), mISCx(aISCx)
1601 {
1602 DEBUG_CREATE ("jsdContext", gContextCount);
1603 mLiveListEntry.value = this;
1604 mLiveListEntry.key = static_cast<void *>(aJSCx);
1605 jsds_InsertEphemeral (&gLiveContexts, &mLiveListEntry);
1606 }
1607
1608 jsdContext::~jsdContext()
1609 {
1610 DEBUG_DESTROY ("jsdContext", gContextCount);
1611 if (mValid)
1612 {
1613 /* call Invalidate() to take ourselves out of the live list */
1614 Invalidate();
1615 }
1616 }
1617
1618 NS_IMETHODIMP
1619 jsdContext::GetIsValid(bool *_rval)
1620 {
1621 *_rval = mValid;
1622 return NS_OK;
1623 }
1624
1625 NS_IMETHODIMP
1626 jsdContext::Invalidate()
1627 {
1628 ASSERT_VALID_EPHEMERAL;
1629 mValid = false;
1630 jsds_RemoveEphemeral (&gLiveContexts, &mLiveListEntry);
1631 return NS_OK;
1632 }
1633
1634 void
1635 jsdContext::InvalidateAll()
1636 {
1637 if (gLiveContexts)
1638 jsds_InvalidateAllEphemerals (&gLiveContexts);
1639 }
1640
1641 NS_IMETHODIMP
1642 jsdContext::GetJSContext(JSContext **_rval)
1643 {
1644 ASSERT_VALID_EPHEMERAL;
1645 *_rval = mJSCx;
1646 return NS_OK;
1647 }
1648
1649 /* Simulate the old options API in terms of the new one for backwards
1650 * compatibility */
1651
1652 #define JSOPTION_EXTRA_WARNINGS JS_BIT(0)
1653 #define JSOPTION_WERROR JS_BIT(1)
1654 #define JSOPTION_VAROBJFIX JS_BIT(2)
1655 #define JSOPTION_PRIVATE_IS_NSISUPPORTS JS_BIT(3)
1656 #define JSOPTION_DONT_REPORT_UNCAUGHT JS_BIT(8)
1657 #define JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT JS_BIT(11)
1658 #define JSOPTION_NO_SCRIPT_RVAL JS_BIT(12)
1659 #define JSOPTION_STRICT_MODE JS_BIT(17)
1660 #define JSOPTION_MASK JS_BITMASK(20)
1661
1662 NS_IMETHODIMP
1663 jsdContext::GetOptions(uint32_t *_rval)
1664 {
1665 ASSERT_VALID_EPHEMERAL;
1666 *_rval = (JS::ContextOptionsRef(mJSCx).extraWarnings() ? JSOPTION_EXTRA_WARNINGS : 0)
1667 | (JS::ContextOptionsRef(mJSCx).werror() ? JSOPTION_WERROR : 0)
1668 | (JS::ContextOptionsRef(mJSCx).varObjFix() ? JSOPTION_VAROBJFIX : 0)
1669 | (JS::ContextOptionsRef(mJSCx).privateIsNSISupports() ? JSOPTION_PRIVATE_IS_NSISUPPORTS : 0)
1670 | (JS::ContextOptionsRef(mJSCx).dontReportUncaught() ? JSOPTION_DONT_REPORT_UNCAUGHT : 0)
1671 | (JS::ContextOptionsRef(mJSCx).noDefaultCompartmentObject() ? JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT : 0)
1672 | (JS::ContextOptionsRef(mJSCx).noScriptRval() ? JSOPTION_NO_SCRIPT_RVAL : 0)
1673 | (JS::ContextOptionsRef(mJSCx).strictMode() ? JSOPTION_STRICT_MODE : 0);
1674 return NS_OK;
1675 }
1676
1677 NS_IMETHODIMP
1678 jsdContext::SetOptions(uint32_t options)
1679 {
1680 ASSERT_VALID_EPHEMERAL;
1681
1682 /* don't let users change this option, they'd just be shooting themselves
1683 * in the foot. */
1684 if (JS::ContextOptionsRef(mJSCx).privateIsNSISupports() !=
1685 (options & JSOPTION_PRIVATE_IS_NSISUPPORTS))
1686 return NS_ERROR_ILLEGAL_VALUE;
1687
1688 JS::ContextOptionsRef(mJSCx).setExtraWarnings(options & JSOPTION_EXTRA_WARNINGS)
1689 .setWerror(options & JSOPTION_WERROR)
1690 .setVarObjFix(options & JSOPTION_VAROBJFIX)
1691 .setDontReportUncaught(options & JSOPTION_DONT_REPORT_UNCAUGHT)
1692 .setNoDefaultCompartmentObject(options & JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT)
1693 .setNoScriptRval(options & JSOPTION_NO_SCRIPT_RVAL)
1694 .setStrictMode(options & JSOPTION_STRICT_MODE);
1695 return NS_OK;
1696 }
1697
1698 NS_IMETHODIMP
1699 jsdContext::GetPrivateData(nsISupports **_rval)
1700 {
1701 ASSERT_VALID_EPHEMERAL;
1702 if (JS::ContextOptionsRef(mJSCx).privateIsNSISupports())
1703 {
1704 *_rval = static_cast<nsISupports*>(JS_GetContextPrivate(mJSCx));
1705 NS_IF_ADDREF(*_rval);
1706 }
1707 else
1708 {
1709 *_rval = nullptr;
1710 }
1711
1712 return NS_OK;
1713 }
1714
1715 NS_IMETHODIMP
1716 jsdContext::GetWrappedContext(nsISupports **_rval)
1717 {
1718 ASSERT_VALID_EPHEMERAL;
1719 NS_IF_ADDREF(*_rval = mISCx);
1720 return NS_OK;
1721 }
1722
1723 NS_IMETHODIMP
1724 jsdContext::GetTag(uint32_t *_rval)
1725 {
1726 ASSERT_VALID_EPHEMERAL;
1727 if (!mTag)
1728 mTag = ++jsdContext::LastTag;
1729
1730 *_rval = mTag;
1731 return NS_OK;
1732 }
1733
1734 NS_IMETHODIMP
1735 jsdContext::GetGlobalObject (jsdIValue **_rval)
1736 {
1737 ASSERT_VALID_EPHEMERAL;
1738 JSObject *glob = GetDefaultScopeFromJSContext(mJSCx);
1739 JSDValue *jsdv = JSD_NewValue (mJSDCx, OBJECT_TO_JSVAL(glob));
1740 if (!jsdv)
1741 return NS_ERROR_FAILURE;
1742 *_rval = jsdValue::FromPtr (mJSDCx, jsdv);
1743 if (!*_rval)
1744 return NS_ERROR_FAILURE;
1745 return NS_OK;
1746 }
1747
1748 NS_IMETHODIMP
1749 jsdContext::GetScriptsEnabled (bool *_rval)
1750 {
1751 ASSERT_VALID_EPHEMERAL;
1752 *_rval = IsScriptEnabled();
1753 return NS_OK;
1754 }
1755
1756 NS_IMETHODIMP
1757 jsdContext::SetScriptsEnabled (bool _rval)
1758 {
1759 ASSERT_VALID_EPHEMERAL;
1760 if (_rval == IsScriptEnabled())
1761 return NS_OK;
1762
1763 nsCOMPtr<nsIScriptContext> scx = do_QueryInterface(mISCx);
1764 NS_ENSURE_TRUE(scx && scx->GetWindowProxy(), NS_ERROR_NO_INTERFACE);
1765 nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(scx->GetGlobalObject());
1766 NS_ENSURE_TRUE(piWin, NS_ERROR_NO_INTERFACE);
1767 uint64_t currentWindowID = piWin->WindowID();
1768
1769 if (_rval) {
1770 if (mScriptDisabledForWindowWithID != currentWindowID) {
1771 NS_WARNING("Please stop abusing JSD and fix your code!");
1772 return NS_ERROR_UNEXPECTED;
1773 }
1774 xpc::Scriptability::Get(scx->GetWindowProxy()).Unblock();
1775 piWin->ResumeTimeouts();
1776 mScriptDisabledForWindowWithID = 0;
1777 }
1778 else {
1779 piWin->SuspendTimeouts();
1780 xpc::Scriptability::Get(scx->GetWindowProxy()).Block();
1781 mScriptDisabledForWindowWithID = currentWindowID;
1782 }
1783
1784 return NS_OK;
1785 }
1786
1787 /* Stack Frames */
1788 NS_IMPL_ISUPPORTS(jsdStackFrame, jsdIStackFrame, jsdIEphemeral)
1789
1790 jsdStackFrame::jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState,
1791 JSDStackFrameInfo *aStackFrameInfo) :
1792 mCx(aCx), mThreadState(aThreadState), mStackFrameInfo(aStackFrameInfo)
1793 {
1794 DEBUG_CREATE ("jsdStackFrame", gFrameCount);
1795 mValid = (aCx && aThreadState && aStackFrameInfo);
1796 if (mValid) {
1797 mLiveListEntry.key = aStackFrameInfo;
1798 mLiveListEntry.value = this;
1799 jsds_InsertEphemeral (&gLiveStackFrames, &mLiveListEntry);
1800 }
1801 }
1802
1803 jsdStackFrame::~jsdStackFrame()
1804 {
1805 DEBUG_DESTROY ("jsdStackFrame", gFrameCount);
1806 if (mValid)
1807 {
1808 /* call Invalidate() to take ourselves out of the live list */
1809 Invalidate();
1810 }
1811 }
1812
1813 jsdIStackFrame *
1814 jsdStackFrame::FromPtr (JSDContext *aCx, JSDThreadState *aThreadState,
1815 JSDStackFrameInfo *aStackFrameInfo)
1816 {
1817 if (!aStackFrameInfo)
1818 return nullptr;
1819
1820 jsdIStackFrame *rv;
1821 nsCOMPtr<jsdIStackFrame> frame;
1822
1823 nsCOMPtr<jsdIEphemeral> eph =
1824 jsds_FindEphemeral (&gLiveStackFrames,
1825 reinterpret_cast<void *>(aStackFrameInfo));
1826
1827 if (eph)
1828 {
1829 frame = do_QueryInterface(eph);
1830 rv = frame;
1831 }
1832 else
1833 {
1834 rv = new jsdStackFrame (aCx, aThreadState, aStackFrameInfo);
1835 }
1836
1837 NS_IF_ADDREF(rv);
1838 return rv;
1839 }
1840
1841 NS_IMETHODIMP
1842 jsdStackFrame::Invalidate()
1843 {
1844 ASSERT_VALID_EPHEMERAL;
1845 mValid = false;
1846 jsds_RemoveEphemeral (&gLiveStackFrames, &mLiveListEntry);
1847 return NS_OK;
1848 }
1849
1850 void
1851 jsdStackFrame::InvalidateAll()
1852 {
1853 if (gLiveStackFrames)
1854 jsds_InvalidateAllEphemerals (&gLiveStackFrames);
1855 }
1856
1857 NS_IMETHODIMP
1858 jsdStackFrame::GetJSDContext(JSDContext **_rval)
1859 {
1860 ASSERT_VALID_EPHEMERAL;
1861 *_rval = mCx;
1862 return NS_OK;
1863 }
1864
1865 NS_IMETHODIMP
1866 jsdStackFrame::GetJSDThreadState(JSDThreadState **_rval)
1867 {
1868 ASSERT_VALID_EPHEMERAL;
1869 *_rval = mThreadState;
1870 return NS_OK;
1871 }
1872
1873 NS_IMETHODIMP
1874 jsdStackFrame::GetJSDStackFrameInfo(JSDStackFrameInfo **_rval)
1875 {
1876 ASSERT_VALID_EPHEMERAL;
1877 *_rval = mStackFrameInfo;
1878 return NS_OK;
1879 }
1880
1881 NS_IMETHODIMP
1882 jsdStackFrame::GetIsValid(bool *_rval)
1883 {
1884 *_rval = mValid;
1885 return NS_OK;
1886 }
1887
1888 NS_IMETHODIMP
1889 jsdStackFrame::GetCallingFrame(jsdIStackFrame **_rval)
1890 {
1891 ASSERT_VALID_EPHEMERAL;
1892 JSDStackFrameInfo *sfi = JSD_GetCallingStackFrame (mCx, mThreadState,
1893 mStackFrameInfo);
1894 *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
1895 return NS_OK;
1896 }
1897
1898 NS_IMETHODIMP
1899 jsdStackFrame::GetExecutionContext(jsdIContext **_rval)
1900 {
1901 ASSERT_VALID_EPHEMERAL;
1902 JSContext *cx = JSD_GetJSContext (mCx, mThreadState);
1903 *_rval = jsdContext::FromPtr (mCx, cx);
1904 return NS_OK;
1905 }
1906
1907 NS_IMETHODIMP
1908 jsdStackFrame::GetFunctionName(nsACString &_rval)
1909 {
1910 ASSERT_VALID_EPHEMERAL;
1911 JSString *str = JSD_GetIdForStackFrame(mCx, mThreadState, mStackFrameInfo);
1912 if (str)
1913 return AssignToJSString(mCx, &_rval, str);
1914
1915 _rval.Assign("anonymous");
1916 return NS_OK;
1917 }
1918
1919 NS_IMETHODIMP
1920 jsdStackFrame::GetIsDebugger(bool *_rval)
1921 {
1922 ASSERT_VALID_EPHEMERAL;
1923 *_rval = JSD_IsStackFrameDebugger (mCx, mThreadState, mStackFrameInfo);
1924 return NS_OK;
1925 }
1926
1927 NS_IMETHODIMP
1928 jsdStackFrame::GetIsConstructing(bool *_rval)
1929 {
1930 ASSERT_VALID_EPHEMERAL;
1931 *_rval = JSD_IsStackFrameConstructing (mCx, mThreadState, mStackFrameInfo);
1932 return NS_OK;
1933 }
1934
1935 NS_IMETHODIMP
1936 jsdStackFrame::GetScript(jsdIScript **_rval)
1937 {
1938 ASSERT_VALID_EPHEMERAL;
1939 JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1940 mStackFrameInfo);
1941 *_rval = jsdScript::FromPtr (mCx, script);
1942 return NS_OK;
1943 }
1944
1945 NS_IMETHODIMP
1946 jsdStackFrame::GetPc(uint32_t *_rval)
1947 {
1948 ASSERT_VALID_EPHEMERAL;
1949 JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1950 mStackFrameInfo);
1951 if (!script)
1952 return NS_ERROR_FAILURE;
1953 uintptr_t pcbase = JSD_GetClosestPC(mCx, script, 0);
1954
1955 uintptr_t pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
1956 if (pc)
1957 *_rval = pc - pcbase;
1958 else
1959 *_rval = pcbase;
1960 return NS_OK;
1961 }
1962
1963 NS_IMETHODIMP
1964 jsdStackFrame::GetLine(uint32_t *_rval)
1965 {
1966 ASSERT_VALID_EPHEMERAL;
1967 JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState,
1968 mStackFrameInfo);
1969 if (script) {
1970 uintptr_t pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo);
1971 *_rval = JSD_GetClosestLine (mCx, script, pc);
1972 } else {
1973 return NS_ERROR_FAILURE;
1974 }
1975 return NS_OK;
1976 }
1977
1978 NS_IMETHODIMP
1979 jsdStackFrame::GetCallee(jsdIValue **_rval)
1980 {
1981 ASSERT_VALID_EPHEMERAL;
1982 JSDValue *jsdv = JSD_GetCallObjectForStackFrame (mCx, mThreadState,
1983 mStackFrameInfo);
1984
1985 *_rval = jsdValue::FromPtr (mCx, jsdv);
1986 return NS_OK;
1987 }
1988
1989 NS_IMETHODIMP
1990 jsdStackFrame::GetScope(jsdIValue **_rval)
1991 {
1992 ASSERT_VALID_EPHEMERAL;
1993 JSDValue *jsdv = JSD_GetScopeChainForStackFrame (mCx, mThreadState,
1994 mStackFrameInfo);
1995
1996 *_rval = jsdValue::FromPtr (mCx, jsdv);
1997 return NS_OK;
1998 }
1999
2000 NS_IMETHODIMP
2001 jsdStackFrame::GetThisValue(jsdIValue **_rval)
2002 {
2003 ASSERT_VALID_EPHEMERAL;
2004 JSDValue *jsdv = JSD_GetThisForStackFrame (mCx, mThreadState,
2005 mStackFrameInfo);
2006
2007 *_rval = jsdValue::FromPtr (mCx, jsdv);
2008 return NS_OK;
2009 }
2010
2011
2012 NS_IMETHODIMP
2013 jsdStackFrame::Eval (const nsAString &bytes, const nsACString &fileName,
2014 uint32_t line, jsdIValue **result, bool *_rval)
2015 {
2016 ASSERT_VALID_EPHEMERAL;
2017
2018 if (bytes.IsEmpty())
2019 return NS_ERROR_INVALID_ARG;
2020
2021 // get pointer to buffer contained in |bytes|
2022 nsAString::const_iterator h;
2023 bytes.BeginReading(h);
2024 const jschar *char_bytes = reinterpret_cast<const jschar *>(h.get());
2025
2026 JSExceptionState *estate = 0;
2027
2028 AutoPushJSContext cx(JSD_GetJSContext (mCx, mThreadState));
2029
2030 JS::RootedValue jv(cx);
2031
2032 estate = JS_SaveExceptionState (cx);
2033 JS_ClearPendingException (cx);
2034
2035 *_rval = JSD_AttemptUCScriptInStackFrame (mCx, mThreadState,
2036 mStackFrameInfo,
2037 char_bytes, bytes.Length(),
2038 PromiseFlatCString(fileName).get(),
2039 line, &jv);
2040 if (!*_rval) {
2041 if (JS_IsExceptionPending(cx))
2042 JS_GetPendingException (cx, &jv);
2043 else
2044 jv = JSVAL_NULL;
2045 }
2046
2047 JS_RestoreExceptionState (cx, estate);
2048
2049 JSDValue *jsdv = JSD_NewValue (mCx, jv);
2050 if (!jsdv)
2051 return NS_ERROR_FAILURE;
2052 *result = jsdValue::FromPtr (mCx, jsdv);
2053 if (!*result)
2054 return NS_ERROR_FAILURE;
2055
2056 return NS_OK;
2057 }
2058
2059 /* Values */
2060 NS_IMPL_ISUPPORTS(jsdValue, jsdIValue, jsdIEphemeral)
2061 jsdIValue *
2062 jsdValue::FromPtr (JSDContext *aCx, JSDValue *aValue)
2063 {
2064 /* value will be dropped by te jsdValue destructor. */
2065
2066 if (!aValue)
2067 return nullptr;
2068
2069 jsdIValue *rv = new jsdValue (aCx, aValue);
2070 NS_IF_ADDREF(rv);
2071 return rv;
2072 }
2073
2074 jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(true),
2075 mCx(aCx),
2076 mValue(aValue)
2077 {
2078 DEBUG_CREATE ("jsdValue", gValueCount);
2079 mLiveListEntry.value = this;
2080 jsds_InsertEphemeral (&gLiveValues, &mLiveListEntry);
2081 }
2082
2083 jsdValue::~jsdValue()
2084 {
2085 DEBUG_DESTROY ("jsdValue", gValueCount);
2086 if (mValid)
2087 /* call Invalidate() to take ourselves out of the live list */
2088 Invalidate();
2089 }
2090
2091 NS_IMETHODIMP
2092 jsdValue::GetIsValid(bool *_rval)
2093 {
2094 *_rval = mValid;
2095 return NS_OK;
2096 }
2097
2098 NS_IMETHODIMP
2099 jsdValue::Invalidate()
2100 {
2101 ASSERT_VALID_EPHEMERAL;
2102 mValid = false;
2103 jsds_RemoveEphemeral (&gLiveValues, &mLiveListEntry);
2104 JSD_DropValue (mCx, mValue);
2105 return NS_OK;
2106 }
2107
2108 void
2109 jsdValue::InvalidateAll()
2110 {
2111 if (gLiveValues)
2112 jsds_InvalidateAllEphemerals (&gLiveValues);
2113 }
2114
2115 NS_IMETHODIMP
2116 jsdValue::GetJSDContext(JSDContext **_rval)
2117 {
2118 ASSERT_VALID_EPHEMERAL;
2119 *_rval = mCx;
2120 return NS_OK;
2121 }
2122
2123 NS_IMETHODIMP
2124 jsdValue::GetJSDValue (JSDValue **_rval)
2125 {
2126 ASSERT_VALID_EPHEMERAL;
2127 *_rval = mValue;
2128 return NS_OK;
2129 }
2130
2131 NS_IMETHODIMP
2132 jsdValue::GetIsNative (bool *_rval)
2133 {
2134 ASSERT_VALID_EPHEMERAL;
2135 *_rval = JSD_IsValueNative (mCx, mValue);
2136 return NS_OK;
2137 }
2138
2139 NS_IMETHODIMP
2140 jsdValue::GetIsNumber (bool *_rval)
2141 {
2142 ASSERT_VALID_EPHEMERAL;
2143 *_rval = JSD_IsValueNumber (mCx, mValue);
2144 return NS_OK;
2145 }
2146
2147 NS_IMETHODIMP
2148 jsdValue::GetIsPrimitive (bool *_rval)
2149 {
2150 ASSERT_VALID_EPHEMERAL;
2151 *_rval = JSD_IsValuePrimitive (mCx, mValue);
2152 return NS_OK;
2153 }
2154
2155 NS_IMETHODIMP
2156 jsdValue::GetJsType (uint32_t *_rval)
2157 {
2158 ASSERT_VALID_EPHEMERAL;
2159 JS::RootedValue val(JSD_GetJSRuntime(mCx), JSD_GetValueWrappedJSVal (mCx, mValue));
2160
2161 if (JSVAL_IS_NULL(val))
2162 *_rval = TYPE_NULL;
2163 else if (JSVAL_IS_BOOLEAN(val))
2164 *_rval = TYPE_BOOLEAN;
2165 else if (JSVAL_IS_DOUBLE(val))
2166 *_rval = TYPE_DOUBLE;
2167 else if (JSVAL_IS_INT(val))
2168 *_rval = TYPE_INT;
2169 else if (JSVAL_IS_STRING(val))
2170 *_rval = TYPE_STRING;
2171 else if (JSVAL_IS_VOID(val))
2172 *_rval = TYPE_VOID;
2173 else if (JSD_IsValueFunction (mCx, mValue))
2174 *_rval = TYPE_FUNCTION;
2175 else if (!JSVAL_IS_PRIMITIVE(val))
2176 *_rval = TYPE_OBJECT;
2177 else
2178 NS_ASSERTION (0, "Value has no discernible type.");
2179
2180 return NS_OK;
2181 }
2182
2183 NS_IMETHODIMP
2184 jsdValue::GetJsPrototype (jsdIValue **_rval)
2185 {
2186 ASSERT_VALID_EPHEMERAL;
2187 JSDValue *jsdv = JSD_GetValuePrototype (mCx, mValue);
2188 *_rval = jsdValue::FromPtr (mCx, jsdv);
2189 return NS_OK;
2190 }
2191
2192 NS_IMETHODIMP
2193 jsdValue::GetJsParent (jsdIValue **_rval)
2194 {
2195 ASSERT_VALID_EPHEMERAL;
2196 JSDValue *jsdv = JSD_GetValueParent (mCx, mValue);
2197 *_rval = jsdValue::FromPtr (mCx, jsdv);
2198 return NS_OK;
2199 }
2200
2201 NS_IMETHODIMP
2202 jsdValue::GetJsClassName(nsACString &_rval)
2203 {
2204 ASSERT_VALID_EPHEMERAL;
2205 _rval.Assign(JSD_GetValueClassName(mCx, mValue));
2206
2207 return NS_OK;
2208 }
2209
2210 NS_IMETHODIMP
2211 jsdValue::GetJsConstructor (jsdIValue **_rval)
2212 {
2213 ASSERT_VALID_EPHEMERAL;
2214 JSDValue *jsdv = JSD_GetValueConstructor (mCx, mValue);
2215 *_rval = jsdValue::FromPtr (mCx, jsdv);
2216 return NS_OK;
2217 }
2218
2219 NS_IMETHODIMP
2220 jsdValue::GetJsFunctionName(nsACString &_rval)
2221 {
2222 ASSERT_VALID_EPHEMERAL;
2223 return AssignToJSString(mCx, &_rval, JSD_GetValueFunctionId(mCx, mValue));
2224 }
2225
2226 NS_IMETHODIMP
2227 jsdValue::GetBooleanValue(bool *_rval)
2228 {
2229 ASSERT_VALID_EPHEMERAL;
2230 *_rval = JSD_GetValueBoolean (mCx, mValue);
2231 return NS_OK;
2232 }
2233
2234 NS_IMETHODIMP
2235 jsdValue::GetDoubleValue(double *_rval)
2236 {
2237 ASSERT_VALID_EPHEMERAL;
2238 *_rval = JSD_GetValueDouble (mCx, mValue);
2239 return NS_OK;
2240 }
2241
2242 NS_IMETHODIMP
2243 jsdValue::GetIntValue(int32_t *_rval)
2244 {
2245 ASSERT_VALID_EPHEMERAL;
2246 *_rval = JSD_GetValueInt (mCx, mValue);
2247 return NS_OK;
2248 }
2249
2250 NS_IMETHODIMP
2251 jsdValue::GetObjectValue(jsdIObject **_rval)
2252 {
2253 ASSERT_VALID_EPHEMERAL;
2254 JSDObject *obj;
2255 obj = JSD_GetObjectForValue (mCx, mValue);
2256 *_rval = jsdObject::FromPtr (mCx, obj);
2257 if (!*_rval)
2258 return NS_ERROR_FAILURE;
2259 return NS_OK;
2260 }
2261
2262 NS_IMETHODIMP
2263 jsdValue::GetStringValue(nsACString &_rval)
2264 {
2265 ASSERT_VALID_EPHEMERAL;
2266 AutoSafeJSContext cx;
2267 JSString *jstr_val = JSD_GetValueString(mCx, mValue);
2268 if (jstr_val) {
2269 size_t length;
2270 const jschar *chars = JS_GetStringCharsZAndLength(cx, jstr_val, &length);
2271 if (!chars)
2272 return NS_ERROR_FAILURE;
2273 nsDependentString depStr(chars, length);
2274 CopyUTF16toUTF8(depStr, _rval);
2275 } else {
2276 _rval.Truncate();
2277 }
2278 return NS_OK;
2279 }
2280
2281 NS_IMETHODIMP
2282 jsdValue::GetPropertyCount (int32_t *_rval)
2283 {
2284 ASSERT_VALID_EPHEMERAL;
2285 if (JSD_IsValueObject(mCx, mValue))
2286 *_rval = JSD_GetCountOfProperties (mCx, mValue);
2287 else
2288 *_rval = -1;
2289 return NS_OK;
2290 }
2291
2292 NS_IMETHODIMP
2293 jsdValue::GetProperties (jsdIProperty ***propArray, uint32_t *length)
2294 {
2295 ASSERT_VALID_EPHEMERAL;
2296 *propArray = nullptr;
2297 if (length)
2298 *length = 0;
2299
2300 uint32_t prop_count = JSD_IsValueObject(mCx, mValue)
2301 ? JSD_GetCountOfProperties (mCx, mValue)
2302 : 0;
2303 NS_ENSURE_TRUE(prop_count, NS_OK);
2304
2305 jsdIProperty **pa_temp =
2306 static_cast<jsdIProperty **>
2307 (nsMemory::Alloc(sizeof (jsdIProperty *) *
2308 prop_count));
2309 NS_ENSURE_TRUE(pa_temp, NS_ERROR_OUT_OF_MEMORY);
2310
2311 uint32_t i = 0;
2312 JSDProperty *iter = nullptr;
2313 JSDProperty *prop;
2314 while ((prop = JSD_IterateProperties (mCx, mValue, &iter))) {
2315 pa_temp[i] = jsdProperty::FromPtr (mCx, prop);
2316 ++i;
2317 }
2318
2319 NS_ASSERTION (prop_count == i, "property count mismatch");
2320
2321 /* if caller doesn't care about length, don't bother telling them */
2322 *propArray = pa_temp;
2323 if (length)
2324 *length = prop_count;
2325
2326 return NS_OK;
2327 }
2328
2329 NS_IMETHODIMP
2330 jsdValue::GetProperty (const nsACString &name, jsdIProperty **_rval)
2331 {
2332 ASSERT_VALID_EPHEMERAL;
2333 AutoSafeJSContext cx;
2334 JSAutoCompartment ac(cx, JSD_GetDefaultGlobal (mCx)); // Just in case.
2335
2336 /* not rooting this */
2337 JSString *jstr_name = JS_NewStringCopyZ(cx, PromiseFlatCString(name).get());
2338 if (!jstr_name)
2339 return NS_ERROR_OUT_OF_MEMORY;
2340
2341 JSDProperty *prop = JSD_GetValueProperty (mCx, mValue, jstr_name);
2342
2343 *_rval = jsdProperty::FromPtr (mCx, prop);
2344 return NS_OK;
2345 }
2346
2347 NS_IMETHODIMP
2348 jsdValue::Refresh()
2349 {
2350 ASSERT_VALID_EPHEMERAL;
2351 JSD_RefreshValue (mCx, mValue);
2352 return NS_OK;
2353 }
2354
2355 NS_IMETHODIMP
2356 jsdValue::GetWrappedValue(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval)
2357 {
2358 ASSERT_VALID_EPHEMERAL;
2359
2360 aRetval.set(JSD_GetValueWrappedJSVal(mCx, mValue));
2361 if (!JS_WrapValue(aCx, aRetval))
2362 return NS_ERROR_FAILURE;
2363
2364 return NS_OK;
2365 }
2366
2367 NS_IMETHODIMP
2368 jsdValue::GetScript(jsdIScript **_rval)
2369 {
2370 ASSERT_VALID_EPHEMERAL;
2371 JSDScript *script = JSD_GetScriptForValue(mCx, mValue);
2372 *_rval = jsdScript::FromPtr(mCx, script);
2373 return NS_OK;
2374 }
2375
2376 /******************************************************************************
2377 * debugger service implementation
2378 ******************************************************************************/
2379
2380 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(jsdService)
2381 NS_INTERFACE_MAP_ENTRY(jsdIDebuggerService)
2382 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, jsdIDebuggerService)
2383 NS_INTERFACE_MAP_END
2384
2385 NS_IMPL_CYCLE_COLLECTION(jsdService,
2386 mErrorHook, mBreakpointHook, mDebugHook,
2387 mDebuggerHook, mInterruptHook, mScriptHook,
2388 mThrowHook, mTopLevelHook, mFunctionHook,
2389 mActivationCallback)
2390 NS_IMPL_CYCLE_COLLECTING_ADDREF(jsdService)
2391 NS_IMPL_CYCLE_COLLECTING_RELEASE(jsdService)
2392
2393 NS_IMETHODIMP
2394 jsdService::GetJSDContext(JSDContext **_rval)
2395 {
2396 *_rval = mCx;
2397 return NS_OK;
2398 }
2399
2400 NS_IMETHODIMP
2401 jsdService::GetFlags (uint32_t *_rval)
2402 {
2403 ASSERT_VALID_CONTEXT;
2404 *_rval = JSD_GetContextFlags (mCx);
2405 return NS_OK;
2406 }
2407
2408 NS_IMETHODIMP
2409 jsdService::SetFlags (uint32_t flags)
2410 {
2411 ASSERT_VALID_CONTEXT;
2412 JSD_SetContextFlags (mCx, flags);
2413 return NS_OK;
2414 }
2415
2416 NS_IMETHODIMP
2417 jsdService::GetImplementationString(nsACString &aImplementationString)
2418 {
2419 aImplementationString.AssignLiteral(implementationString);
2420 return NS_OK;
2421 }
2422
2423 NS_IMETHODIMP
2424 jsdService::GetImplementationMajor(uint32_t *_rval)
2425 {
2426 *_rval = JSDS_MAJOR_VERSION;
2427 return NS_OK;
2428 }
2429
2430 NS_IMETHODIMP
2431 jsdService::GetImplementationMinor(uint32_t *_rval)
2432 {
2433 *_rval = JSDS_MINOR_VERSION;
2434 return NS_OK;
2435 }
2436
2437 NS_IMETHODIMP
2438 jsdService::GetIsOn (bool *_rval)
2439 {
2440 *_rval = mOn;
2441 return NS_OK;
2442 }
2443
2444 NS_IMETHODIMP
2445 jsdService::On (void)
2446 {
2447 return NS_ERROR_NOT_IMPLEMENTED;
2448 }
2449
2450 NS_IMETHODIMP
2451 jsdService::AsyncOn (jsdIActivationCallback *activationCallback)
2452 {
2453 nsresult rv;
2454
2455 // Warn that JSD is deprecated, unless the caller has told us
2456 // that they know already.
2457 if (mDeprecationAcknowledged) {
2458 mDeprecationAcknowledged = false;
2459 } else if (!mWarnedAboutDeprecation) {
2460 // In any case, warn only once.
2461 mWarnedAboutDeprecation = true;
2462
2463 // Ignore errors: simply being unable to print the message
2464 // shouldn't (effectively) disable JSD.
2465 nsContentUtils::ReportToConsoleNonLocalized(
2466 NS_LITERAL_STRING("\
2467 The jsdIDebuggerService and its associated interfaces are deprecated. \
2468 Please use Debugger, via IJSDebugger, instead."),
2469 nsIScriptError::warningFlag,
2470 NS_LITERAL_CSTRING("JSD"),
2471 nullptr);
2472 }
2473
2474 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2475 if (NS_FAILED(rv)) return rv;
2476
2477 mActivationCallback = activationCallback;
2478
2479 return xpc->SetDebugModeWhenPossible(true, true);
2480 }
2481
2482 NS_IMETHODIMP
2483 jsdService::RecompileForDebugMode (JSContext *cx, JSCompartment *comp, bool mode) {
2484 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
2485 /* XPConnect now does this work itself, so this IDL entry point is no longer used. */
2486 return NS_ERROR_NOT_IMPLEMENTED;
2487 }
2488
2489 NS_IMETHODIMP
2490 jsdService::DeactivateDebugger ()
2491 {
2492 if (!mCx)
2493 return NS_OK;
2494
2495 jsdContext::InvalidateAll();
2496 jsdScript::InvalidateAll();
2497 jsdValue::InvalidateAll();
2498 jsdProperty::InvalidateAll();
2499 jsdStackFrame::InvalidateAll();
2500 ClearAllBreakpoints();
2501
2502 JSD_SetErrorReporter (mCx, nullptr, nullptr);
2503 JSD_SetScriptHook (mCx, nullptr, nullptr);
2504 JSD_ClearThrowHook (mCx);
2505 JSD_ClearInterruptHook (mCx);
2506 JSD_ClearDebuggerHook (mCx);
2507 JSD_ClearDebugBreakHook (mCx);
2508 JSD_ClearTopLevelHook (mCx);
2509 JSD_ClearFunctionHook (mCx);
2510
2511 JSD_DebuggerOff (mCx);
2512
2513 mCx = nullptr;
2514 mRuntime = nullptr;
2515 mOn = false;
2516
2517 return NS_OK;
2518 }
2519
2520
2521 NS_IMETHODIMP
2522 jsdService::ActivateDebugger (JSRuntime *rt)
2523 {
2524 if (mOn)
2525 return (rt == mRuntime) ? NS_OK : NS_ERROR_ALREADY_INITIALIZED;
2526
2527 mRuntime = rt;
2528
2529 if (gPrevGCSliceCallback == jsds_GCSliceCallbackProc)
2530 /* condition indicates that the callback proc has not been set yet */
2531 gPrevGCSliceCallback = JS::SetGCSliceCallback (rt, jsds_GCSliceCallbackProc);
2532
2533 mCx = JSD_DebuggerOnForUser (rt, nullptr, nullptr);
2534 if (!mCx)
2535 return NS_ERROR_FAILURE;
2536
2537 AutoSafeJSContext cx;
2538 JS::RootedObject glob(cx, JSD_GetDefaultGlobal (mCx));
2539 JSAutoCompartment ac(cx, glob);
2540
2541 /* init xpconnect on the debugger's context in case xpconnect tries to
2542 * use it for stuff. */
2543 nsresult rv;
2544 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2545 if (NS_FAILED(rv))
2546 return rv;
2547
2548 xpc->InitClasses (cx, glob);
2549
2550 /* Start watching for script creation/destruction and manage jsdScript
2551 * objects accordingly
2552 */
2553 JSD_SetScriptHook (mCx, jsds_ScriptHookProc, nullptr);
2554
2555 /* If any of these mFooHook objects are installed, do the required JSD
2556 * hookup now. See also, jsdService::SetFooHook().
2557 */
2558 if (mErrorHook)
2559 JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, nullptr);
2560 if (mThrowHook)
2561 JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, nullptr);
2562 /* can't ignore script callbacks, as we need to |Release| the wrapper
2563 * stored in private data when a script is deleted. */
2564 if (mInterruptHook)
2565 JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, nullptr);
2566 if (mDebuggerHook)
2567 JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, nullptr);
2568 if (mDebugHook)
2569 JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, nullptr);
2570 if (mTopLevelHook)
2571 JSD_SetTopLevelHook (mCx, jsds_CallHookProc, nullptr);
2572 else
2573 JSD_ClearTopLevelHook (mCx);
2574 if (mFunctionHook)
2575 JSD_SetFunctionHook (mCx, jsds_CallHookProc, nullptr);
2576 else
2577 JSD_ClearFunctionHook (mCx);
2578 mOn = true;
2579
2580 #ifdef DEBUG
2581 printf ("+++ JavaScript debugging hooks installed.\n");
2582 #endif
2583
2584 nsCOMPtr<jsdIActivationCallback> activationCallback;
2585 mActivationCallback.swap(activationCallback);
2586 if (activationCallback)
2587 return activationCallback->OnDebuggerActivated();
2588
2589 return NS_OK;
2590 }
2591
2592 NS_IMETHODIMP
2593 jsdService::Off (void)
2594 {
2595 if (!mOn)
2596 return NS_OK;
2597
2598 if (!mCx || !mRuntime)
2599 return NS_ERROR_NOT_INITIALIZED;
2600
2601 if (gDeadScripts) {
2602 if (gGCRunning)
2603 return NS_ERROR_NOT_AVAILABLE;
2604
2605 while (gDeadScripts)
2606 jsds_NotifyPendingDeadScripts (JS_GetRuntime(nsContentUtils::GetSafeJSContext()));
2607 }
2608
2609 DeactivateDebugger();
2610
2611 #ifdef DEBUG
2612 printf ("+++ JavaScript debugging hooks removed.\n");
2613 #endif
2614
2615 nsresult rv;
2616 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2617 if (NS_FAILED(rv))
2618 return rv;
2619
2620 xpc->SetDebugModeWhenPossible(false, true);
2621
2622 return NS_OK;
2623 }
2624
2625 NS_IMETHODIMP
2626 jsdService::GetPauseDepth(uint32_t *_rval)
2627 {
2628 NS_ENSURE_ARG_POINTER(_rval);
2629 *_rval = mPauseLevel;
2630 return NS_OK;
2631 }
2632
2633 NS_IMETHODIMP
2634 jsdService::Pause(uint32_t *_rval)
2635 {
2636 return DoPause(_rval, false);
2637 }
2638
2639 nsresult
2640 jsdService::DoPause(uint32_t *_rval, bool internalCall)
2641 {
2642 if (!mCx)
2643 return NS_ERROR_NOT_INITIALIZED;
2644
2645 if (++mPauseLevel == 1) {
2646 JSD_SetErrorReporter (mCx, nullptr, nullptr);
2647 JSD_ClearThrowHook (mCx);
2648 JSD_ClearInterruptHook (mCx);
2649 JSD_ClearDebuggerHook (mCx);
2650 JSD_ClearDebugBreakHook (mCx);
2651 JSD_ClearTopLevelHook (mCx);
2652 JSD_ClearFunctionHook (mCx);
2653 JSD_DebuggerPause (mCx);
2654
2655 nsresult rv;
2656 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2657 if (NS_FAILED(rv)) return rv;
2658
2659 if (!internalCall) {
2660 rv = xpc->SetDebugModeWhenPossible(false, false);
2661 NS_ENSURE_SUCCESS(rv, rv);
2662 }
2663 }
2664
2665 if (_rval)
2666 *_rval = mPauseLevel;
2667
2668 return NS_OK;
2669 }
2670
2671 NS_IMETHODIMP
2672 jsdService::UnPause(uint32_t *_rval)
2673 {
2674 return DoUnPause(_rval, false);
2675 }
2676
2677 nsresult
2678 jsdService::DoUnPause(uint32_t *_rval, bool internalCall)
2679 {
2680 if (!mCx)
2681 return NS_ERROR_NOT_INITIALIZED;
2682
2683 if (mPauseLevel == 0)
2684 return NS_ERROR_NOT_AVAILABLE;
2685
2686 /* check mOn before we muck with this stuff, it's possible the debugger
2687 * was turned off while we were paused.
2688 */
2689 if (--mPauseLevel == 0 && mOn) {
2690 JSD_DebuggerUnpause (mCx);
2691 if (mErrorHook)
2692 JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, nullptr);
2693 if (mThrowHook)
2694 JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, nullptr);
2695 if (mInterruptHook)
2696 JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, nullptr);
2697 if (mDebuggerHook)
2698 JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, nullptr);
2699 if (mDebugHook)
2700 JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, nullptr);
2701 if (mTopLevelHook)
2702 JSD_SetTopLevelHook (mCx, jsds_CallHookProc, nullptr);
2703 else
2704 JSD_ClearTopLevelHook (mCx);
2705 if (mFunctionHook)
2706 JSD_SetFunctionHook (mCx, jsds_CallHookProc, nullptr);
2707 else
2708 JSD_ClearFunctionHook (mCx);
2709
2710 nsresult rv;
2711 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2712 if (NS_FAILED(rv)) return rv;
2713
2714 if (!internalCall) {
2715 rv = xpc->SetDebugModeWhenPossible(true, false);
2716 NS_ENSURE_SUCCESS(rv, rv);
2717 }
2718 }
2719
2720 if (_rval)
2721 *_rval = mPauseLevel;
2722
2723 return NS_OK;
2724 }
2725
2726 NS_IMETHODIMP
2727 jsdService::EnumerateContexts (jsdIContextEnumerator *enumerator)
2728 {
2729 ASSERT_VALID_CONTEXT;
2730
2731 if (!enumerator)
2732 return NS_OK;
2733
2734 JSContext *iter = nullptr;
2735 JSContext *cx;
2736
2737 while ((cx = JS_ContextIterator (mRuntime, &iter)))
2738 {
2739 nsCOMPtr<jsdIContext> jsdicx =
2740 dont_AddRef(jsdContext::FromPtr(mCx, cx));
2741 if (jsdicx)
2742 {
2743 if (NS_FAILED(enumerator->EnumerateContext(jsdicx)))
2744 break;
2745 }
2746 }
2747
2748 return NS_OK;
2749 }
2750
2751 NS_IMETHODIMP
2752 jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
2753 {
2754 ASSERT_VALID_CONTEXT;
2755
2756 JSDScript *script;
2757 JSDScript *iter = nullptr;
2758 nsresult rv = NS_OK;
2759
2760 JSD_LockScriptSubsystem(mCx);
2761 while((script = JSD_IterateScripts(mCx, &iter))) {
2762 nsCOMPtr<jsdIScript> jsdis =
2763 dont_AddRef(jsdScript::FromPtr(mCx, script));
2764 rv = enumerator->EnumerateScript (jsdis);
2765 if (NS_FAILED(rv))
2766 break;
2767 }
2768 JSD_UnlockScriptSubsystem(mCx);
2769
2770 return rv;
2771 }
2772
2773 NS_IMETHODIMP
2774 jsdService::GC (void)
2775 {
2776 ASSERT_VALID_CONTEXT;
2777 JSRuntime *rt = JSD_GetJSRuntime (mCx);
2778 JS_GC(rt);
2779 return NS_OK;
2780 }
2781
2782 NS_IMETHODIMP
2783 jsdService::DumpHeap(const nsACString &fileName)
2784 {
2785 ASSERT_VALID_CONTEXT;
2786 #ifndef DEBUG
2787 return NS_ERROR_NOT_IMPLEMENTED;
2788 #else
2789 nsresult rv = NS_OK;
2790 FILE *file = !fileName.IsEmpty() ? fopen(PromiseFlatCString(fileName).get(), "w") : stdout;
2791 if (!file) {
2792 rv = NS_ERROR_FAILURE;
2793 } else {
2794 if (!JS_DumpHeap(JS_GetRuntime(nsContentUtils::GetSafeJSContext()),
2795 file, nullptr, JSTRACE_OBJECT, nullptr, (size_t)-1, nullptr))
2796 rv = NS_ERROR_FAILURE;
2797 if (file != stdout)
2798 fclose(file);
2799 }
2800 return rv;
2801 #endif
2802 }
2803
2804 NS_IMETHODIMP
2805 jsdService::ClearProfileData ()
2806 {
2807 ASSERT_VALID_CONTEXT;
2808 JSD_ClearAllProfileData (mCx);
2809 return NS_OK;
2810 }
2811
2812 NS_IMETHODIMP
2813 jsdService::InsertFilter (jsdIFilter *filter, jsdIFilter *after)
2814 {
2815 NS_ENSURE_ARG_POINTER (filter);
2816 if (jsds_FindFilter (filter))
2817 return NS_ERROR_INVALID_ARG;
2818
2819 FilterRecord *rec = PR_NEWZAP (FilterRecord);
2820 if (!rec)
2821 return NS_ERROR_OUT_OF_MEMORY;
2822
2823 if (!jsds_SyncFilter (rec, filter)) {
2824 PR_Free (rec);
2825 return NS_ERROR_FAILURE;
2826 }
2827
2828 if (gFilters) {
2829 if (!after) {
2830 /* insert at head of list */
2831 PR_INSERT_LINK(&rec->links, &gFilters->links);
2832 gFilters = rec;
2833 } else {
2834 /* insert somewhere in the list */
2835 FilterRecord *afterRecord = jsds_FindFilter (after);
2836 if (!afterRecord) {
2837 jsds_FreeFilter(rec);
2838 return NS_ERROR_INVALID_ARG;
2839 }
2840 PR_INSERT_AFTER(&rec->links, &afterRecord->links);
2841 }
2842 } else {
2843 if (after) {
2844 /* user asked to insert into the middle of an empty list, bail. */
2845 jsds_FreeFilter(rec);
2846 return NS_ERROR_NOT_INITIALIZED;
2847 }
2848 PR_INIT_CLIST(&rec->links);
2849 gFilters = rec;
2850 }
2851
2852 return NS_OK;
2853 }
2854
2855 NS_IMETHODIMP
2856 jsdService::AppendFilter (jsdIFilter *filter)
2857 {
2858 NS_ENSURE_ARG_POINTER (filter);
2859 if (jsds_FindFilter (filter))
2860 return NS_ERROR_INVALID_ARG;
2861 FilterRecord *rec = PR_NEWZAP (FilterRecord);
2862
2863 if (!jsds_SyncFilter (rec, filter)) {
2864 PR_Free (rec);
2865 return NS_ERROR_FAILURE;
2866 }
2867
2868 if (gFilters) {
2869 PR_INSERT_BEFORE(&rec->links, &gFilters->links);
2870 } else {
2871 PR_INIT_CLIST(&rec->links);
2872 gFilters = rec;
2873 }
2874
2875 return NS_OK;
2876 }
2877
2878 NS_IMETHODIMP
2879 jsdService::RemoveFilter (jsdIFilter *filter)
2880 {
2881 NS_ENSURE_ARG_POINTER(filter);
2882 FilterRecord *rec = jsds_FindFilter (filter);
2883 if (!rec)
2884 return NS_ERROR_INVALID_ARG;
2885
2886 if (gFilters == rec) {
2887 gFilters = reinterpret_cast<FilterRecord *>
2888 (PR_NEXT_LINK(&rec->links));
2889 /* If we're the only filter left, null out the list head. */
2890 if (gFilters == rec)
2891 gFilters = nullptr;
2892 }
2893
2894
2895 PR_REMOVE_LINK(&rec->links);
2896 jsds_FreeFilter (rec);
2897
2898 return NS_OK;
2899 }
2900
2901 NS_IMETHODIMP
2902 jsdService::SwapFilters (jsdIFilter *filter_a, jsdIFilter *filter_b)
2903 {
2904 NS_ENSURE_ARG_POINTER(filter_a);
2905 NS_ENSURE_ARG_POINTER(filter_b);
2906
2907 FilterRecord *rec_a = jsds_FindFilter (filter_a);
2908 if (!rec_a)
2909 return NS_ERROR_INVALID_ARG;
2910
2911 if (filter_a == filter_b) {
2912 /* just a refresh */
2913 if (!jsds_SyncFilter (rec_a, filter_a))
2914 return NS_ERROR_FAILURE;
2915 return NS_OK;
2916 }
2917
2918 FilterRecord *rec_b = jsds_FindFilter (filter_b);
2919 if (!rec_b) {
2920 /* filter_b is not in the list, replace filter_a with filter_b. */
2921 if (!jsds_SyncFilter (rec_a, filter_b))
2922 return NS_ERROR_FAILURE;
2923 } else {
2924 /* both filters are in the list, swap. */
2925 if (!jsds_SyncFilter (rec_a, filter_b))
2926 return NS_ERROR_FAILURE;
2927 if (!jsds_SyncFilter (rec_b, filter_a))
2928 return NS_ERROR_FAILURE;
2929 }
2930
2931 return NS_OK;
2932 }
2933
2934 NS_IMETHODIMP
2935 jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator)
2936 {
2937 if (!gFilters)
2938 return NS_OK;
2939
2940 FilterRecord *current = gFilters;
2941 do {
2942 jsds_SyncFilter (current, current->filterObject);
2943 /* SyncFilter failure would be bad, but what would we do about it? */
2944 if (enumerator) {
2945 nsresult rv = enumerator->EnumerateFilter (current->filterObject);
2946 if (NS_FAILED(rv))
2947 return rv;
2948 }
2949 current = reinterpret_cast<FilterRecord *>
2950 (PR_NEXT_LINK (&current->links));
2951 } while (current != gFilters);
2952
2953 return NS_OK;
2954 }
2955
2956 NS_IMETHODIMP
2957 jsdService::RefreshFilters ()
2958 {
2959 return EnumerateFilters(nullptr);
2960 }
2961
2962 NS_IMETHODIMP
2963 jsdService::ClearFilters ()
2964 {
2965 if (!gFilters)
2966 return NS_OK;
2967
2968 FilterRecord *current = reinterpret_cast<FilterRecord *>
2969 (PR_NEXT_LINK (&gFilters->links));
2970 do {
2971 FilterRecord *next = reinterpret_cast<FilterRecord *>
2972 (PR_NEXT_LINK (&current->links));
2973 PR_REMOVE_AND_INIT_LINK(&current->links);
2974 jsds_FreeFilter(current);
2975 current = next;
2976 } while (current != gFilters);
2977
2978 jsds_FreeFilter(current);
2979 gFilters = nullptr;
2980
2981 return NS_OK;
2982 }
2983
2984 NS_IMETHODIMP
2985 jsdService::ClearAllBreakpoints (void)
2986 {
2987 ASSERT_VALID_CONTEXT;
2988
2989 JSD_LockScriptSubsystem(mCx);
2990 JSD_ClearAllExecutionHooks (mCx);
2991 JSD_UnlockScriptSubsystem(mCx);
2992 return NS_OK;
2993 }
2994
2995 NS_IMETHODIMP
2996 jsdService::WrapValue(JS::Handle<JS::Value> value, jsdIValue **_rval)
2997 {
2998 ASSERT_VALID_CONTEXT;
2999 JSDValue *jsdv = JSD_NewValue(mCx, value);
3000 if (!jsdv)
3001 return NS_ERROR_FAILURE;
3002
3003 *_rval = jsdValue::FromPtr (mCx, jsdv);
3004 return NS_OK;
3005 }
3006
3007
3008 NS_IMETHODIMP
3009 jsdService::EnterNestedEventLoop (jsdINestCallback *callback, uint32_t *_rval)
3010 {
3011 // Nesting event queues is a thing of the past. Now, we just spin the
3012 // current event loop.
3013 nsresult rv = NS_OK;
3014 AutoNoJSAPI nojsapi;
3015 uint32_t nestLevel = ++mNestedLoopLevel;
3016 nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
3017
3018 if (callback) {
3019 DoPause(nullptr, true);
3020 rv = callback->OnNest();
3021 DoUnPause(nullptr, true);
3022 }
3023
3024 while (NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) {
3025 if (!NS_ProcessNextEvent(thread))
3026 rv = NS_ERROR_UNEXPECTED;
3027 }
3028
3029 NS_ASSERTION (mNestedLoopLevel <= nestLevel,
3030 "nested event didn't unwind properly");
3031 if (mNestedLoopLevel == nestLevel)
3032 --mNestedLoopLevel;
3033
3034 *_rval = mNestedLoopLevel;
3035 return rv;
3036 }
3037
3038 NS_IMETHODIMP
3039 jsdService::ExitNestedEventLoop (uint32_t *_rval)
3040 {
3041 if (mNestedLoopLevel > 0)
3042 --mNestedLoopLevel;
3043 else
3044 return NS_ERROR_FAILURE;
3045
3046 *_rval = mNestedLoopLevel;
3047 return NS_OK;
3048 }
3049
3050 NS_IMETHODIMP
3051 jsdService::AcknowledgeDeprecation()
3052 {
3053 mDeprecationAcknowledged = true;
3054 return NS_OK;
3055 }
3056
3057 /* hook attribute get/set functions */
3058
3059 NS_IMETHODIMP
3060 jsdService::SetErrorHook (jsdIErrorHook *aHook)
3061 {
3062 mErrorHook = aHook;
3063
3064 /* if the debugger isn't initialized, that's all we can do for now. The
3065 * ActivateDebugger() method will do the rest when the coast is clear.
3066 */
3067 if (!mCx || mPauseLevel)
3068 return NS_OK;
3069
3070 if (aHook)
3071 JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, nullptr);
3072 else
3073 JSD_SetErrorReporter (mCx, nullptr, nullptr);
3074
3075 return NS_OK;
3076 }
3077
3078 NS_IMETHODIMP
3079 jsdService::GetErrorHook (jsdIErrorHook **aHook)
3080 {
3081 *aHook = mErrorHook;
3082 NS_IF_ADDREF(*aHook);
3083
3084 return NS_OK;
3085 }
3086
3087 NS_IMETHODIMP
3088 jsdService::SetBreakpointHook (jsdIExecutionHook *aHook)
3089 {
3090 mBreakpointHook = aHook;
3091 return NS_OK;
3092 }
3093
3094 NS_IMETHODIMP
3095 jsdService::GetBreakpointHook (jsdIExecutionHook **aHook)
3096 {
3097 *aHook = mBreakpointHook;
3098 NS_IF_ADDREF(*aHook);
3099
3100 return NS_OK;
3101 }
3102
3103 NS_IMETHODIMP
3104 jsdService::SetDebugHook (jsdIExecutionHook *aHook)
3105 {
3106 mDebugHook = aHook;
3107
3108 /* if the debugger isn't initialized, that's all we can do for now. The
3109 * ActivateDebugger() method will do the rest when the coast is clear.
3110 */
3111 if (!mCx || mPauseLevel)
3112 return NS_OK;
3113
3114 if (aHook)
3115 JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, nullptr);
3116 else
3117 JSD_ClearDebugBreakHook (mCx);
3118
3119 return NS_OK;
3120 }
3121
3122 NS_IMETHODIMP
3123 jsdService::GetDebugHook (jsdIExecutionHook **aHook)
3124 {
3125 *aHook = mDebugHook;
3126 NS_IF_ADDREF(*aHook);
3127
3128 return NS_OK;
3129 }
3130
3131 NS_IMETHODIMP
3132 jsdService::SetDebuggerHook (jsdIExecutionHook *aHook)
3133 {
3134 mDebuggerHook = aHook;
3135
3136 /* if the debugger isn't initialized, that's all we can do for now. The
3137 * ActivateDebugger() method will do the rest when the coast is clear.
3138 */
3139 if (!mCx || mPauseLevel)
3140 return NS_OK;
3141
3142 if (aHook)
3143 JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, nullptr);
3144 else
3145 JSD_ClearDebuggerHook (mCx);
3146
3147 return NS_OK;
3148 }
3149
3150 NS_IMETHODIMP
3151 jsdService::GetDebuggerHook (jsdIExecutionHook **aHook)
3152 {
3153 *aHook = mDebuggerHook;
3154 NS_IF_ADDREF(*aHook);
3155
3156 return NS_OK;
3157 }
3158
3159 NS_IMETHODIMP
3160 jsdService::SetInterruptHook (jsdIExecutionHook *aHook)
3161 {
3162 mInterruptHook = aHook;
3163
3164 /* if the debugger isn't initialized, that's all we can do for now. The
3165 * ActivateDebugger() method will do the rest when the coast is clear.
3166 */
3167 if (!mCx || mPauseLevel)
3168 return NS_OK;
3169
3170 if (aHook)
3171 JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, nullptr);
3172 else
3173 JSD_ClearInterruptHook (mCx);
3174
3175 return NS_OK;
3176 }
3177
3178 NS_IMETHODIMP
3179 jsdService::GetInterruptHook (jsdIExecutionHook **aHook)
3180 {
3181 *aHook = mInterruptHook;
3182 NS_IF_ADDREF(*aHook);
3183
3184 return NS_OK;
3185 }
3186
3187 NS_IMETHODIMP
3188 jsdService::SetScriptHook (jsdIScriptHook *aHook)
3189 {
3190 mScriptHook = aHook;
3191
3192 /* if the debugger isn't initialized, that's all we can do for now. The
3193 * ActivateDebugger() method will do the rest when the coast is clear.
3194 */
3195 if (!mCx || mPauseLevel)
3196 return NS_OK;
3197
3198 if (aHook)
3199 JSD_SetScriptHook (mCx, jsds_ScriptHookProc, nullptr);
3200 /* we can't unset it if !aHook, because we still need to see script
3201 * deletes in order to Release the jsdIScripts held in JSDScript
3202 * private data. */
3203 return NS_OK;
3204 }
3205
3206 NS_IMETHODIMP
3207 jsdService::GetScriptHook (jsdIScriptHook **aHook)
3208 {
3209 *aHook = mScriptHook;
3210 NS_IF_ADDREF(*aHook);
3211
3212 return NS_OK;
3213 }
3214
3215 NS_IMETHODIMP
3216 jsdService::SetThrowHook (jsdIExecutionHook *aHook)
3217 {
3218 mThrowHook = aHook;
3219
3220 /* if the debugger isn't initialized, that's all we can do for now. The
3221 * ActivateDebugger() method will do the rest when the coast is clear.
3222 */
3223 if (!mCx || mPauseLevel)
3224 return NS_OK;
3225
3226 if (aHook)
3227 JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, nullptr);
3228 else
3229 JSD_ClearThrowHook (mCx);
3230
3231 return NS_OK;
3232 }
3233
3234 NS_IMETHODIMP
3235 jsdService::GetThrowHook (jsdIExecutionHook **aHook)
3236 {
3237 *aHook = mThrowHook;
3238 NS_IF_ADDREF(*aHook);
3239
3240 return NS_OK;
3241 }
3242
3243 NS_IMETHODIMP
3244 jsdService::SetTopLevelHook (jsdICallHook *aHook)
3245 {
3246 mTopLevelHook = aHook;
3247
3248 /* if the debugger isn't initialized, that's all we can do for now. The
3249 * ActivateDebugger() method will do the rest when the coast is clear.
3250 */
3251 if (!mCx || mPauseLevel)
3252 return NS_OK;
3253
3254 if (aHook)
3255 JSD_SetTopLevelHook (mCx, jsds_CallHookProc, nullptr);
3256 else
3257 JSD_ClearTopLevelHook (mCx);
3258
3259 return NS_OK;
3260 }
3261
3262 NS_IMETHODIMP
3263 jsdService::GetTopLevelHook (jsdICallHook **aHook)
3264 {
3265 *aHook = mTopLevelHook;
3266 NS_IF_ADDREF(*aHook);
3267
3268 return NS_OK;
3269 }
3270
3271 NS_IMETHODIMP
3272 jsdService::SetFunctionHook (jsdICallHook *aHook)
3273 {
3274 mFunctionHook = aHook;
3275
3276 /* if the debugger isn't initialized, that's all we can do for now. The
3277 * ActivateDebugger() method will do the rest when the coast is clear.
3278 */
3279 if (!mCx || mPauseLevel)
3280 return NS_OK;
3281
3282 if (aHook)
3283 JSD_SetFunctionHook (mCx, jsds_CallHookProc, nullptr);
3284 else
3285 JSD_ClearFunctionHook (mCx);
3286
3287 return NS_OK;
3288 }
3289
3290 NS_IMETHODIMP
3291 jsdService::GetFunctionHook (jsdICallHook **aHook)
3292 {
3293 *aHook = mFunctionHook;
3294 NS_IF_ADDREF(*aHook);
3295
3296 return NS_OK;
3297 }
3298
3299 /* virtual */
3300 jsdService::~jsdService()
3301 {
3302 ClearFilters();
3303 mErrorHook = nullptr;
3304 mBreakpointHook = nullptr;
3305 mDebugHook = nullptr;
3306 mDebuggerHook = nullptr;
3307 mInterruptHook = nullptr;
3308 mScriptHook = nullptr;
3309 mThrowHook = nullptr;
3310 mTopLevelHook = nullptr;
3311 mFunctionHook = nullptr;
3312 Off();
3313 gJsds = nullptr;
3314 }
3315
3316 jsdService *
3317 jsdService::GetService ()
3318 {
3319 if (!gJsds)
3320 gJsds = new jsdService();
3321
3322 NS_IF_ADDREF(gJsds);
3323 return gJsds;
3324 }
3325
3326 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService)
3327
3328 /* app-start observer. turns on the debugger at app-start. this is inserted
3329 * and/or removed from the app-start category by the jsdService::initAtStartup
3330 * property.
3331 */
3332 class jsdASObserver MOZ_FINAL : public nsIObserver
3333 {
3334 public:
3335 NS_DECL_THREADSAFE_ISUPPORTS
3336 NS_DECL_NSIOBSERVER
3337
3338 jsdASObserver () {}
3339 };
3340
3341 NS_IMPL_ISUPPORTS(jsdASObserver, nsIObserver)
3342
3343 NS_IMETHODIMP
3344 jsdASObserver::Observe (nsISupports *aSubject, const char *aTopic,
3345 const char16_t *aData)
3346 {
3347 nsresult rv;
3348
3349 // Hmm. Why is the app-startup observer called multiple times?
3350 //NS_ASSERTION(!gJsds, "app startup observer called twice");
3351 nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
3352 if (NS_FAILED(rv))
3353 return rv;
3354
3355 bool on;
3356 rv = jsds->GetIsOn(&on);
3357 if (NS_FAILED(rv) || on)
3358 return rv;
3359
3360 nsCOMPtr<nsIJSRuntimeService> rts = do_GetService(NS_JSRT_CTRID, &rv);
3361 if (NS_FAILED(rv))
3362 return rv;
3363
3364 JSRuntime *rt;
3365 rts->GetRuntime (&rt);
3366 if (NS_FAILED(rv))
3367 return rv;
3368
3369 rv = jsds->ActivateDebugger(rt);
3370 if (NS_FAILED(rv))
3371 return rv;
3372
3373 return NS_OK;
3374 }
3375
3376 NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver)
3377 NS_DEFINE_NAMED_CID(JSDSERVICE_CID);
3378 NS_DEFINE_NAMED_CID(JSDASO_CID);
3379
3380 static const mozilla::Module::CIDEntry kJSDCIDs[] = {
3381 { &kJSDSERVICE_CID, false, nullptr, jsdServiceConstructor },
3382 { &kJSDASO_CID, false, nullptr, jsdASObserverConstructor },
3383 { nullptr }
3384 };
3385
3386 static const mozilla::Module::ContractIDEntry kJSDContracts[] = {
3387 { jsdServiceCtrID, &kJSDSERVICE_CID },
3388 { jsdARObserverCtrID, &kJSDASO_CID },
3389 { nullptr }
3390 };
3391
3392 static const mozilla::Module kJSDModule = {
3393 mozilla::Module::kVersion,
3394 kJSDCIDs,
3395 kJSDContracts
3396 };
3397
3398 NSMODULE_DEFN(JavaScript_Debugger) = &kJSDModule;
3399
3400 void
3401 global_finalize(JSFreeOp *aFop, JSObject *aObj)
3402 {
3403 nsIScriptObjectPrincipal *sop =
3404 static_cast<nsIScriptObjectPrincipal *>(js::GetObjectPrivate(aObj));
3405 MOZ_ASSERT(sop);
3406 static_cast<SandboxPrivate *>(sop)->ForgetGlobalObject();
3407 NS_IF_RELEASE(sop);
3408 }
3409
3410 JSObject *
3411 CreateJSDGlobal(JSContext *aCx, const JSClass *aClasp)
3412 {
3413 nsresult rv;
3414 nsCOMPtr<nsIPrincipal> nullPrin = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
3415 NS_ENSURE_SUCCESS(rv, nullptr);
3416
3417 JSPrincipals *jsPrin = nsJSPrincipals::get(nullPrin);
3418 JS::RootedObject global(aCx, JS_NewGlobalObject(aCx, aClasp, jsPrin, JS::DontFireOnNewGlobalHook));
3419 NS_ENSURE_TRUE(global, nullptr);
3420
3421 // We have created a new global let's attach a private to it
3422 // that implements nsIGlobalObject.
3423 nsCOMPtr<nsIScriptObjectPrincipal> sbp =
3424 new SandboxPrivate(nullPrin, global);
3425 JS_SetPrivate(global, sbp.forget().take());
3426
3427 JS_FireOnNewGlobalObject(aCx, global);
3428
3429 return global;
3430 }
3431
3432 /********************************************************************************
3433 ********************************************************************************
3434 * graveyard
3435 */
3436
3437 #if 0
3438 /* Thread States */
3439 NS_IMPL_ISUPPORTS(jsdThreadState, jsdIThreadState);
3440
3441 NS_IMETHODIMP
3442 jsdThreadState::GetJSDContext(JSDContext **_rval)
3443 {
3444 *_rval = mCx;
3445 return NS_OK;
3446 }
3447
3448 NS_IMETHODIMP
3449 jsdThreadState::GetJSDThreadState(JSDThreadState **_rval)
3450 {
3451 *_rval = mThreadState;
3452 return NS_OK;
3453 }
3454
3455 NS_IMETHODIMP
3456 jsdThreadState::GetFrameCount (uint32_t *_rval)
3457 {
3458 *_rval = JSD_GetCountOfStackFrames (mCx, mThreadState);
3459 return NS_OK;
3460 }
3461
3462 NS_IMETHODIMP
3463 jsdThreadState::GetTopFrame (jsdIStackFrame **_rval)
3464 {
3465 JSDStackFrameInfo *sfi = JSD_GetStackFrame (mCx, mThreadState);
3466
3467 *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi);
3468 return NS_OK;
3469 }
3470
3471 NS_IMETHODIMP
3472 jsdThreadState::GetPendingException(jsdIValue **_rval)
3473 {
3474 JSDValue *jsdv = JSD_GetException (mCx, mThreadState);
3475
3476 *_rval = jsdValue::FromPtr (mCx, jsdv);
3477 return NS_OK;
3478 }
3479
3480 NS_IMETHODIMP
3481 jsdThreadState::SetPendingException(jsdIValue *aException)
3482 {
3483 JSDValue *jsdv;
3484
3485 nsresult rv = aException->GetJSDValue (&jsdv);
3486 if (NS_FAILED(rv))
3487 return NS_ERROR_FAILURE;
3488
3489 if (!JSD_SetException (mCx, mThreadState, jsdv))
3490 return NS_ERROR_FAILURE;
3491
3492 return NS_OK;
3493 }
3494
3495 #endif

mercurial