bug 99601, add filter support to js/jsd, r=jband sr=brendan

large addition to the jsd_xpc component allows arbitrary filtering of debug hooks by url pattern, line range, and global object.  also adds ability to begin instrumenting jsscripts at app startup.
This commit is contained in:
rginda%netscape.com 2001-10-30 13:41:32 +00:00
Родитель 0babafcaa0
Коммит db43fadc93
11 изменённых файлов: 952 добавлений и 86 удалений

Просмотреть файл

@ -32,12 +32,17 @@ MODULE = jsdebug
LIBRARY_NAME = jsd LIBRARY_NAME = jsd
# REQUIRES = java js # REQUIRES = java js
REQUIRES = xpcom \ REQUIRES = js
ifndef JSD_STANDALONE
REQUIRES += \
string \ string \
js \ xpcom \
xpconnect \ xpconnect \
widget \ widget \
dom \
$(NULL) $(NULL)
endif
EXTRA_DSO_LDOPTS += \ EXTRA_DSO_LDOPTS += \
$(MOZ_COMPONENT_LIBS) \ $(MOZ_COMPONENT_LIBS) \

Просмотреть файл

@ -53,7 +53,9 @@ native jsuword(jsuword);
/* interfaces we declare in this file */ /* interfaces we declare in this file */
interface jsdIDebuggerService; interface jsdIDebuggerService;
interface jsdIFilter;
interface jsdINestCallback; interface jsdINestCallback;
interface jsdIFilterEnumerator;
interface jsdIScriptEnumerator; interface jsdIScriptEnumerator;
interface jsdIScriptHook; interface jsdIScriptHook;
interface jsdIExecutionHook; interface jsdIExecutionHook;
@ -106,6 +108,12 @@ interface jsdIDebuggerService : nsISupports
*/ */
attribute jsdICallHook functionHook; attribute jsdICallHook functionHook;
/**
* |true| if the debugger should register an app-start observer in order
* to begin collecting debug information when mozilla is launched.
*/
attribute boolean initAtStartup;
/** /**
* |true| if the debugger service has been turned on. This does not * |true| if the debugger service has been turned on. This does not
* necessarily mean another app is actively using the service, as the * necessarily mean another app is actively using the service, as the
@ -134,23 +142,87 @@ interface jsdIDebuggerService : nsISupports
*/ */
void off (); void off ();
/**
* Temporarily disable the debugger. Hooks will not be called while the
* debugger is paused. Multiple calls to pause will increase the "pause
* depth", and equal number of unPause calles must be made to resume
* normal debugging.
*
* @return depth Number of times pause has been called since the debugger
* has been unpaused.
*/
unsigned long pause();
/**
* Undo a pause.
*
* @return depth The number of remaining pending pause calls.
*/
unsigned long unPause();
/** /**
* Force the engine to perform garbage collection. * Force the engine to perform garbage collection.
*/ */
void GC(); void GC();
/** /**
* Ignore/unignore execution hooks which are triggered by a context with a * Adds an execution hook filter. These filters are consulted each time one
* particular global object. * of the jsdIExecutionHooks is about to be called. Filters are matched in
* a first in, first compared fashion. The first filter to match determines
* whether or not the hook is called. Use swapFilter to reorder existing
* filters, and removeFilter to remove them.
* *
* @param glob Global object to ignore/unignore. For the typical Mozilla * If |filter| is already present this method throws NS_ERROR_INVALID_ARG.
* context, global objects are DOM windows.
* *
* @param state |true| to ignore this global object, |false| to unignore. * @param filter Object representing the filter to add.
* If state is |false| and the the glob passed in is not * @param after Insert |filter| after this one. Pass null to insert at
* currently ignored, this method will throw an * the beginning.
* NS_ERROR_INVALID_PARAM exception.
*/ */
//void filterGlobalObject (in nsISupports glob, in boolean state); void insertFilter (in jsdIFilter filter, in jsdIFilter after);
/**
* Same as insertFilter, except always add to the end of the list.
*/
void appendFilter (in jsdIFilter filter);
/**
* Unignore execution hooks which are triggered by a jsdIScript matching
* particular filter.
*
* If |filter| is not present this method throws NS_ERROR_INVALID_ARG.
*
* @param filter Object representing the filter to remove. Must be the exact
* object passed to addFilter, not just a new object with the same
* properties.
*/
void removeFilter (in jsdIFilter filter);
/**
* Swaps the positions of the filters passed in.
*
* If |filter_a| is not present, this method throws NS_ERROR_INVALID_ARG.
* If |filter_b| is not present, filter_a is replaced by filter_b.
* If |filter_a| == |filter_b|, then the glob and urlPattern properties of
* the filter are reread.
*/
void swapFilters (in jsdIFilter filter_a, in jsdIFilter filter_b);
/**
* Enumerate all active filters. This routine refreshes each filter before
* passing them on to the enumeration function. Calling this with a null
* |enumerator| is equivilant to jsdIService::refreshFilters.
*
* @param enumerator jsdIFilterEnumerator instance to be called back for the
* enumeration.
*/
void enumerateFilters (in jsdIFilterEnumerator enumerator);
/**
* Force the debugger to resync its internal filter cache with the
* actual values in the jsdIFilter objects. To refresh a single filter,
* see jsdIService::swapFilters. This method is equivilant to
* jsdIService::enumerateFilters with a null enumerator.
*/
void refreshFilters ();
/**
* Causes the debugger service to clear its list of filters.
*/
void clearFilters();
/** /**
* Enumerate all scripts the debugger knows about. Any scripts created * Enumerate all scripts the debugger knows about. Any scripts created
* before you turned the debugger on, or after turning the debugger off * before you turned the debugger on, or after turning the debugger off
@ -165,16 +237,97 @@ interface jsdIDebuggerService : nsISupports
*/ */
void clearAllBreakpoints (); void clearAllBreakpoints ();
/* XXX these two routines are candidates for refactoring. The only problem
* is that it is not clear where and how they should land.
*/
/** /**
* XXX temporary hacks * Push a new network queue, and enter a new UI event loop.
* These routines are used to nest and unnest UI and network event loops. * @param callback jsdINestCallback instance to be called back after the
* network queue has been pushed, but before the
* UI loop starts.
* @return depth returns the current number of times the event loop has been
* nested. your code can use it for sanity checks.
*/ */
unsigned long enterNestedEventLoop (in jsdINestCallback callback); unsigned long enterNestedEventLoop (in jsdINestCallback callback);
/**
* Exit the current nested event loop after the current iteration completes,
* and pop the network event queue.
*
* @return depth returns the current number of times the event loop has been
* nested. your code can use it for sanity checks.
*/
unsigned long exitNestedEventLoop (); unsigned long exitNestedEventLoop ();
}; };
/* callback interfaces */ /* callback interfaces */
/**
* Object representing a pattern of global object and/or url the debugger should
* ignore. The debugger service itself will not modify properties of these
* objects.
*/
[scriptable, uuid(05593438-1b83-4517-864f-3cea3d37a266)]
interface jsdIFilter : nsISupports
{
/**
* These two bytes of the flags attribute are reserved for interpretation
* by the jsdService implementation. You can do what you like with the
* remaining flags.
*/
const unsigned long FLAG_RESERVED_MASK = 0xFF;
/**
* Filters without this flag set are ignored.
*/
const unsigned long FLAG_ENABLED = 0x01;
/**
* Filters with this flag set are "pass" filters, they allow matching hooks
* to continue. Filters without this flag block matching hooks.
*/
const unsigned long FLAG_PASS = 0x02;
/**
* FLAG_* values from above, OR'd together.
*/
attribute unsigned long flags;
/**
* An nsISupports version of the global object to be filtered. A null glob
* matches all hooks. This attribute must be QI'able to the
* (non-scriptable) nsIScriptGlobalObject interface.
*
* The jsdIService caches this value internally, to if it changes you must
* swap the filter with itself using jsdIService::swapFilters.
*/
attribute nsISupports glob;
/**
* String representing the url pattern to be filtered. Supports limited
* glob matching, at the beginning and end of the pattern only. For example,
* "chrome://venkman*" filters all urls that start with chrome/venkman,
* "*.cgi" filters all cgi's, and "http://myserver/utils.js" filters only
* the utils.js file on "myserver". A null urlPattern matches all urls.
*
* The jsdIService caches this value internally, to if it changes you must
* swap the filter with itself using jsdIService::swapFilters.
*/
attribute string urlPattern;
/**
* Line number for the start of this filter. Line numbers are one based.
* Assigning a 0 to this attribute will tell the debugger to ignore the
* entire file.
*/
attribute unsigned long startLine;
/**
* Line number for the end of this filter. Line numbers are one based.
* Assigning a 0 to this attribute will tell the debugger to ignore from
* |startLine| to the end of the file.
*/
attribute unsigned long endLine;
};
/** /**
* Pass an instance of one of these to jsdIDebuggerService::enterNestedEventLoop. * Pass an instance of one of these to jsdIDebuggerService::enterNestedEventLoop.
*/ */
@ -189,6 +342,19 @@ interface jsdINestCallback : nsISupports
void onNest (); void onNest ();
}; };
/**
* Pass an instance of one of these to jsdIDebuggerService::enumerateFilters.
*/
[scriptable, uuid(54382875-ed12-4f90-9a63-1f0498d0a3f2)]
interface jsdIFilterEnumerator : nsISupports
{
/**
* The enumerateFilter method will be called once for every filter the
* debugger knows about.
*/
void enumerateFilter (in jsdIFilter filter);
};
/** /**
* Pass an instance of one of these to jsdIDebuggerService::enumerateScripts. * Pass an instance of one of these to jsdIDebuggerService::enumerateScripts.
*/ */
@ -199,7 +365,7 @@ interface jsdIScriptEnumerator : nsISupports
* The enumerateScript method will be called once for every script the * The enumerateScript method will be called once for every script the
* debugger knows about. * debugger knows about.
*/ */
boolean enumerateScript (in jsdIScript script); void enumerateScript (in jsdIScript script);
}; };
/** /**
@ -428,6 +594,11 @@ interface jsdIScript : jsdIEphemeral
* Get the first PC associated with a line. * Get the first PC associated with a line.
*/ */
jsdIPC lineToPc (in unsigned long line); jsdIPC lineToPc (in unsigned long line);
/**
* Determine is a particular line is executable, like checking that
* lineToPc == pcToLine, except in one call.
*/
boolean isLineExecutable (in unsigned long line);
/** /**
* Set a breakpoint at a PC in this script. * Set a breakpoint at a PC in this script.
*/ */

