зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
0babafcaa0
Коммит
db43fadc93
|
@ -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(¤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
|
* 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 (¤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
|
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>
|
||||||
|
|
Загрузка…
Ссылка в новой задаче