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. git-svn-id: svn://10.0.0.236/trunk@106689 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
487a8a8072
commit
9dffd404bd
@ -32,12 +32,17 @@ MODULE = jsdebug
|
||||
LIBRARY_NAME = jsd
|
||||
|
||||
# REQUIRES = java js
|
||||
REQUIRES = xpcom \
|
||||
REQUIRES = js
|
||||
|
||||
ifndef JSD_STANDALONE
|
||||
REQUIRES += \
|
||||
string \
|
||||
js \
|
||||
xpcom \
|
||||
xpconnect \
|
||||
widget \
|
||||
dom \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
EXTRA_DSO_LDOPTS += \
|
||||
$(MOZ_COMPONENT_LIBS) \
|
||||
|
||||
@ -53,7 +53,9 @@ native jsuword(jsuword);
|
||||
|
||||
/* interfaces we declare in this file */
|
||||
interface jsdIDebuggerService;
|
||||
interface jsdIFilter;
|
||||
interface jsdINestCallback;
|
||||
interface jsdIFilterEnumerator;
|
||||
interface jsdIScriptEnumerator;
|
||||
interface jsdIScriptHook;
|
||||
interface jsdIExecutionHook;
|
||||
@ -106,6 +108,12 @@ interface jsdIDebuggerService : nsISupports
|
||||
*/
|
||||
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
|
||||
* necessarily mean another app is actively using the service, as the
|
||||
@ -134,23 +142,87 @@ interface jsdIDebuggerService : nsISupports
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
void GC();
|
||||
|
||||
/**
|
||||
* Ignore/unignore execution hooks which are triggered by a context with a
|
||||
* particular global object.
|
||||
* Adds an execution hook filter. These filters are consulted each time one
|
||||
* 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
|
||||
* context, global objects are DOM windows.
|
||||
* If |filter| is already present this method throws NS_ERROR_INVALID_ARG.
|
||||
*
|
||||
* @param state |true| to ignore this global object, |false| to unignore.
|
||||
* If state is |false| and the the glob passed in is not
|
||||
* currently ignored, this method will throw an
|
||||
* NS_ERROR_INVALID_PARAM exception.
|
||||
* @param filter Object representing the filter to add.
|
||||
* @param after Insert |filter| after this one. Pass null to insert at
|
||||
* the beginning.
|
||||
*/
|
||||
//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
|
||||
* before you turned the debugger on, or after turning the debugger off
|
||||
@ -165,16 +237,97 @@ interface jsdIDebuggerService : nsISupports
|
||||
*/
|
||||
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
|
||||
* These routines are used to nest and unnest UI and network event loops.
|
||||
* Push a new network queue, and enter a new UI event loop.
|
||||
* @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);
|
||||
/**
|
||||
* 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 ();
|
||||
};
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
@ -189,6 +342,19 @@ interface jsdINestCallback : nsISupports
|
||||
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.
|
||||
*/
|
||||
@ -199,7 +365,7 @@ interface jsdIScriptEnumerator : nsISupports
|
||||
* The enumerateScript method will be called once for every script the
|
||||
* debugger knows about.
|
||||
*/
|
||||
boolean enumerateScript (in jsdIScript script);
|
||||
void enumerateScript (in jsdIScript script);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -427,7 +593,12 @@ interface jsdIScript : jsdIEphemeral
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@ -586,6 +586,9 @@ jsd_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
|
||||
extern JSDStackFrameInfo*
|
||||
jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
|
||||
|
||||
extern JSContext*
|
||||
jsd_GetJSContext(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
|
||||
|
||||
extern JSDStackFrameInfo*
|
||||
jsd_GetCallingStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
|
||||
@ -56,6 +56,15 @@ jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rva
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
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);
|
||||
jsdscript = jsd_FindJSDScript(jsdc, script);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
@ -67,12 +76,6 @@ jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rva
|
||||
return JSTRAP_CONTINUE;
|
||||
#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,
|
||||
hook, hookData, rval);
|
||||
}
|
||||
@ -92,17 +95,19 @@ jsd_DebuggerHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
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 */
|
||||
JSD_LOCK();
|
||||
hook = jsdc->debuggerHook;
|
||||
hookData = jsdc->debuggerHookData;
|
||||
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,
|
||||
hook, hookData, rval);
|
||||
@ -118,25 +123,27 @@ jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
|
||||
JSD_ExecutionHookProc hook;
|
||||
void* hookData;
|
||||
|
||||
JS_GetPendingException(cx, rval);
|
||||
|
||||
if( ! jsdc || ! jsdc->inited )
|
||||
return JSD_HOOK_RETURN_CONTINUE_THROW;
|
||||
|
||||
if( JSD_IS_DANGEROUS_THREAD(jsdc) )
|
||||
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);
|
||||
jsdscript = jsd_FindJSDScript(jsdc, script);
|
||||
JSD_UNLOCK_SCRIPTS(jsdc);
|
||||
if( ! jsdscript )
|
||||
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();
|
||||
JS_GetPendingException(cx, rval);
|
||||
|
||||
return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_THROW,
|
||||
hook, hookData, rval);
|
||||
|
||||
@ -95,7 +95,6 @@ _destroyFrame(JSDStackFrameInfo* jsdframe)
|
||||
free(jsdframe);
|
||||
}
|
||||
|
||||
|
||||
JSDThreadState*
|
||||
jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
|
||||
{
|
||||
@ -188,6 +187,19 @@ jsd_GetStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate)
|
||||
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*
|
||||
jsd_GetCallingStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
|
||||
@ -136,7 +136,7 @@ _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before,
|
||||
}
|
||||
else
|
||||
{
|
||||
hookresult = JS_FALSE;
|
||||
hookresult = JS_TRUE;
|
||||
}
|
||||
|
||||
#ifdef JSD_TRACE
|
||||
|
||||
@ -39,6 +39,11 @@
|
||||
#include "nsIXPConnect.h"
|
||||
#include "nsIGenericFactory.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsICategoryManager.h"
|
||||
#include "nsIJSRuntimeService.h"
|
||||
#include "nsIEventQueueService.h"
|
||||
#include "nsMemory.h"
|
||||
#include "jsdebug.h"
|
||||
@ -76,6 +81,21 @@
|
||||
{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)
|
||||
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);
|
||||
|
||||
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
|
||||
PRUint32 gScriptCount = 0;
|
||||
@ -105,6 +125,25 @@ static struct DeadScript {
|
||||
jsdIScript *script;
|
||||
} *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;
|
||||
|
||||
/*******************************************************************************
|
||||
@ -164,6 +203,218 @@ jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item)
|
||||
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(¤t->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(¤tFilter->links));
|
||||
} while (currentFilter != gFilters);
|
||||
|
||||
return PR_TRUE;
|
||||
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* c callbacks
|
||||
*******************************************************************************/
|
||||
@ -179,6 +430,7 @@ jsds_NotifyPendingDeadScripts (JSContext *cx)
|
||||
#ifdef CAUTIOUS_SCRIPTHOOK
|
||||
JSRuntime *rt = JS_GetRuntime(cx);
|
||||
#endif
|
||||
gJsds->Pause(nsnull);
|
||||
do {
|
||||
ds = gDeadScripts;
|
||||
|
||||
@ -201,6 +453,7 @@ jsds_NotifyPendingDeadScripts (JSContext *cx)
|
||||
PR_Free(ds);
|
||||
} while (&gDeadScripts->links != &ds->links);
|
||||
/* keep going until we catch up with our tail */
|
||||
gJsds->UnPause(nsnull);
|
||||
}
|
||||
gDeadScripts = 0;
|
||||
}
|
||||
@ -209,7 +462,7 @@ JS_STATIC_DLL_CALLBACK (JSBool)
|
||||
jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
|
||||
{
|
||||
gGCStatus = status;
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_verbose
|
||||
printf ("new gc status is %i\n", status);
|
||||
#endif
|
||||
if (status == JSGC_END && gDeadScripts)
|
||||
@ -221,16 +474,6 @@ jsds_GCCallbackProc (JSContext *cx, JSGCStatus status)
|
||||
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)
|
||||
jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
|
||||
uintN type, void* callerdata)
|
||||
@ -256,11 +499,16 @@ jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
|
||||
if (!hook)
|
||||
return JS_TRUE;
|
||||
|
||||
if (!jsds_FilterHook (jsdc, jsdthreadstate))
|
||||
return JS_FALSE;
|
||||
|
||||
JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
|
||||
nsCOMPtr<jsdIStackFrame> frame =
|
||||
getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
|
||||
native_frame));
|
||||
gJsds->Pause(nsnull);
|
||||
hook->OnCall(frame, type);
|
||||
gJsds->UnPause(nsnull);
|
||||
frame->Invalidate();
|
||||
|
||||
return JS_TRUE;
|
||||
@ -305,11 +553,16 @@ jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
|
||||
if (!hook)
|
||||
return hook_rv;
|
||||
|
||||
if (!jsds_FilterHook (jsdc, jsdthreadstate))
|
||||
return JSD_HOOK_RETURN_CONTINUE;
|
||||
|
||||
JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate);
|
||||
nsCOMPtr<jsdIStackFrame> frame =
|
||||
getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
|
||||
native_frame));
|
||||
gJsds->Pause(nsnull);
|
||||
hook->OnExecute (frame, type, &js_rv, &hook_rv);
|
||||
gJsds->UnPause(nsnull);
|
||||
frame->Invalidate();
|
||||
|
||||
if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
|
||||
@ -345,7 +598,9 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
|
||||
#ifdef CAUTIOUS_SCRIPTHOOK
|
||||
JS_DISABLE_GC(rt);
|
||||
#endif
|
||||
gJsds->Pause(nsnull);
|
||||
hook->OnScriptCreated (script);
|
||||
gJsds->UnPause(nsnull);
|
||||
#ifdef CAUTIOUS_SCRIPTHOOK
|
||||
JS_ENABLE_GC(rt);
|
||||
#endif
|
||||
@ -364,7 +619,10 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
|
||||
#ifdef CAUTIOUS_SCRIPTHOOK
|
||||
JS_DISABLE_GC(rt);
|
||||
#endif
|
||||
|
||||
gJsds->Pause(nsnull);
|
||||
hook->OnScriptDestroyed (jsdis);
|
||||
gJsds->UnPause(nsnull);
|
||||
#ifdef CAUTIOUS_SCRIPTHOOK
|
||||
JS_ENABLE_GC(rt);
|
||||
#endif
|
||||
@ -589,8 +847,7 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE),
|
||||
DEBUG_CREATE ("jsdScript", gScriptCount);
|
||||
NS_INIT_ISUPPORTS();
|
||||
|
||||
if (mScript)
|
||||
{
|
||||
if (mScript) {
|
||||
/* copy the script's information now, so we have it later, when it
|
||||
* gets destroyed. */
|
||||
JSD_LockScriptSubsystem(mCx);
|
||||
@ -705,6 +962,15 @@ jsdScript::LineToPc(PRUint32 aLine, jsdIPC **_rval)
|
||||
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
|
||||
jsdScript::SetBreakpoint(jsdIPC *aPC)
|
||||
{
|
||||
@ -1033,7 +1299,8 @@ NS_IMETHODIMP
|
||||
jsdValue::GetJsFunctionName(char **_rval)
|
||||
{
|
||||
ASSERT_VALID_VALUE;
|
||||
*_rval = ToNewCString(nsDependentCString(JSD_GetValueFunctionName(mCx, mValue)));
|
||||
*_rval =
|
||||
ToNewCString(nsDependentCString(JSD_GetValueFunctionName(mCx, mValue)));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1157,6 +1424,109 @@ jsdValue::Refresh()
|
||||
******************************************************************************/
|
||||
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
|
||||
jsdService::GetIsOn (PRBool *_rval)
|
||||
{
|
||||
@ -1228,23 +1598,29 @@ jsdService::OnForRuntime (JSRuntime *rt)
|
||||
if (mTopLevelHook)
|
||||
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
|
||||
else
|
||||
JSD_SetTopLevelHook (mCx, jsds_EmptyCallHookProc, NULL);
|
||||
JSD_ClearTopLevelHook (mCx);
|
||||
if (mFunctionHook)
|
||||
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
|
||||
else
|
||||
JSD_SetFunctionHook (mCx, jsds_EmptyCallHookProc, NULL);
|
||||
JSD_ClearFunctionHook (mCx);
|
||||
mOn = PR_TRUE;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf ("+++ JavaScript debuging hooks installed.\n");
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
jsdService::Off (void)
|
||||
{
|
||||
if (!mOn)
|
||||
return NS_OK;
|
||||
|
||||
if (!mCx || !mRuntime)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
if (gDeadScripts)
|
||||
if (gDeadScripts) {
|
||||
if (gGCStatus == JSGC_END)
|
||||
{
|
||||
JSContext *cx = JSD_GetDefaultJSContext(mCx);
|
||||
@ -1252,6 +1628,7 @@ jsdService::Off (void)
|
||||
}
|
||||
else
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
/*
|
||||
if (gLastGCProc != jsds_GCCallbackProc)
|
||||
@ -1262,19 +1639,73 @@ jsdService::Off (void)
|
||||
jsdProperty::InvalidateAll();
|
||||
ClearAllBreakpoints();
|
||||
|
||||
JSD_SetThrowHook (mCx, NULL, NULL);
|
||||
JSD_ClearThrowHook (mCx);
|
||||
JSD_SetScriptHook (mCx, NULL, NULL);
|
||||
JSD_SetInterruptHook (mCx, NULL, NULL);
|
||||
JSD_SetDebuggerHook (mCx, NULL, NULL);
|
||||
JSD_SetDebugBreakHook (mCx, NULL, NULL);
|
||||
JSD_SetTopLevelHook (mCx, NULL, NULL);
|
||||
JSD_SetFunctionHook (mCx, NULL, NULL);
|
||||
JSD_ClearInterruptHook (mCx);
|
||||
JSD_ClearDebuggerHook (mCx);
|
||||
JSD_ClearDebugBreakHook (mCx);
|
||||
JSD_ClearTopLevelHook (mCx);
|
||||
JSD_ClearFunctionHook (mCx);
|
||||
|
||||
JSD_DebuggerOff (mCx);
|
||||
|
||||
mCx = nsnull;
|
||||
mRuntime = nsnull;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1285,14 +1716,12 @@ jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
|
||||
|
||||
JSDScript *script;
|
||||
JSDScript *iter = NULL;
|
||||
PRBool cont_flag;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
JSD_LockScriptSubsystem(mCx);
|
||||
while((script = JSD_IterateScripts(mCx, &iter)) != NULL) {
|
||||
rv = enumerator->EnumerateScript (jsdScript::FromPtr(mCx, script),
|
||||
&cont_flag);
|
||||
if (NS_FAILED(rv) || !cont_flag)
|
||||
rv = enumerator->EnumerateScript (jsdScript::FromPtr(mCx, script));
|
||||
if (NS_FAILED(rv))
|
||||
break;
|
||||
}
|
||||
JSD_UnlockScriptSubsystem(mCx);
|
||||
@ -1310,6 +1739,171 @@ jsdService::GC (void)
|
||||
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 (¤t->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 (¤t->links));
|
||||
PR_REMOVE_AND_INIT_LINK(¤t->links);
|
||||
jsds_FreeFilter(current);
|
||||
current = next;
|
||||
} while (current != gFilters);
|
||||
|
||||
jsds_FreeFilter(current);
|
||||
gFilters = nsnull;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
jsdService::ClearAllBreakpoints (void)
|
||||
{
|
||||
@ -1342,9 +1936,10 @@ jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval)
|
||||
if (stack && NS_SUCCEEDED(stack->Push(nsnull)) &&
|
||||
NS_SUCCEEDED(eventService->PushThreadEventQueue(getter_AddRefs(eventQ))))
|
||||
{
|
||||
if (NS_SUCCEEDED(rv) && callback)
|
||||
{
|
||||
if (NS_SUCCEEDED(rv) && callback) {
|
||||
Pause(nsnull);
|
||||
rv = callback->OnNest();
|
||||
UnPause(nsnull);
|
||||
}
|
||||
|
||||
while(NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel)
|
||||
@ -1355,10 +1950,8 @@ jsdService::EnterNestedEventLoop (jsdINestCallback *callback, PRUint32 *_rval)
|
||||
|
||||
rv = appShell->GetNativeEvent(isRealEvent, data);
|
||||
if(NS_SUCCEEDED(rv))
|
||||
{
|
||||
appShell->DispatchNativeEvent(isRealEvent, data);
|
||||
}
|
||||
}
|
||||
JSContext* cx;
|
||||
stack->Pop(&cx);
|
||||
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
|
||||
* OnForRuntime() method will do the rest when the coast is clear.
|
||||
*/
|
||||
if (!mCx)
|
||||
if (!mCx || mPauseLevel)
|
||||
return NS_OK;
|
||||
|
||||
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
|
||||
* OnForRuntime() method will do the rest when the coast is clear.
|
||||
*/
|
||||
if (!mCx)
|
||||
if (!mCx || mPauseLevel)
|
||||
return NS_OK;
|
||||
|
||||
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
|
||||
* OnForRuntime() method will do the rest when the coast is clear.
|
||||
*/
|
||||
if (!mCx)
|
||||
if (!mCx || mPauseLevel)
|
||||
return NS_OK;
|
||||
|
||||
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
|
||||
* OnForRuntime() method will do the rest when the coast is clear.
|
||||
*/
|
||||
if (!mCx)
|
||||
if (!mCx || mPauseLevel)
|
||||
return NS_OK;
|
||||
|
||||
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
|
||||
* OnForRuntime() method will do the rest when the coast is clear.
|
||||
*/
|
||||
if (!mCx)
|
||||
if (!mCx || mPauseLevel)
|
||||
return NS_OK;
|
||||
|
||||
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
|
||||
* OnForRuntime() method will do the rest when the coast is clear.
|
||||
*/
|
||||
if (!mCx)
|
||||
if (!mCx || mPauseLevel)
|
||||
return NS_OK;
|
||||
|
||||
if (aHook)
|
||||
JSD_SetTopLevelHook (mCx, jsds_CallHookProc, NULL);
|
||||
else
|
||||
JSD_SetTopLevelHook (mCx, jsds_EmptyCallHookProc, NULL);
|
||||
JSD_ClearTopLevelHook (mCx);
|
||||
|
||||
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
|
||||
* OnForRuntime() method will do the rest when the coast is clear.
|
||||
*/
|
||||
if (!mCx)
|
||||
if (!mCx || mPauseLevel)
|
||||
return NS_OK;
|
||||
|
||||
if (aHook)
|
||||
JSD_SetFunctionHook (mCx, jsds_CallHookProc, NULL);
|
||||
else
|
||||
JSD_SetFunctionHook (mCx, jsds_EmptyCallHookProc, NULL);
|
||||
JSD_ClearFunctionHook (mCx);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1616,8 +2209,55 @@ 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[] = {
|
||||
{"JSDService", JSDSERVICE_CID, jsdServiceCtrID, jsdServiceConstructor}
|
||||
{"JSDService", JSDSERVICE_CID, jsdServiceCtrID, jsdServiceConstructor},
|
||||
{"JSDASObserver", JSDASO_CID, jsdASObserverCtrID, jsdASObserverConstructor}
|
||||
};
|
||||
|
||||
NS_IMPL_NSGETMODULE(JavaScript_Debugger, components);
|
||||
|
||||
@ -285,20 +285,33 @@ class jsdService : public jsdIDebuggerService
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_JSDIDEBUGGERSERVICE
|
||||
|
||||
jsdService() : mOn(PR_FALSE), mNestedLoopLevel(0), mCx(0), mRuntime(0),
|
||||
mBreakpointHook(0), mErrorHook(0), mDebuggerHook(0),
|
||||
mInterruptHook(0), mScriptHook(0), mThrowHook(0),
|
||||
mTopLevelHook(0), mFunctionHook(0)
|
||||
jsdService() : mInitAtStartup(triUnknown), mOn(PR_FALSE), mPauseLevel(0),
|
||||
mNestedLoopLevel(0), mCx(0), mRuntime(0), mBreakpointHook(0),
|
||||
mErrorHook(0), mDebuggerHook(0), mInterruptHook(0),
|
||||
mScriptHook(0), mThrowHook(0), mTopLevelHook(0),
|
||||
mFunctionHook(0)
|
||||
{
|
||||
NS_INIT_ISUPPORTS();
|
||||
}
|
||||
|
||||
virtual ~jsdService() { }
|
||||
virtual ~jsdService()
|
||||
{
|
||||
ClearFilters();
|
||||
Off();
|
||||
}
|
||||
|
||||
static jsdService *GetService ();
|
||||
|
||||
private:
|
||||
enum Tristate {
|
||||
triUnknown = 0U,
|
||||
triYes = 1U,
|
||||
triNo = 2U
|
||||
};
|
||||
|
||||
Tristate mInitAtStartup;
|
||||
PRBool mOn;
|
||||
PRUint32 mPauseLevel;
|
||||
PRUint32 mNestedLoopLevel;
|
||||
JSDContext *mCx;
|
||||
JSRuntime *mRuntime;
|
||||
|
||||
@ -550,6 +550,13 @@ JSD_GetStackFrame(JSDContext* jsdc, JSDThreadState* 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_GetCallingStackFrame(JSDContext* jsdc,
|
||||
JSDThreadState* jsdthreadstate,
|
||||
|
||||
@ -723,6 +723,12 @@ JSD_GetCountOfStackFrames(JSDContext* jsdc, JSDThreadState* jsdthreadstate);
|
||||
extern JSD_PUBLIC_API(JSDStackFrameInfo*)
|
||||
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
|
||||
*/
|
||||
|
||||
@ -25,13 +25,15 @@
|
||||
#//------------------------------------------------------------------------
|
||||
DEPTH=..\..
|
||||
|
||||
REQUIRES = xpcom \
|
||||
REQUIRES = js \
|
||||
!if !defined(JSD_STANDALONE)
|
||||
xpcom \
|
||||
string \
|
||||
js \
|
||||
xpconnect \
|
||||
widget \
|
||||
appshell \
|
||||
pref \
|
||||
dom \
|
||||
!endif
|
||||
$(NULL)
|
||||
|
||||
include <$(DEPTH)\config\config.mak>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user