Просмотреть файл

@ -586,6 +586,9 @@ jsd_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
extern JSDStackFrameInfo* extern JSDStackFrameInfo*
jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate); jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
extern JSContext*
jsd_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
extern JSDStackFrameInfo* extern JSDStackFrameInfo*
jsd_GetCallingStackFrame(JSDContext* jsdc, jsd_GetCallingStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate, JSDThreadState* jsdthreadstate,

Просмотреть файл

@ -56,6 +56,15 @@ jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rva
if( JSD_IS_DANGEROUS_THREAD(jsdc) ) if( JSD_IS_DANGEROUS_THREAD(jsdc) )
return JSTRAP_CONTINUE; return JSTRAP_CONTINUE;
/* local in case jsdc->interruptHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->interruptHook;
hookData = jsdc->interruptHookData;
JSD_UNLOCK();
if (!hook)
return JSTRAP_CONTINUE;
JSD_LOCK_SCRIPTS(jsdc); JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, script); jsdscript = jsd_FindJSDScript(jsdc, script);
JSD_UNLOCK_SCRIPTS(jsdc); JSD_UNLOCK_SCRIPTS(jsdc);
@ -67,12 +76,6 @@ jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rva
return JSTRAP_CONTINUE; return JSTRAP_CONTINUE;
#endif #endif
/* local in case jsdc->interruptHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->interruptHook;
hookData = jsdc->interruptHookData;
JSD_UNLOCK();
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_INTERRUPTED, return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_INTERRUPTED,
hook, hookData, rval); hook, hookData, rval);
} }
@ -92,17 +95,19 @@ jsd_DebuggerHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
if( JSD_IS_DANGEROUS_THREAD(jsdc) ) if( JSD_IS_DANGEROUS_THREAD(jsdc) )
return JSTRAP_CONTINUE; return JSTRAP_CONTINUE;
JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, script);
JSD_UNLOCK_SCRIPTS(jsdc);
if( ! jsdscript )
return JSTRAP_CONTINUE;
/* local in case jsdc->debuggerHook gets cleared on another thread */ /* local in case jsdc->debuggerHook gets cleared on another thread */
JSD_LOCK(); JSD_LOCK();
hook = jsdc->debuggerHook; hook = jsdc->debuggerHook;
hookData = jsdc->debuggerHookData; hookData = jsdc->debuggerHookData;
JSD_UNLOCK(); JSD_UNLOCK();
if(!hook)
return JSTRAP_CONTINUE;
JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, script);
JSD_UNLOCK_SCRIPTS(jsdc);
if( ! jsdscript )
return JSTRAP_CONTINUE;
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUGGER_KEYWORD, return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUGGER_KEYWORD,
hook, hookData, rval); hook, hookData, rval);
@ -118,25 +123,27 @@ jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
JSD_ExecutionHookProc hook; JSD_ExecutionHookProc hook;
void* hookData; void* hookData;
JS_GetPendingException(cx, rval);
if( ! jsdc || ! jsdc->inited ) if( ! jsdc || ! jsdc->inited )
return JSD_HOOK_RETURN_CONTINUE_THROW; return JSD_HOOK_RETURN_CONTINUE_THROW;
if( JSD_IS_DANGEROUS_THREAD(jsdc) ) if( JSD_IS_DANGEROUS_THREAD(jsdc) )
return JSD_HOOK_RETURN_CONTINUE_THROW; return JSD_HOOK_RETURN_CONTINUE_THROW;
/* local in case jsdc->throwHook gets cleared on another thread */
JSD_LOCK();
hook = jsdc->throwHook;
hookData = jsdc->throwHookData;
JSD_UNLOCK();
if (!hook)
return JSD_HOOK_RETURN_CONTINUE_THROW;
JSD_LOCK_SCRIPTS(jsdc); JSD_LOCK_SCRIPTS(jsdc);
jsdscript = jsd_FindJSDScript(jsdc, script); jsdscript = jsd_FindJSDScript(jsdc, script);
JSD_UNLOCK_SCRIPTS(jsdc); JSD_UNLOCK_SCRIPTS(jsdc);
if( ! jsdscript ) if( ! jsdscript )
return JSD_HOOK_RETURN_CONTINUE_THROW; return JSD_HOOK_RETURN_CONTINUE_THROW;
/* local in case jsdc->throwHook gets cleared on another thread */ JS_GetPendingException(cx, rval);
JSD_LOCK();
hook = jsdc->throwHook;
hookData = jsdc->throwHookData;
JSD_UNLOCK();
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_THROW, return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_THROW,
hook, hookData, rval); hook, hookData, rval);

Просмотреть файл

@ -95,7 +95,6 @@ _destroyFrame(JSDStackFrameInfo* jsdframe)
free(jsdframe); free(jsdframe);
} }
JSDThreadState* JSDThreadState*
jsd_NewThreadState(JSDContext* jsdc, JSContext *cx ) jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
{ {
@ -188,6 +187,19 @@ jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
return jsdframe; return jsdframe;
} }
JSContext *
jsd_GetJSContext (JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
JSContext* cx = NULL;
JSD_LOCK_THREADSTATES(jsdc);
if( jsd_IsValidThreadState(jsdc, jsdthreadstate) )
cx = jsdthreadstate->context;
JSD_UNLOCK_THREADSTATES(jsdc);
return cx;
}
JSDStackFrameInfo* JSDStackFrameInfo*
jsd_GetCallingStackFrame(JSDContext* jsdc, jsd_GetCallingStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate, JSDThreadState* jsdthreadstate,

Просмотреть файл

@ -136,7 +136,7 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before,
} }
else else
{ {
hookresult = JS_FALSE; hookresult = JS_TRUE;
} }
#ifdef JSD_TRACE #ifdef JSD_TRACE

Просмотреть файл

@ -39,6 +39,11 @@
#include "nsIXPConnect.h" #include "nsIXPConnect.h"
#include "nsIGenericFactory.h" #include "nsIGenericFactory.h"
#include "nsIServiceManager.h" #include "nsIServiceManager.h"
#include "nsIScriptGlobalObject.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsICategoryManager.h"
#include "nsIJSRuntimeService.h"
#include "nsIEventQueueService.h" #include "nsIEventQueueService.h"
#include "nsMemory.h" #include "nsMemory.h"
#include "jsdebug.h" #include "jsdebug.h"
@ -76,6 +81,21 @@
{0xa3, 0x47, 0xee, 0x6b, 0x76, 0x60, 0xe0, 0x48} \ {0xa3, 0x47, 0xee, 0x6b, 0x76, 0x60, 0xe0, 0x48} \
} }
#define JSDASO_CID \
{ /* 2fd6b7f6-eb8c-4f32-ad26-113f2c02d0fe */ \
0x2fd6b7f6, \
0xeb8c, \
0x4f32, \
{0xad, 0x26, 0x11, 0x3f, 0x2c, 0x02, 0xd0, 0xfe} \
}
#define NS_CATMAN_CTRID "@mozilla.org/categorymanager;1"
#define NS_JSRT_CTRID "@mozilla.org/js/xpc/RuntimeService;1"
#define AUTOREG_CATEGORY "xpcom-autoregistration"
#define APPSTART_CATEGORY "app-startup"
#define JSD_STARTUP_ENTRY "JSDebugger Startup Observer"
JS_STATIC_DLL_CALLBACK (JSBool) JS_STATIC_DLL_CALLBACK (JSBool)
jsds_GCCallbackProc (JSContext *cx, JSGCStatus status); jsds_GCCallbackProc (JSContext *cx, JSGCStatus status);
@ -87,7 +107,7 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1"; const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
const char jsdASObserverCtrID[] = "@mozilla.org/js/jsd/app-start-observer;1"; const char jsdASObserverCtrID[] = "@mozilla.org/js/jsd/app-start-observer;2";
#ifdef DEBUG_verbose #ifdef DEBUG_verbose
PRUint32 gScriptCount = 0; PRUint32 gScriptCount = 0;
@ -105,6 +125,25 @@ static struct DeadScript {
jsdIScript *script; jsdIScript *script;
} *gDeadScripts = nsnull; } *gDeadScripts = nsnull;
enum PatternType {
ptIgnore = 0U,
ptStartsWith = 1U,
ptEndsWith = 2U,
ptContains = 3U,
ptEquals = 4U
};
static struct FilterRecord {
PRCList links;
jsdIFilter *filterObject;
void *glob;
char *urlPattern;
PRUint32 patternLength;
PatternType patternType;
PRUint32 startLine;
PRUint32 endLine;
} *gFilters = nsnull;
static struct LiveEphemeral *gLiveValues = nsnull, *gLiveProperties = nsnull; static struct LiveEphemeral *gLiveValues = nsnull, *gLiveProperties = nsnull;
/******************************************************************************* /*******************************************************************************
@ -164,6 +203,218 @@ jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
PR_REMOVE_AND_INIT_LINK(&item->links); PR_REMOVE_AND_INIT_LINK(&item->links);
} }
/*******************************************************************************
* utility functions for filters
*******************************************************************************/
void
jsds_FreeFilter (FilterRecord *filter)
{
NS_IF_RELEASE (filter->filterObject);
if (filter->urlPattern)
nsMemory::Free(filter->urlPattern);
PR_Free (filter);
}
/* copies appropriate |filter| attributes into |rec|.
* False return indicates failure, the contents of |rec| will not be changed.
*/
PRBool
jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter)
{
NS_ASSERTION (rec, "jsds_SyncFilter without rec");
NS_ASSERTION (filter, "jsds_SyncFilter without filter");
JSObject *glob_proper = nsnull;
nsCOMPtr<nsISupports> glob;
nsresult rv = filter->GetGlob(getter_AddRefs(glob));
if (NS_FAILED(rv))
return PR_FALSE;
if (glob) {
nsCOMPtr<nsIScriptGlobalObject> nsiglob = do_QueryInterface(glob);
if (nsiglob)
glob_proper = nsiglob->GetGlobalJSObject();
}
PRUint32 startLine;
rv = filter->GetStartLine(&startLine);
if (NS_FAILED(rv))
return PR_FALSE;
PRUint32 endLine;
rv = filter->GetStartLine(&endLine);
if (NS_FAILED(rv))
return PR_FALSE;
char *urlPattern;
rv = filter->GetUrlPattern (&urlPattern);
if (NS_FAILED(rv))
return PR_FALSE;
if (urlPattern) {
PRUint32 len = PL_strlen(urlPattern);
if (urlPattern[0] == '*') {
/* pattern starts with a *, shift all chars once to the left,
* including the trailing null. */
memmove (&urlPattern[0], &urlPattern[1], len);
if (urlPattern[len - 2] == '*') {
/* pattern is in the format "*foo*", overwrite the final * with
* a null. */
urlPattern[len - 2] = '\0';
rec->patternType = ptContains;
rec->patternLength = len - 2;
} else {
/* pattern is in the format "*foo", just make a note of the
* new length. */
rec->patternType = ptEndsWith;
rec->patternLength = len - 1;
}
} else if (urlPattern[len - 1] == '*') {
/* pattern is in the format "foo*", overwrite the final * with a
* null. */
urlPattern[len - 1] = '\0';
rec->patternType = ptStartsWith;
rec->patternLength = len - 1;
} else {
/* pattern is in the format "foo". */
rec->patternType = ptEquals;
rec->patternLength = len;
}
} else {
rec->patternType = ptIgnore;
rec->patternLength = 0;
}
/* we got everything we need without failing, now copy it into rec. */
if (rec->filterObject != filter) {
NS_IF_RELEASE(rec->filterObject);
NS_ADDREF(filter);
rec->filterObject = filter;
}
rec->glob = glob_proper;
rec->startLine = startLine;
rec->endLine = endLine;
if (rec->urlPattern)
nsMemory::Free (rec->urlPattern);
rec->urlPattern = urlPattern;
return PR_TRUE;
}
FilterRecord *
jsds_FindFilter (jsdIFilter *filter)
{
if (!gFilters)
return nsnull;
FilterRecord *current = gFilters;
do {
if (current->filterObject == filter)
return current;
current = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK(&current->links));
} while (current != gFilters);
return nsnull;
}
/* returns true if the hook should be executed. */
PRBool
jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state)
{
if (!gFilters)
return PR_TRUE;
JSContext *cx = JSD_GetJSContext (jsdc, state);
void *glob = NS_STATIC_CAST(void *, JS_GetGlobalObject (cx));
if (NS_WARN_IF_FALSE(glob, "No global in threadstate"))
return PR_TRUE;
JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state);
if (NS_WARN_IF_FALSE(frame, "No frame in threadstate"))
return PR_TRUE;
JSDScript *script = JSD_GetScriptForStackFrame (jsdc, state, frame);
if (NS_WARN_IF_FALSE(script, "No script in threadstate"))
return PR_TRUE;
jsuint pc = JSD_GetPCForStackFrame (jsdc, state, frame);
if (NS_WARN_IF_FALSE(pc, "No pc in threadstate"))
return PR_TRUE;
PRUint32 currentLine = JSD_GetClosestLine (jsdc, script, pc);
if (NS_WARN_IF_FALSE(currentLine, "Can't convert pc to line"))
return PR_TRUE;
const char *url = nsnull;
PRUint32 len = 0;
FilterRecord *currentFilter = gFilters;
do {
PRUint32 flags = 0;
nsresult rv = currentFilter->filterObject->GetFlags(&flags);
NS_ASSERTION(NS_SUCCEEDED(rv), "Error getting flags for filter");
if (flags & jsdIFilter::FLAG_ENABLED) {
/* if there is no glob, or the globs match */
if ((!currentFilter->glob || currentFilter->glob == glob) &&
/* and there is no start line, or the start line is before
* or equal to the current */
(!currentFilter->startLine ||
currentFilter->startLine <= currentLine) &&
/* and there is no end line, or the end line is after
* or equal to the current */
(!currentFilter->endLine ||
currentFilter->endLine >= currentLine)) {
/* then we're going to have to compare the url. */
if (currentFilter->patternType == ptIgnore)
return flags & jsdIFilter::FLAG_PASS;
if (!url) {
url = JSD_GetScriptFilename (jsdc, script);
NS_ASSERTION (url, "Script with no filename");
len = PL_strlen(url);
}
if (len >= currentFilter->patternLength) {
switch (currentFilter->patternType) {
case ptEquals:
if (!PL_strcmp(currentFilter->urlPattern, url))
return flags & jsdIFilter::FLAG_PASS;
break;
case ptStartsWith:
if (!PL_strncmp(currentFilter->urlPattern, url,
currentFilter->patternLength))
return flags & jsdIFilter::FLAG_PASS;
break;
case ptEndsWith:
if (!PL_strcmp(currentFilter->urlPattern,
&url[len -
currentFilter->patternLength]))
return flags & jsdIFilter::FLAG_PASS;
break;
case ptContains:
if (PL_strstr(url, currentFilter->urlPattern))
return flags & jsdIFilter::FLAG_PASS;
break;
default:
NS_ASSERTION(0, "Invalid pattern type");
}
}
}
}
currentFilter = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK(&currentFilter->links));
} while (currentFilter != gFilters);
return PR_TRUE;
}
/******************************************************************************* /*******************************************************************************
* c callbacks * c callbacks
*******************************************************************************/ *******************************************************************************/
@ -179,6 +430,7 @@ jsds_NotifyPendingDeadScripts (JSContext *cx)
#ifdef CAUTIOUS_SCRIPTHOOK #ifdef CAUTIOUS_SCRIPTHOOK
JSRuntime *rt = JS_GetRuntime(cx); JSRuntime *rt = JS_GetRuntime(cx);
#endif #endif
gJsds->Pause(nsnull);
do { do {
ds = gDeadScripts; ds = gDeadScripts;
@ -201,6 +453,7 @@ jsds_NotifyPendingDeadScripts (JSContext *cx)
PR_Free(ds); PR_Free(ds);
} while (&gDeadScripts->links != &ds->links); } while (&gDeadScripts->links != &ds->links);
/* keep going until we catch up with our tail */ /* keep going until we catch up with our tail */
gJsds->UnPause(nsnull);
} }
gDeadScripts = 0; gDeadScripts = 0;
} }
@ -209,7 +462,7 @@ JS_STATIC_DLL_CALLBACK (JSBool)
jsds_GCCallbackProc (JSContext *cx, JSGCStatus status) jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
{ {
gGCStatus = status; gGCStatus = status;
#ifdef DEBUG #ifdef DEBUG_verbose
printf ("new gc status is %i\n", status); printf ("new gc status is %i\n", status);
#endif #endif
if (status == JSGC_END && gDeadScripts) if (status == JSGC_END && gDeadScripts)
@ -221,16 +474,6 @@ jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
return JS_TRUE; return JS_TRUE;
} }
JS_STATIC_DLL_CALLBACK (JSBool)
jsds_EmptyCallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
uintN type, void* callerdata)
{
/* empty hook returns true so that we get called when the function
* returns (we may have a hook function by then.) */
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK (JSBool) JS_STATIC_DLL_CALLBACK (JSBool)
jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate, jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
uintN type, void* callerdata) uintN type, void* callerdata)
@ -256,11 +499,16 @@ jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
if (!hook) if (!hook)
return JS_TRUE; return JS_TRUE;
if (!jsds_FilterHook (jsdc, jsdthreadstate))
return JS_FALSE;
JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate); JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
nsCOMPtr<jsdIStackFrame> frame = nsCOMPtr<jsdIStackFrame> frame =
getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
native_frame)); native_frame));
gJsds->Pause(nsnull);
hook->OnCall(frame, type); hook->OnCall(frame, type);
gJsds->UnPause(nsnull);
frame->Invalidate(); frame->Invalidate();
return JS_TRUE; return JS_TRUE;
@ -305,11 +553,16 @@ jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
if (!hook) if (!hook)
return hook_rv; return hook_rv;
if (!jsds_FilterHook (jsdc, jsdthreadstate))
return JSD_HOOK_RETURN_CONTINUE;
JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate); JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
nsCOMPtr<jsdIStackFrame> frame = nsCOMPtr<jsdIStackFrame> frame =
getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
native_frame)); native_frame));
gJsds->Pause(nsnull);
hook->OnExecute (frame, type, &js_rv, &hook_rv); hook->OnExecute (frame, type, &js_rv, &hook_rv);
gJsds->UnPause(nsnull);
frame->Invalidate(); frame->Invalidate();
if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL || if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
@ -345,7 +598,9 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
#ifdef CAUTIOUS_SCRIPTHOOK #ifdef CAUTIOUS_SCRIPTHOOK
JS_DISABLE_GC(rt); JS_DISABLE_GC(rt);
#endif #endif
gJsds->Pause(nsnull);
hook->OnScriptCreated (script); hook->OnScriptCreated (script);
gJsds->UnPause(nsnull);
#ifdef CAUTIOUS_SCRIPTHOOK #ifdef CAUTIOUS_SCRIPTHOOK
JS_ENABLE_GC(rt); JS_ENABLE_GC(rt);
#endif #endif
@ -364,7 +619,10 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
#ifdef CAUTIOUS_SCRIPTHOOK #ifdef CAUTIOUS_SCRIPTHOOK
JS_DISABLE_GC(rt); JS_DISABLE_GC(rt);
#endif #endif
gJsds->Pause(nsnull);
hook->OnScriptDestroyed (jsdis); hook->OnScriptDestroyed (jsdis);
gJsds->UnPause(nsnull);
#ifdef CAUTIOUS_SCRIPTHOOK #ifdef CAUTIOUS_SCRIPTHOOK
JS_ENABLE_GC(rt); JS_ENABLE_GC(rt);
#endif #endif
@ -589,8 +847,7 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE),
DEBUG_CREATE ("jsdScript", gScriptCount); DEBUG_CREATE ("jsdScript", gScriptCount);
NS_INIT_ISUPPORTS(); NS_INIT_ISUPPORTS();
if (mScript) if (mScript) {
{
/* copy the script's information now, so we have it later, when it /* copy the script's information now, so we have it later, when it
* gets destroyed. */ * gets destroyed. */
JSD_LockScriptSubsystem(mCx); JSD_LockScriptSubsystem(mCx);
@ -705,6 +962,15 @@ jsdScript::LineToPc(PRUint32 aLine, jsdIPC **_rval)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
jsdScript::IsLineExecutable(PRUint32 aLine, PRBool *_rval)
{
ASSERT_VALID_SCRIPT;
jsuword pc = JSD_GetClosestPC (mCx, mScript, aLine);
*_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc));
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
jsdScript::SetBreakpoint(jsdIPC *aPC) jsdScript::SetBreakpoint(jsdIPC *aPC)
{ {
@ -1033,7 +1299,8 @@ NS_IMETHODIMP
jsdValue::GetJsFunctionName(char **_rval) jsdValue::GetJsFunctionName(char **_rval)
{ {
ASSERT_VALID_VALUE; ASSERT_VALID_VALUE;
*_rval = ToNewCString(nsDependentCString(JSD_GetValueFunctionName(mCx, mValue))); *_rval =
ToNewCString(nsDependentCString(JSD_GetValueFunctionName(mCx, mValue)));
return NS_OK; return NS_OK;
} }
@ -1157,6 +1424,109 @@ jsdValue::Refresh()
******************************************************************************/ ******************************************************************************/
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdService, jsdIDebuggerService); NS_IMPL_THREADSAFE_ISUPPORTS1(jsdService, jsdIDebuggerService);
NS_IMETHODIMP
jsdService::GetInitAtStartup (PRBool *_rval)
{
nsresult rv;
nsCOMPtr<nsICategoryManager>
categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
if (NS_FAILED(rv))
return rv;
if (mInitAtStartup == triUnknown) {
nsXPIDLCString notused;
nsresult autoreg_rv, appstart_rv;
autoreg_rv = categoryManager->GetCategoryEntry(AUTOREG_CATEGORY,
JSD_STARTUP_ENTRY,
getter_Copies(notused));
appstart_rv = categoryManager->GetCategoryEntry(APPSTART_CATEGORY,
JSD_STARTUP_ENTRY,
getter_Copies(notused));
if (autoreg_rv != appstart_rv) {
/* we have an inconsistent state in the registry, attempt to fix.
* we need to make mInitAtStartup disagree with the state passed
* to SetInitAtStartup to make it actually do something.
*/
mInitAtStartup = triYes;
rv = SetInitAtStartup (PR_FALSE);
if (NS_FAILED(rv))
return rv;
} else if (autoreg_rv == NS_ERROR_NOT_AVAILABLE) {
mInitAtStartup = triNo;
} else if (NS_SUCCEEDED(autoreg_rv)) {
mInitAtStartup = triYes;
} else {
return rv;
}
}
if (_rval)
*_rval = (mInitAtStartup == triYes);
return NS_OK;
}
/*
* The initAtStartup property controls whether or not we register the
* app start observer (jsdASObserver.) We register for both
* "xpcom-autoregistration" and "app-startup" notifications if |state| is true.
* the autoreg message is sent just before registration occurs (before
* "app-startup".) We care about autoreg because it may load javascript
* components. autoreg does *not* fire if components haven't changed since the
* last autoreg, so we watch "app-startup" as a fallback.
*/
NS_IMETHODIMP
jsdService::SetInitAtStartup (PRBool state)
{
nsresult rv;
if (mInitAtStartup == triUnknown) {
/* side effect sets mInitAtStartup */
rv = GetInitAtStartup(nsnull);
if (NS_FAILED(rv))
return rv;
}
if (state && mInitAtStartup == triYes ||
!state && mInitAtStartup == triNo) {
/* already in the requested state */
return NS_OK;
}
nsCOMPtr<nsICategoryManager>
categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
if (state) {
rv = categoryManager->AddCategoryEntry(AUTOREG_CATEGORY,
JSD_STARTUP_ENTRY,
jsdASObserverCtrID,
PR_TRUE, PR_TRUE, nsnull);
if (NS_FAILED(rv))
return rv;
rv = categoryManager->AddCategoryEntry(APPSTART_CATEGORY,
JSD_STARTUP_ENTRY,
jsdASObserverCtrID,
PR_TRUE, PR_TRUE, nsnull);
if (NS_FAILED(rv))
return rv;
mInitAtStartup = triYes;
} else {
rv = categoryManager->DeleteCategoryEntry(AUTOREG_CATEGORY,
JSD_STARTUP_ENTRY, PR_TRUE);
if (NS_FAILED(rv))
return rv;
rv = categoryManager->DeleteCategoryEntry(APPSTART_CATEGORY,
JSD_STARTUP_ENTRY, PR_TRUE);
if (NS_FAILED(rv))
return rv;
mInitAtStartup = triNo;
}
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
jsdService::GetIsOn (PRBool *_rval) jsdService::GetIsOn (PRBool *_rval)
{ {
@ -1228,23 +1598,29 @@ jsdService::OnForRuntime (JSRuntime *rt)
if (mTopLevelHook) if (mTopLevelHook)
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL); JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
else else
JSD_SetTopLevelHook (mCx, jsds_EmptyCallHookProc, NULL); JSD_ClearTopLevelHook (mCx);
if (mFunctionHook) if (mFunctionHook)
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL); JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
else else
JSD_SetFunctionHook (mCx, jsds_EmptyCallHookProc, NULL); JSD_ClearFunctionHook (mCx);
mOn = PR_TRUE; mOn = PR_TRUE;
#ifdef DEBUG
printf ("+++ JavaScript debuging hooks installed.\n");
#endif
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP
jsdService::Off (void) jsdService::Off (void)
{ {
if (!mOn)
return NS_OK;
if (!mCx || !mRuntime) if (!mCx || !mRuntime)
return NS_ERROR_NOT_INITIALIZED; return NS_ERROR_NOT_INITIALIZED;
if (gDeadScripts) if (gDeadScripts) {
if (gGCStatus == JSGC_END) if (gGCStatus == JSGC_END)
{ {
JSContext *cx = JSD_GetDefaultJSContext(mCx); JSContext *cx = JSD_GetDefaultJSContext(mCx);
@ -1252,6 +1628,7 @@ jsdService::Off (void)
} }
else else
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
}
/* /*
if (gLastGCProc != jsds_GCCallbackProc) if (gLastGCProc != jsds_GCCallbackProc)
@ -1262,19 +1639,73 @@ jsdService::Off (void)
jsdProperty::InvalidateAll(); jsdProperty::InvalidateAll();
ClearAllBreakpoints(); ClearAllBreakpoints();
JSD_SetThrowHook (mCx, NULL, NULL); JSD_ClearThrowHook (mCx);
JSD_SetScriptHook (mCx, NULL, NULL); JSD_SetScriptHook (mCx, NULL, NULL);
JSD_SetInterruptHook (mCx, NULL, NULL); JSD_ClearInterruptHook (mCx);
JSD_SetDebuggerHook (mCx, NULL, NULL); JSD_ClearDebuggerHook (mCx);
JSD_SetDebugBreakHook (mCx, NULL, NULL); JSD_ClearDebugBreakHook (mCx);
JSD_SetTopLevelHook (mCx, NULL, NULL); JSD_ClearTopLevelHook (mCx);
JSD_SetFunctionHook (mCx, NULL, NULL); JSD_ClearFunctionHook (mCx);
JSD_DebuggerOff (mCx); JSD_DebuggerOff (mCx);
mCx = nsnull; mCx = nsnull;
mRuntime = nsnull; mRuntime = nsnull;
mOn = PR_FALSE; mOn = PR_FALSE;
#ifdef DEBUG
printf ("+++ JavaScript debuging hooks removed.\n");
#endif
return NS_OK;
}
NS_IMETHODIMP
jsdService::Pause(PRUint32 *_rval)
{
if (++mPauseLevel == 1) {
JSD_ClearThrowHook (mCx);
JSD_ClearInterruptHook (mCx);
JSD_ClearDebuggerHook (mCx);
JSD_ClearDebugBreakHook (mCx);
JSD_ClearTopLevelHook (mCx);
JSD_ClearFunctionHook (mCx);
}
if (_rval)
*_rval = mPauseLevel;
return NS_OK;
}
NS_IMETHODIMP
jsdService::UnPause(PRUint32 *_rval)
{
if (mPauseLevel == 0)
return NS_ERROR_NOT_AVAILABLE;
if (--mPauseLevel == 0) {
if (mThrowHook)
JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, NULL);
if (mInterruptHook)
JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, NULL);
if (mDebuggerHook)
JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, NULL);
if (mErrorHook)
JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, NULL);
if (mTopLevelHook)
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
else
JSD_ClearTopLevelHook (mCx);
if (mFunctionHook)
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
else
JSD_ClearFunctionHook (mCx);
}
if (_rval)
*_rval = mPauseLevel;
return NS_OK; return NS_OK;
} }
@ -1285,14 +1716,12 @@ jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
JSDScript *script; JSDScript *script;
JSDScript *iter = NULL; JSDScript *iter = NULL;
PRBool cont_flag;
nsresult rv = NS_OK; nsresult rv = NS_OK;
JSD_LockScriptSubsystem(mCx); JSD_LockScriptSubsystem(mCx);
while((script = JSD_IterateScripts(mCx, &iter)) != NULL) { while((script = JSD_IterateScripts(mCx, &iter)) != NULL) {
rv = enumerator->EnumerateScript (jsdScript::FromPtr(mCx, script), rv = enumerator->EnumerateScript (jsdScript::FromPtr(mCx, script));
&cont_flag); if (NS_FAILED(rv))
if (NS_FAILED(rv) || !cont_flag)
break; break;
} }
JSD_UnlockScriptSubsystem(mCx); JSD_UnlockScriptSubsystem(mCx);
@ -1310,6 +1739,171 @@ jsdService::GC (void)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
jsdService::InsertFilter (jsdIFilter *filter, jsdIFilter *after)
{
NS_ENSURE_ARG_POINTER (filter);
if (jsds_FindFilter (filter))
return NS_ERROR_INVALID_ARG;
FilterRecord *rec = PR_NEWZAP (FilterRecord);
if (!jsds_SyncFilter (rec, filter)) {
PR_Free (rec);
return NS_ERROR_FAILURE;
}
if (gFilters) {
if (!after) {
/* insert at head of list */
PR_INSERT_LINK(&rec->links, &gFilters->links);
gFilters = rec;
} else {
/* insert somewhere in the list */
FilterRecord *afterRecord = jsds_FindFilter (after);
if (!afterRecord) {
jsds_FreeFilter(rec);
return NS_ERROR_INVALID_ARG;
}
PR_INSERT_AFTER(&rec->links, &afterRecord->links);
}
} else {
if (after) {
/* user asked to insert into the middle of an empty list, bail. */
jsds_FreeFilter(rec);
return NS_ERROR_NOT_INITIALIZED;
}
PR_INIT_CLIST(&rec->links);
gFilters = rec;
}
return NS_OK;
}
NS_IMETHODIMP
jsdService::AppendFilter (jsdIFilter *filter)
{
NS_ENSURE_ARG_POINTER (filter);
if (jsds_FindFilter (filter))
return NS_ERROR_INVALID_ARG;
FilterRecord *rec = PR_NEWZAP (FilterRecord);
if (!jsds_SyncFilter (rec, filter)) {
PR_Free (rec);
return NS_ERROR_FAILURE;
}
if (gFilters) {
PR_INSERT_BEFORE(&rec->links, &gFilters->links);
} else {
PR_INIT_CLIST(&rec->links);
gFilters = rec;
}
return NS_OK;
}
NS_IMETHODIMP
jsdService::RemoveFilter (jsdIFilter *filter)
{
NS_ENSURE_ARG_POINTER(filter);
FilterRecord *rec = jsds_FindFilter (filter);
if (!rec)
return NS_ERROR_INVALID_ARG;
if (gFilters == rec)
gFilters = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK(&rec->links));
PR_REMOVE_LINK(&rec->links);
jsds_FreeFilter (rec);
return NS_OK;
}
NS_IMETHODIMP
jsdService::SwapFilters (jsdIFilter *filter_a, jsdIFilter *filter_b)
{
NS_ENSURE_ARG_POINTER(filter_a);
NS_ENSURE_ARG_POINTER(filter_b);
FilterRecord *rec_a = jsds_FindFilter (filter_a);
if (!rec_a)
return NS_ERROR_INVALID_ARG;
if (filter_a == filter_b) {
/* just a refresh */
if (!jsds_SyncFilter (rec_a, filter_a))
return NS_ERROR_FAILURE;
return NS_OK;
}
FilterRecord *rec_b = jsds_FindFilter (filter_b);
if (!rec_b) {
/* filter_b is not in the list, replace filter_a with filter_b. */
if (!jsds_SyncFilter (rec_a, filter_b))
return NS_ERROR_FAILURE;
} else {
/* both filters are in the list, swap. */
if (!jsds_SyncFilter (rec_a, filter_b))
return NS_ERROR_FAILURE;
if (!jsds_SyncFilter (rec_b, filter_a))
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator)
{
if (!gFilters)
return NS_OK;
FilterRecord *current = gFilters;
do {
jsds_SyncFilter (current, current->filterObject);
/* SyncFilter failure would be bad, but what would we do about it? */
if (enumerator) {
nsresult rv = enumerator->EnumerateFilter (current->filterObject);
if (NS_FAILED(rv))
return rv;
}
current = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK (&current->links));
} while (current != gFilters);
return NS_OK;
}
NS_IMETHODIMP
jsdService::RefreshFilters ()
{
return EnumerateFilters(nsnull);
}
NS_IMETHODIMP
jsdService::ClearFilters ()
{
if (!gFilters)
return NS_OK;
FilterRecord *current = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK (&gFilters->links));
do {
FilterRecord *next = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK (&current->links));
PR_REMOVE_AND_INIT_LINK(&current->links);
jsds_FreeFilter(current);
current = next;
} while (current != gFilters);
jsds_FreeFilter(current);
gFilters = nsnull;
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
jsdService::ClearAllBreakpoints (void) jsdService::ClearAllBreakpoints (void)
{ {
@ -1342,9 +1936,10 @@ jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval)
if (stack && NS_SUCCEEDED(stack->Push(nsnull)) && if (stack && NS_SUCCEEDED(stack->Push(nsnull)) &&
NS_SUCCEEDED(eventService->PushThreadEventQueue(getter_AddRefs(eventQ)))) NS_SUCCEEDED(eventService->PushThreadEventQueue(getter_AddRefs(eventQ))))
{ {
if (NS_SUCCEEDED(rv) && callback) if (NS_SUCCEEDED(rv) && callback) {
{ Pause(nsnull);
rv = callback->OnNest(); rv = callback->OnNest();
UnPause(nsnull);
} }
while(NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) while(NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel)
@ -1355,10 +1950,8 @@ jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval)
rv = appShell->GetNativeEvent(isRealEvent, data); rv = appShell->GetNativeEvent(isRealEvent, data);
if(NS_SUCCEEDED(rv)) if(NS_SUCCEEDED(rv))
{
appShell->DispatchNativeEvent(isRealEvent, data); appShell->DispatchNativeEvent(isRealEvent, data);
} }
}
JSContext* cx; JSContext* cx;
stack->Pop(&cx); stack->Pop(&cx);
NS_ASSERTION(cx == nsnull, "JSContextStack mismatch"); NS_ASSERTION(cx == nsnull, "JSContextStack mismatch");
@ -1416,7 +2009,7 @@ jsdService::SetErrorHook (jsdIExecutionHook *aHook)
/* if the debugger isn't initialized, that's all we can do for now. The /* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear. * OnForRuntime() method will do the rest when the coast is clear.
*/ */
if (!mCx) if (!mCx || mPauseLevel)
return NS_OK; return NS_OK;
if (aHook) if (aHook)
@ -1444,7 +2037,7 @@ jsdService::SetDebuggerHook (jsdIExecutionHook *aHook)
/* if the debugger isn't initialized, that's all we can do for now. The /* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear. * OnForRuntime() method will do the rest when the coast is clear.
*/ */
if (!mCx) if (!mCx || mPauseLevel)
return NS_OK; return NS_OK;
if (aHook) if (aHook)
@ -1472,7 +2065,7 @@ jsdService::SetInterruptHook (jsdIExecutionHook *aHook)
/* if the debugger isn't initialized, that's all we can do for now. The /* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear. * OnForRuntime() method will do the rest when the coast is clear.
*/ */
if (!mCx) if (!mCx || mPauseLevel)
return NS_OK; return NS_OK;
if (aHook) if (aHook)
@ -1500,7 +2093,7 @@ jsdService::SetScriptHook (jsdIScriptHook *aHook)
/* if the debugger isn't initialized, that's all we can do for now. The /* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear. * OnForRuntime() method will do the rest when the coast is clear.
*/ */
if (!mCx) if (!mCx || mPauseLevel)
return NS_OK; return NS_OK;
if (aHook) if (aHook)
@ -1528,7 +2121,7 @@ jsdService::SetThrowHook (jsdIExecutionHook *aHook)
/* if the debugger isn't initialized, that's all we can do for now. The /* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear. * OnForRuntime() method will do the rest when the coast is clear.
*/ */
if (!mCx) if (!mCx || mPauseLevel)
return NS_OK; return NS_OK;
if (aHook) if (aHook)
@ -1556,13 +2149,13 @@ jsdService::SetTopLevelHook (jsdICallHook *aHook)
/* if the debugger isn't initialized, that's all we can do for now. The /* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear. * OnForRuntime() method will do the rest when the coast is clear.
*/ */
if (!mCx) if (!mCx || mPauseLevel)
return NS_OK; return NS_OK;
if (aHook) if (aHook)
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL); JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
else else
JSD_SetTopLevelHook (mCx, jsds_EmptyCallHookProc, NULL); JSD_ClearTopLevelHook (mCx);
return NS_OK; return NS_OK;
} }
@ -1584,13 +2177,13 @@ jsdService::SetFunctionHook (jsdICallHook *aHook)
/* if the debugger isn't initialized, that's all we can do for now. The /* if the debugger isn't initialized, that's all we can do for now. The
* OnForRuntime() method will do the rest when the coast is clear. * OnForRuntime() method will do the rest when the coast is clear.
*/ */
if (!mCx) if (!mCx || mPauseLevel)
return NS_OK; return NS_OK;
if (aHook) if (aHook)
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL); JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
else else
JSD_SetFunctionHook (mCx, jsds_EmptyCallHookProc, NULL); JSD_ClearFunctionHook (mCx);
return NS_OK; return NS_OK;
} }
@ -1616,8 +2209,55 @@ jsdService::GetService ()
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService); NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService);
/* app-start observer. turns on the debugger at app-start. this is inserted
* and/or removed from the app-start category by the jsdService::initAtStartup
* property.
*/
class jsdASObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
jsdASObserver ()
{
NS_INIT_ISUPPORTS();
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(jsdASObserver, nsIObserver);
NS_IMETHODIMP
jsdASObserver::Observe (nsISupports *aSubject, const char *aTopic,
const PRUnichar *aData)
{
nsresult rv;
jsdService *jsds = jsdService::GetService();
PRBool on;
rv = jsds->GetIsOn(&on);
if (NS_FAILED(rv) || on)
return rv;
nsCOMPtr<nsIJSRuntimeService> rts = do_GetService(NS_JSRT_CTRID, &rv);
if (NS_FAILED(rv))
return rv;
JSRuntime *rt;
rts->GetRuntime (&rt);
if (NS_FAILED(rv))
return rv;
rv = jsds->OnForRuntime(rt);
return rv;
}
NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver);
static nsModuleComponentInfo components[] = { static nsModuleComponentInfo components[] = {
{"JSDService", JSDSERVICE_CID, jsdServiceCtrID, jsdServiceConstructor} {"JSDService", JSDSERVICE_CID, jsdServiceCtrID, jsdServiceConstructor},
{"JSDASObserver", JSDASO_CID, jsdASObserverCtrID, jsdASObserverConstructor}
}; };
NS_IMPL_NSGETMODULE(JavaScript_Debugger, components); NS_IMPL_NSGETMODULE(JavaScript_Debugger, components);

Просмотреть файл

@ -285,20 +285,33 @@ class jsdService : public jsdIDebuggerService
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_JSDIDEBUGGERSERVICE NS_DECL_JSDIDEBUGGERSERVICE
jsdService() : mOn(PR_FALSE), mNestedLoopLevel(0), mCx(0), mRuntime(0), jsdService() : mInitAtStartup(triUnknown), mOn(PR_FALSE), mPauseLevel(0),
mBreakpointHook(0), mErrorHook(0), mDebuggerHook(0), mNestedLoopLevel(0), mCx(0), mRuntime(0), mBreakpointHook(0),
mInterruptHook(0), mScriptHook(0), mThrowHook(0), mErrorHook(0), mDebuggerHook(0), mInterruptHook(0),
mTopLevelHook(0), mFunctionHook(0) mScriptHook(0), mThrowHook(0), mTopLevelHook(0),
mFunctionHook(0)
{ {
NS_INIT_ISUPPORTS(); NS_INIT_ISUPPORTS();
} }
virtual ~jsdService() { } virtual ~jsdService()
{
ClearFilters();
Off();
}
static jsdService *GetService (); static jsdService *GetService ();
private: private:
enum Tristate {
triUnknown = 0U,
triYes = 1U,
triNo = 2U
};
Tristate mInitAtStartup;
PRBool mOn; PRBool mOn;
PRUint32 mPauseLevel;
PRUint32 mNestedLoopLevel; PRUint32 mNestedLoopLevel;
JSDContext *mCx; JSDContext *mCx;
JSRuntime *mRuntime; JSRuntime *mRuntime;

Просмотреть файл

@ -550,6 +550,13 @@ JSD_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
return jsd_GetStackFrame(jsdc, jsdthreadstate); return jsd_GetStackFrame(jsdc, jsdthreadstate);
} }
JSD_PUBLIC_API(JSContext*)
JSD_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
{
JSD_ASSERT_VALID_CONTEXT(jsdc);
return jsd_GetJSContext(jsdc, jsdthreadstate);
}
JSD_PUBLIC_API(JSDStackFrameInfo*) JSD_PUBLIC_API(JSDStackFrameInfo*)
JSD_GetCallingStackFrame(JSDContext* jsdc, JSD_GetCallingStackFrame(JSDContext* jsdc,
JSDThreadState* jsdthreadstate, JSDThreadState* jsdthreadstate,

Просмотреть файл

@ -723,6 +723,12 @@ JSD_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
extern JSD_PUBLIC_API(JSDStackFrameInfo*) extern JSD_PUBLIC_API(JSDStackFrameInfo*)
JSD_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate); JSD_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
/*
* Get the JSContext for the given JSDThreadState
*/
extern JSD_PUBLIC_API(JSContext*)
JSD_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
/* /*
* Get the calling call stack frame for the given frame * Get the calling call stack frame for the given frame
*/ */

Просмотреть файл

@ -25,13 +25,15 @@
#//------------------------------------------------------------------------ #//------------------------------------------------------------------------
DEPTH=..\.. DEPTH=..\..
REQUIRES = xpcom \ REQUIRES = js \
!if !defined(JSD_STANDALONE)
xpcom \
string \ string \
js \
xpconnect \ xpconnect \
widget \ widget \
appshell \ appshell \
pref \ dom \
!endif
$(NULL) $(NULL)
include <$(DEPTH)\config\config.mak> include <$(DEPTH)\config\config.mak>