/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is The JavaScript Debugger. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, , original author * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const JSD_CTRID = "@mozilla.org/js/jsd/debugger-service;1"; const jsdIDebuggerService = Components.interfaces.jsdIDebuggerService; const jsdIExecutionHook = Components.interfaces.jsdIExecutionHook; const jsdIErrorHook = Components.interfaces.jsdIErrorHook; const jsdICallHook = Components.interfaces.jsdICallHook; const jsdIValue = Components.interfaces.jsdIValue; const jsdIProperty = Components.interfaces.jsdIProperty; const jsdIScript = Components.interfaces.jsdIScript; const jsdIStackFrame = Components.interfaces.jsdIStackFrame; const TYPE_VOID = jsdIValue.TYPE_VOID; const TYPE_NULL = jsdIValue.TYPE_NULL; const TYPE_BOOLEAN = jsdIValue.TYPE_BOOLEAN; const TYPE_INT = jsdIValue.TYPE_INT; const TYPE_DOUBLE = jsdIValue.TYPE_DOUBLE; const TYPE_STRING = jsdIValue.TYPE_STRING; const TYPE_FUNCTION = jsdIValue.TYPE_FUNCTION; const TYPE_OBJECT = jsdIValue.TYPE_OBJECT; const PROP_ENUMERATE = jsdIProperty.FLAG_ENUMERATE; const PROP_READONLY = jsdIProperty.FLAG_READONLY; const PROP_PERMANENT = jsdIProperty.FLAG_PERMANENT; const PROP_ALIAS = jsdIProperty.FLAG_ALIAS; const PROP_ARGUMENT = jsdIProperty.FLAG_ARGUMENT; const PROP_VARIABLE = jsdIProperty.FLAG_VARIABLE; const PROP_EXCEPTION = jsdIProperty.FLAG_EXCEPTION; const PROP_ERROR = jsdIProperty.FLAG_ERROR; const PROP_HINTED = jsdIProperty.FLAG_HINTED; const SCRIPT_NODEBUG = jsdIScript.FLAG_DEBUG; const SCRIPT_NOPROFILE = jsdIScript.FLAG_PROFILE; const COLLECT_PROFILE_DATA = jsdIDebuggerService.COLLECT_PROFILE_DATA; const PCMAP_SOURCETEXT = jsdIScript.PCMAP_SOURCETEXT; const PCMAP_PRETTYPRINT = jsdIScript.PCMAP_PRETTYPRINT; const RETURN_CONTINUE = jsdIExecutionHook.RETURN_CONTINUE; const RETURN_CONT_THROW = jsdIExecutionHook.RETURN_CONTINUE_THROW; const RETURN_VALUE = jsdIExecutionHook.RETURN_RET_WITH_VAL; const RETURN_THROW = jsdIExecutionHook.RETURN_THROW_WITH_VAL; const FTYPE_STD = 0; const FTYPE_SUMMARY = 1; const FTYPE_ARRAY = 2; const BREAKPOINT_STOPNEVER = 0; const BREAKPOINT_STOPALWAYS = 1; const BREAKPOINT_STOPTRUE = 2; const BREAKPOINT_EARLYRETURN = 3; var $ = new Array(); /* array to store results from evals in debug frames */ function compareVersion(maj, min) { if (console.jsds.implementationMajor < maj) return -1; if (console.jsds.implementationMajor > maj) return 1; if (console.jsds.implementationMinor < min) return -1; if (console.jsds.implementationMinor > min) return 1; return 0; } function initDebugger() { dd ("initDebugger {"); console.instanceSequence = 0; console._continueCodeStack = new Array(); /* top of stack is the default */ /* return code for the most */ /* recent debugTrap(). */ console.scriptWrappers = new Object(); console.scriptManagers = new Object(); console.breaks = new Object(); console.fbreaks = new Object(); console.sbreaks = new Object(); /* create the debugger instance */ if (!(JSD_CTRID in Components.classes)) throw new BadMojo (ERR_NO_DEBUGGER); console.jsds = Components.classes[JSD_CTRID].getService(jsdIDebuggerService); console.jsds.on(); if (compareVersion(1, 2) >= 0) console.jsds.flags = jsdIDebuggerService.DISABLE_OBJECT_TRACE; console.executionHook = { onExecute: jsdExecutionHook }; console.errorHook = { onError: jsdErrorHook }; console.callHook = { onCall: jsdCallHook }; console.jsdConsole = console.jsds.wrapValue(console); dispatch ("tmode", {mode: console.prefs["lastThrowMode"]}); dispatch ("emode", {mode: console.prefs["lastErrorMode"]}); var enumer = { enumerateScript: console.scriptHook.onScriptCreated }; console.jsds.scriptHook = console.scriptHook; console.jsds.enumerateScripts(enumer); console.jsds.breakpointHook = console.executionHook; console.jsds.debuggerHook = console.executionHook; console.jsds.debugHook = console.executionHook; console.jsds.errorHook = console.errorHook; console.jsds.flags = jsdIDebuggerService.ENABLE_NATIVE_FRAMES; dd ("} initDebugger"); } function detachDebugger() { if ("frames" in console) console.jsds.exitNestedEventLoop(); var b; for (b in console.breaks) console.breaks[b].clearBreakpoint(); for (b in console.fbreaks) console.fbreaks[b].clearFutureBreakpoint(); console.jsds.topLevelHook = null; console.jsds.functionHook = null; console.jsds.breakpointHook = null; console.jsds.debuggerHook = null; console.jsds.errorHook = null; console.jsds.scriptHook = null; console.jsds.interruptHook = null; console.jsds.clearAllBreakpoints(); console.jsds.GC(); if (!console.jsds.initAtStartup) console.jsds.off(); } console.scriptHook = new Object(); console.scriptHook.onScriptCreated = function sh_created (jsdScript) { try { jsdScriptCreated(jsdScript); } catch (ex) { dd ("caught " + dumpObjectTree(ex) + " while creating script."); } } console.scriptHook.onScriptDestroyed = function sh_destroyed (jsdScript) { try { jsdScriptDestroyed(jsdScript); } catch (ex) { dd ("caught " + dumpObjectTree(ex) + " while destroying script."); } } function jsdScriptCreated (jsdScript) { var url = jsdScript.fileName; var manager; if (!(url in console.scriptManagers)) { manager = console.scriptManagers[url] = new ScriptManager(url); //dispatchCommand (console.coManagerCreated, { scriptManager: manager }); } else { manager = console.scriptManagers[url]; } manager.onScriptCreated(jsdScript); } function jsdScriptDestroyed (jsdScript) { if (!(jsdScript.tag in console.scriptWrappers)) return; var scriptWrapper = console.scriptWrappers[jsdScript.tag]; scriptWrapper.scriptManager.onScriptInvalidated(scriptWrapper); if (scriptWrapper.scriptManager.instances.length == 0 && scriptWrapper.scriptManager.transientCount == 0) { delete console.scriptManagers[scriptWrapper.scriptManager.url]; //dispatchCommand (console.coManagerDestroyed, // { scriptManager: scriptWrapper.scriptManager }); } } function jsdExecutionHook (frame, type, rv) { dd ("execution hook: " + formatFrame(frame)); var hookReturn = jsdIExecutionHook.RETURN_CONTINUE; if (!console.initialized) return hookReturn; if (!ASSERT(!("frames" in console), "Execution hook called while stopped") || frame.isNative || !ASSERT(frame.script, "Execution hook called with no script") || frame.script.fileName == MSG_VAL_CONSOLE || !ASSERT(!(frame.script.flags & SCRIPT_NODEBUG), "Stopped in a script marked as don't debug") || !ASSERT(isURLVenkman(frame.script.fileName) || !isURLFiltered(frame.script.fileName), "stopped in a filtered URL")) { return hookReturn; } var frames = new Array(); var prevFrame = frame; var hasDisabledFrame = false; while (prevFrame) { frames.push(prevFrame); prevFrame = prevFrame.callingFrame; } var targetWindow = null; var wasModal = false; var cx; try { cx = frame.executionContext; } catch (ex) { dd ("no context"); cx = null; } var targetWasEnabled = true; var debuggerWasEnabled = console.baseWindow.enabled; console.baseWindow.enabled = true; if (!ASSERT(cx, "no cx in execution hook")) return hookReturn; var glob = cx.globalObject; if (!ASSERT(glob, "no glob in execution hook")) return hookReturn; console.targetWindow = getBaseWindowFromWindow(glob.getWrappedValue()); targetWasEnabled = console.targetWindow.enabled; if (console.targetWindow != console.baseWindow) { cx.scriptsEnabled = false; console.targetWindow.enabled = false; } try { //dd ("debug trap " + formatFrame(frame)); hookReturn = debugTrap(frames, type, rv); //dd ("debug trap returned " + hookReturn); } catch (ex) { display (MSG_ERR_INTERNAL_BPT, MT_ERROR); display (formatException(ex), MT_ERROR); } if (console.targetWindow && console.targetWindow != console.baseWindow) { console.targetWindow.enabled = targetWasEnabled; cx.scriptsEnabled = true; } console.baseWindow.enabled = debuggerWasEnabled; delete console.frames; delete console.targetWindow; if ("__exitAfterContinue__" in console) window.close(); return hookReturn; } function jsdCallHook (frame, type) { if (!console.initialized) return; if (type == jsdICallHook.TYPE_FUNCTION_CALL) { setStopState(false); //dd ("Calling: " + frame.functionName); } else if (type == jsdICallHook.TYPE_FUNCTION_RETURN) { // we're called *before* the returning frame is popped from the // stack, so we want our depth calculation to be off by one. var depth = -1; var prevFrame = frame; while (prevFrame) { depth++; prevFrame = prevFrame.callingFrame; } //dd ("Returning: " + frame.functionName + // ", target depth: " + console._stepOverDepth + // ", current depth: " + depth); if (depth <= console._stepOverDepth) { //dd ("step over at target depth of " + depth); setStopState(true); console.jsds.functionHook = null; delete console._stepOverDepth; } } } function jsdErrorHook (message, fileName, line, pos, flags, exception) { if (!console.initialized || isURLFiltered (fileName)) return true; try { var flagstr; flagstr = (flags && jsdIErrorHook.REPORT_EXCEPTION) ? "x" : "-"; flagstr += (flags && jsdIErrorHook.REPORT_STRICT) ? "s" : "-"; //dd ("===\n" + message + "\n" + fileName + "@" + // line + ":" + pos + "; " + flagstr); var msn = (flags & jsdIErrorHook.REPORT_WARNING) ? MSN_ERPT_WARN : MSN_ERPT_ERROR; if (console.errorMode != EMODE_IGNORE) display (getMsg(msn, [message, flagstr, fileName, line, pos]), MT_ETRACE); if (console.errorMode == EMODE_BREAK) return false; return true; } catch (ex) { dd ("error in error hook: " + ex); } return true; } function ScriptManager (url) { this.url = url; this.instances = new Array(); this.transients = new Object(); this.transientCount = 0; this.disableTransients = isURLFiltered(url); } ScriptManager.prototype.onScriptCreated = function smgr_created (jsdScript) { var instance; if (!ASSERT(jsdScript.isValid, "invalid script created!")) return; if (this.instances.length != 0) instance = this.instances[this.instances.length - 1]; if (!instance || (instance.isSealed && jsdScript.functionName)) { //dd ("instance created for " + jsdScript.fileName); instance = new ScriptInstance(this); instance.sequence = console.instanceSequence++; this.instances.push(instance); dispatchCommand (console.coInstanceCreated, { scriptInstance: instance }); } if ("_lastScriptWrapper" in console) { if ((console._lastScriptWrapper.scriptManager != this || console._lastScriptWrapper.scriptInstance != instance) && console._lastScriptWrapper.scriptInstance.scriptCount && !console._lastScriptWrapper.scriptInstance.isSealed) { console._lastScriptWrapper.scriptInstance.seal(); } } var scriptWrapper = new ScriptWrapper(jsdScript); console._lastScriptWrapper = scriptWrapper; scriptWrapper.scriptManager = this; console.scriptWrappers[jsdScript.tag] = scriptWrapper; scriptWrapper.scriptInstance = instance; if (!instance.isSealed) { //dd ("function created " + formatScript(jsdScript)); instance.onScriptCreated (scriptWrapper); } else { //dd ("transient created " + formatScript(jsdScript)); ++this.transientCount; if (this.disableTransients) jsdScript.flags |= SCRIPT_NODEBUG | SCRIPT_NOPROFILE; this.transients[jsdScript.tag] = scriptWrapper; scriptWrapper.functionName = MSG_VAL_EVSCRIPT; //dispatch ("hook-transient-script", { scriptWrapper: scriptWrapper }); } } ScriptManager.prototype.onScriptInvalidated = function smgr_invalidated (scriptWrapper) { //dd ("script invalidated"); delete console.scriptWrappers[scriptWrapper.tag]; if (scriptWrapper.tag in this.transients) { //dd ("transient destroyed " + formatScript(scriptWrapper.jsdScript)); --this.transientCount; delete this.transients[scriptWrapper.tag]; //dispatch ("hook-script-invalidated", { scriptWrapper: scriptWrapper }); } else { //dd ("function destroyed " + formatScript(scriptWrapper.jsdScript)); scriptWrapper.scriptInstance.onScriptInvalidated(scriptWrapper); //dispatch ("hook-script-invalidated", { scriptWrapper: scriptWrapper }); if (scriptWrapper.scriptInstance.scriptCount == 0) { var i = arrayIndexOf(this.instances, scriptWrapper.scriptInstance); arrayRemoveAt(this.instances, i); dispatchCommand (console.coInstanceDestroyed, { scriptInstance: scriptWrapper.scriptInstance }); } } } ScriptManager.prototype.__defineGetter__ ("sourceText", smgr_sourcetext); function smgr_sourcetext() { return this.instances[this.instances.length - 1].sourceText; } ScriptManager.prototype.__defineGetter__ ("lineMap", smgr_linemap); function smgr_linemap() { return this.instances[this.instances.length - 1].lineMap; } ScriptManager.prototype.getInstanceBySequence = function smgr_bysequence (seq) { for (var i = 0; i < this.instances.length; ++i) { if (this.instances[i].sequence == seq) return this.instances[i]; } return null; } ScriptManager.prototype.isLineExecutable = function smgr_isexe (line) { for (var i in this.instances) { if (this.instances[i].isLineExecutable(line)) return true; } return false; } ScriptManager.prototype.hasBreakpoint = function smgr_hasbp (line) { for (var i in this.instances) { if (this.instances[i].hasBreakpoint(line)) return true; } return false; } ScriptManager.prototype.setBreakpoint = function smgr_break (line, parentBP, props) { var found = false; for (var i in this.instances) found |= this.instances[i].setBreakpoint(line, parentBP, props); return found; } ScriptManager.prototype.clearBreakpoint = function smgr_break (line) { var found = false; for (var i in this.instances) found |= this.instances[i].clearBreakpoint(line); return found; } ScriptManager.prototype.hasFutureBreakpoint = function smgr_hasbp (line) { var key = this.url + "#" + line; return (key in console.fbreaks); } ScriptManager.prototype.getFutureBreakpoint = function smgr_getfbp (line) { return getFutureBreakpoint (this.url, line); } ScriptManager.prototype.noteFutureBreakpoint = function smgr_fbreak (line, state) { for (var i in this.instances) { if (this.instances[i]._lineMapInited) { if (state) { arrayOrFlag (this.instances[i]._lineMap, line - 1, LINE_FBREAK); } else { arrayAndFlag (this.instances[i]._lineMap, line - 1, ~LINE_FBREAK); } } } } function ScriptInstance (manager) { this.scriptManager = manager; this.url = manager.url; this.creationDate = new Date(); this.topLevel = null; this.functions = new Object(); this.nestLevel = 0; this.isSealed = false; this.scriptCount = 0; this.breakpointCount = 0; this.disabledScripts = 0; this._lineMap = new Array(); this._lineMapInited = false; } ScriptInstance.prototype.scanForMetaComments = function si_scan (start) { const CHUNK_SIZE = 500; const CHUNK_DELAY = 100; var scriptInstance = this; var sourceText = this.sourceText; function onSourceLoaded(result) { if (result == Components.results.NS_OK) scriptInstance.scanForMetaComments(); }; if (!sourceText.isLoaded) { sourceText.loadSource(onSourceLoaded); return; } if (typeof start == "undefined") start = 0; var end = Math.min (sourceText.lines.length, start + CHUNK_SIZE); var obj = new Object(); for (var i = start; i < end; ++i) { var ary = sourceText.lines[i].match (/\/\/@(\S+)(.*)/); if (ary && ary[1] in console.metaDirectives && !(ary[1] in obj)) { try { console.metaDirectives[ary[1]](scriptInstance, i + 1, ary); } catch (ex) { display (getMsg(MSN_ERR_META_FAILED, [ary[1], this.url, i + 1]), MT_ERROR); display (formatException (ex), MT_ERROR); } } } if (i != sourceText.lines.length) setTimeout (this.scanForMetaComments, CHUNK_DELAY, i); } ScriptInstance.prototype.seal = function si_seal () { this.sealDate = new Date(); this.isSealed = true; if (isURLFiltered(this.url)) { this.disabledScripts = 1; var nada = SCRIPT_NODEBUG | SCRIPT_NOPROFILE; if (this.topLevel && this.topLevel.isValid) this.topLevel.jsdScript.flags |= nada; for (var f in this.functions) { if (this.functions[f].jsdScript.isValid) this.functions[f].jsdScript.flags |= nada; ++this.disabledScripts; } } dispatch ("hook-script-instance-sealed", { scriptInstance: this }); } ScriptInstance.prototype.onScriptCreated = function si_created (scriptWrapper) { var tag = scriptWrapper.jsdScript.tag; if (scriptWrapper.functionName) { this.functions[tag] = scriptWrapper; } else { this.topLevel = scriptWrapper; scriptWrapper.functionName = MSG_VAL_TLSCRIPT; scriptWrapper.addToLineMap(this._lineMap); //var dummy = scriptWrapper.sourceText; this.seal(); } ++this.scriptCount; } ScriptInstance.prototype.onScriptInvalidated = function si_invalidated (scriptWrapper) { //dd ("script invalidated"); scriptWrapper.clearBreakpoints(); --this.scriptCount; } ScriptInstance.prototype.__defineGetter__ ("sourceText", si_gettext); function si_gettext () { if (!("_sourceText" in this)) this._sourceText = new SourceText (this); return this._sourceText; } ScriptInstance.prototype.__defineGetter__ ("lineMap", si_linemap); function si_linemap() { if (!this._lineMapInited) { if (this.topLevel && this.topLevel.jsdScript.isValid) this.topLevel.addToLineMap(this._lineMap); for (var i in this.functions) { if (this.functions[i].jsdScript.isValid) this.functions[i].addToLineMap(this._lineMap); } for (var fbp in console.fbreaks) { var fbreak = console.fbreaks[fbp]; if (fbreak.url == this.url) arrayOrFlag (this._lineMap, fbreak.lineNumber - 1, LINE_FBREAK); } this._lineMapInited = true; } return this._lineMap; } ScriptInstance.prototype.isLineExecutable = function si_isexe (line) { if (this.topLevel && this.topLevel.jsdScript.isValid && this.topLevel.jsdScript.isLineExecutable (line, PCMAP_SOURCETEXT)) { return true; } for (var f in this.functions) { var jsdScript = this.functions[f].jsdScript; if (line >= jsdScript.baseLineNumber && line <= jsdScript.baseLineNumber + jsdScript.lineExtent && jsdScript.isLineExecutable (line, PCMAP_SOURCETEXT)) { return true; } } return false; } ScriptInstance.prototype.hasBreakpoint = function si_hasbp (line) { return Boolean (this.getBreakpoint(line)); } ScriptInstance.prototype.getBreakpoint = function si_getbp (line) { for (var b in console.breaks) { if (console.breaks[b].scriptWrapper.scriptInstance == this) { if (typeof line == "undefined") return true; var jsdScript = console.breaks[b].scriptWrapper.jsdScript; if (jsdScript.pcToLine(console.breaks[b].pc, PCMAP_SOURCETEXT) == line) { return console.breaks[b]; } } } return false; } ScriptInstance.prototype.setBreakpoint = function si_setbp (line, parentBP, props) { function setBP (scriptWrapper) { if (!scriptWrapper.jsdScript.isValid) return false; var jsdScript = scriptWrapper.jsdScript; if (line >= jsdScript.baseLineNumber && line <= jsdScript.baseLineNumber + jsdScript.lineExtent && (jsdScript.isLineExecutable (line, PCMAP_SOURCETEXT) || jsdScript.baseLineNumber == line)) { var pc = jsdScript.lineToPc(line, PCMAP_SOURCETEXT); scriptWrapper.setBreakpoint(pc, parentBP, props); return true; } return false; }; var found; if (this.topLevel) found = setBP (this.topLevel); for (var f in this.functions) found |= setBP (this.functions[f]); if (this._lineMapInited && found) arrayOrFlag(this._lineMap, line - 1, LINE_BREAK); return found; } ScriptInstance.prototype.clearBreakpoint = function si_setbp (line) { var found = false; function clearBP (scriptWrapper) { var jsdScript = scriptWrapper.jsdScript; if (!jsdScript.isValid) return; var pc = jsdScript.lineToPc(line, PCMAP_SOURCETEXT); if (line >= jsdScript.baseLineNumber && line <= jsdScript.baseLineNumber + jsdScript.lineExtent && scriptWrapper.hasBreakpoint(pc)) { found |= scriptWrapper.clearBreakpoint(pc); } }; if (this._lineMapInited) arrayAndFlag(this._lineMap, line - 1, ~LINE_BREAK); if (this.topLevel) clearBP (this.topLevel); for (var f in this.functions) clearBP (this.functions[f]); return found; } ScriptInstance.prototype.getScriptWrapperAtLine = function si_getscript (line) { var targetScript = null; var scriptWrapper; if (this.topLevel) { scriptWrapper = this.topLevel; if (line >= scriptWrapper.jsdScript.baseLineNumber && line <= scriptWrapper.jsdScript.baseLineNumber + scriptWrapper.jsdScript.lineExtent) { targetScript = scriptWrapper; } } for (var f in this.functions) { scriptWrapper = this.functions[f]; if ((line >= scriptWrapper.jsdScript.baseLineNumber && line <= scriptWrapper.jsdScript.baseLineNumber + scriptWrapper.jsdScript.lineExtent) && (!targetScript || scriptWrapper.jsdScript.lineExtent < targetScript.jsdScript.lineExtent)) { targetScript = scriptWrapper; } } return targetScript; } ScriptInstance.prototype.containsScriptTag = function si_contains (tag) { return ((this.topLevel && this.topLevel.tag == tag) || (tag in this.functions)); } ScriptInstance.prototype.guessFunctionNames = function si_guessnames () { var sourceLines = this._sourceText.lines; var context = console.prefs["guessContext"]; var pattern = new RegExp (console.prefs["guessPattern"]); var scanText; function getSourceContext (end) { var startLine = end - context; if (startLine < 0) startLine = 0; var text = ""; for (i = startLine; i <= targetLine; ++i) text += String(sourceLines[i]); var pos = text.lastIndexOf ("function"); if (pos != -1) text = text.substring(0, pos); return text; }; for (var i in this.functions) { var scriptWrapper = this.functions[i]; if (scriptWrapper.jsdScript.functionName != "anonymous") continue; var targetLine = scriptWrapper.jsdScript.baseLineNumber; if (targetLine > sourceLines.length) { dd ("not enough source to guess function at line " + targetLine); return; } scanText = getSourceContext(targetLine); var ary = scanText.match (pattern); if (ary) { if ("charset" in this._sourceText) ary[1] = toUnicode(ary[1], this._sourceText.charset); scriptWrapper.functionName = getMsg(MSN_FMT_GUESSEDNAME, ary[1]); } else { if ("guessFallback" in console) { var name = console.guessFallback(scriptWrapper, scanText); if (name) { scriptWrapper.functionName = getMsg(MSN_FMT_GUESSEDNAME, name); } } } } dispatch ("hook-guess-complete", { scriptInstance: this }); } function ScriptWrapper (jsdScript) { this.jsdScript = jsdScript; this.tag = jsdScript.tag; this.functionName = jsdScript.functionName; this.breakpointCount = 0; this._lineMap = null; this.breaks = new Object(); } ScriptWrapper.prototype.__defineGetter__ ("sourceText", sw_getsource); function sw_getsource () { if (!("_sourceText" in this)) { if (!this.jsdScript.isValid) return null; this._sourceText = new PPSourceText(this); } return this._sourceText; } ScriptWrapper.prototype.__defineGetter__ ("lineMap", sw_linemap); function sw_linemap () { if (!this._lineMap) this.addToLineMap(this._lineMap); return this._lineMap; } ScriptWrapper.prototype.hasBreakpoint = function sw_hasbp (pc) { var key = this.jsdScript.tag + ":" + pc; return key in console.breaks; } ScriptWrapper.prototype.getBreakpoint = function sw_hasbp (pc) { var key = this.jsdScript.tag + ":" + pc; if (key in console.breaks) return console.breaks[key]; return null; } ScriptWrapper.prototype.setBreakpoint = function sw_setbp (pc, parentBP, props) { var key = this.jsdScript.tag + ":" + pc; //dd ("setting breakpoint in " + this.functionName + " " + key); if (key in console.breaks) return null; var brk = new BreakInstance (parentBP, this, pc); if (props) brk.setProperties(props); console.breaks[key] = brk; this.breaks[key] = brk; if (parentBP) { parentBP.childrenBP[key] = brk; brk.lineNumber = parentBP.lineNumber; brk.url = parentBP.url; } if ("_sourceText" in this) { var line = this.jsdScript.pcToLine(brk.pc, PCMAP_PRETTYPRINT); arrayOrFlag (this._sourceText.lineMap, line - 1, LINE_BREAK); } ++this.scriptInstance.breakpointCount; ++this.breakpointCount; if (this.scriptInstance._lineMapInited) { line = this.jsdScript.pcToLine (pc, PCMAP_SOURCETEXT); arrayOrFlag (this.scriptInstance._lineMap, line - 1, LINE_BREAK); } dispatch ("hook-break-set", { breakWrapper: brk }); return brk; } ScriptWrapper.prototype.clearBreakpoints = function sw_clearbps () { var found = false; for (b in this.breaks) found |= this.clearBreakpoint(this.breaks[b].pc); return found; } ScriptWrapper.prototype.clearBreakpoint = function sw_clearbp (pc) { var key = this.jsdScript.tag + ":" + pc; if (!(key in console.breaks)) return false; var brk = console.breaks[key]; if ("propsWindow" in brk) brk.propsWindow.close(); delete console.breaks[key]; delete this.breaks[key]; if (brk.parentBP) delete brk.parentBP.childrenBP[key]; var line; if ("_sourceText" in this && this.jsdScript.isValid) { line = this.jsdScript.pcToLine(brk.pc, PCMAP_PRETTYPRINT); this._sourceText.lineMap[line - 1] &= ~LINE_BREAK; } --this.scriptInstance.breakpointCount; --this.breakpointCount; if (this.scriptInstance._lineMapInited) { if (this.jsdScript.isValid) { line = this.jsdScript.pcToLine (pc, PCMAP_SOURCETEXT); if (!this.scriptInstance.hasBreakpoint(line)) this.scriptInstance._lineMap[line - 1] &= ~LINE_BREAK; } else { /* script is gone, no way to find out where the break actually * was, so we have to redo the whole map. */ this.scriptInstance._lineMapInited = false; this.scriptInstance._lineMap.length = 0; var dummy = this.scriptInstance.lineMap; } } dispatch ("hook-break-clear", { breakWrapper: brk }); if (this.jsdScript.isValid) this.jsdScript.clearBreakpoint (pc); return true; } ScriptWrapper.prototype.addToLineMap = function sw_addmap (lineMap) { var jsdScript = this.jsdScript; var end = jsdScript.baseLineNumber + jsdScript.lineExtent; for (var i = jsdScript.baseLineNumber; i < end; ++i) { if (jsdScript.isLineExecutable(i, PCMAP_SOURCETEXT)) arrayOrFlag (lineMap, i - 1, LINE_BREAKABLE); } for (i in this.breaks) { var line = jsdScript.pcToLine(this.breaks[i].pc, PCMAP_SOURCETEXT); arrayOrFlag (lineMap, line - 1, LINE_BREAK); } } function getScriptWrapper(jsdScript) { if (!ASSERT(jsdScript, "getScriptWrapper: null jsdScript")) return null; var tag = jsdScript.tag; if (tag in console.scriptWrappers) return console.scriptWrappers[tag]; dd ("Can't find a wrapper for " + formatScript(jsdScript)); return null; } function BreakInstance (parentBP, scriptWrapper, pc) { this._enabled = true; this.parentBP = parentBP; this.scriptWrapper = scriptWrapper; this.pc = pc; this.url = scriptWrapper.jsdScript.fileName; this.lineNumber = scriptWrapper.jsdScript.pcToLine (pc, PCMAP_SOURCETEXT); this.oneTime = false; this.triggerCount = 0; scriptWrapper.jsdScript.setBreakpoint (pc); } BreakInstance.prototype.__defineGetter__ ("jsdURL", bi_getURL); function bi_getURL () { return ("x-jsd:break?url=" + encodeURIComponent(this.url) + "&lineNumber=" + this.lineNumber + "&conditionEnabled=" + this.conditionEnabled + "&condition=" + encodeURIComponent(this.condition) + "&passExceptions=" + this.passExceptions + "&logResult=" + this.logResult + "&resultAction=" + this.resultAction + "&enabled=" + this.enabled); } BreakInstance.prototype.getProperties = function bi_getprops() { var rv = new Object(); rv.enabled = this._enabled; if ("_conditionEnabled" in this) rv.conditionEnabled = this._conditionEnabled; if ("_condition" in this) rv.condition = this._condition; if ("_passExceptions" in this) rv.passExceptions = this._passExceptions; if ("_logResult" in this) rv.logResult = this._logResult; if ("_resultAction" in this) rv.resultAction = this._resultAction; return rv; } BreakInstance.prototype.setProperties = function bi_setprops(obj) { for (var p in obj) { if (p.search(/pc|url|lineNumber/) == -1) this[p] = obj[p]; } if ("propsWindow" in this) this.propsWindow.populateFromBreakpoint(); } BreakInstance.prototype.clearBreakpoint = function bi_clear() { this.scriptWrapper.clearBreakpoint(this.pc); } BreakInstance.prototype.__defineGetter__ ("enabled", bi_getEnabled); function bi_getEnabled () { return this._enabled; } BreakInstance.prototype.__defineSetter__ ("enabled", bi_setEnabled); function bi_setEnabled (state) { if (state != this._enabled) { this._enabled = state; if (state) this.scriptWrapper.jsdScript.setBreakpoint(this.pc); else this.scriptWrapper.jsdScript.clearBreakpoint(this.pc); } return state; } BreakInstance.prototype.__defineGetter__ ("conditionEnabled", bi_getCondEnabled); function bi_getCondEnabled () { if ("_conditionEnabled" in this) return this._conditionEnabled; if (this.parentBP) return this.parentBP.conditionEnabled; return false; } BreakInstance.prototype.__defineSetter__ ("conditionEnabled", bi_setCondEnabled); function bi_setCondEnabled (state) { if (this.parentBP) return this.parentBP.conditionEnabled = state; return this._conditionEnabled = state; } BreakInstance.prototype.__defineGetter__ ("condition", bi_getCondition); function bi_getCondition () { if ("_condition" in this) return this._condition; if (this.parentBP) return this.parentBP.condition; return ""; } BreakInstance.prototype.__defineSetter__ ("condition", bi_setCondition); function bi_setCondition (value) { if (this.parentBP) return this.parentBP.condition = value; return this._condition = value; } BreakInstance.prototype.__defineGetter__ ("passExceptions", bi_getException); function bi_getException () { if ("_passExceptions" in this) return this._passExceptions; if (this.parentBP) return this.parentBP.passExceptions; return false; } BreakInstance.prototype.__defineSetter__ ("passExceptions", bi_setException); function bi_setException (state) { if (this.parentBP) return this.parentBP.passExceptions = state; return this._passExceptions = state; } BreakInstance.prototype.__defineGetter__ ("logResult", bi_getLogResult); function bi_getLogResult () { if ("_logResult" in this) return this._logResult; if (this.parentBP) return this.parentBP.logResult; return false; } BreakInstance.prototype.__defineSetter__ ("logResult", bi_setLogResult); function bi_setLogResult (state) { if (this.parentBP) return this.parentBP.logResult = state; return this._logResult = state; } BreakInstance.prototype.__defineGetter__ ("resultAction", bi_getResultAction); function bi_getResultAction () { if ("_resultAction" in this) return this._resultAction; if (this.parentBP) return this.parentBP.resultAction; return BREAKPOINT_STOPALWAYS; } BreakInstance.prototype.__defineSetter__ ("resultAction", bi_setResultAction); function bi_setResultAction (state) { if (this.parentBP) return this.parentBP.resultAction = state; return this._resultAction = state; } function FutureBreakpoint (url, lineNumber) { this.url = url; this.lineNumber = lineNumber; this.enabled = true; this.childrenBP = new Object(); this.conditionEnabled = false; this.condition = ""; this.passExceptions = false; this.logResult = false; this.resultAction = BREAKPOINT_STOPALWAYS; } FutureBreakpoint.prototype.__defineGetter__ ("jsdURL", fb_getURL); function fb_getURL () { return ("x-jsd:fbreak?url=" + encodeURIComponent(this.url) + "&lineNumber=" + this.lineNumber + "&conditionEnabled=" + this.conditionEnabled + "&condition=" + encodeURIComponent(this.condition) + "&passExceptions=" + this.passExceptions + "&logResult=" + this.logResult + "&resultAction=" + this.resultAction + "&enabled=" + this.enabled); } FutureBreakpoint.prototype.getProperties = function fb_getprops() { var rv = new Object(); rv.conditionEnabled = this.conditionEnabled; rv.condition = this.condition; rv.passExceptions = this.passExceptions; rv.logResult = this.logResult; rv.resultAction = this.resultAction; return rv; } FutureBreakpoint.prototype.setProperties = function fb_setprops(obj) { for (var p in obj) { if (p.search(/url|lineNumber|childrenBP/) == -1) this[p] = obj[p]; } if ("propsWindow" in this) this.propsWindow.populateFromBreakpoint(); } FutureBreakpoint.prototype.clearFutureBreakpoint = function fb_clear () { clearFutureBreakpoint (this.url, this.lineNumber); } FutureBreakpoint.prototype.resetInstances = function fb_reseti () { for (var url in console.scriptManagers) { if (url.indexOf(this.url) != -1) console.scriptManagers[url].setBreakpoint(this.lineNumber); } } FutureBreakpoint.prototype.clearInstances = function fb_cleari () { for (var url in console.scriptManagers) { if (url.indexOf(this.url) != -1) console.scriptManagers[url].clearBreakpoint(this.lineNumber); } } function testBreakpoint(currentFrame, rv) { var tag = currentFrame.script.tag; if (!(tag in console.scriptWrappers)) return -1; var scriptWrapper = console.scriptWrappers[tag]; var breakpoint = scriptWrapper.getBreakpoint(currentFrame.pc); if (!ASSERT(breakpoint, "can't find breakpoint for " + formatFrame(currentFrame))) { return -1; } if (!ASSERT(breakpoint.enabled, "stopped at a disabled breakpoint?")) return RETURN_CONTINUE; ++breakpoint.triggerCount; if ("propsWindow" in breakpoint) breakpoint.propsWindow.onBreakpointTriggered(); if (breakpoint.oneTime) scriptWrapper.clearBreakpoint(currentFrame.pc); if (breakpoint.conditionEnabled && breakpoint.condition) { var result = new Object(); var script = "var __trigger__ = function (__count__) {" + breakpoint.condition + "}; __trigger__.apply(this, [" + breakpoint.triggerCount + "]);"; if (!currentFrame.eval (script, JSD_URL_SCHEME + "breakpoint-condition", 1, result)) { /* condition raised an exception */ if (breakpoint.passExceptions) { rv.value = result.value; return RETURN_THROW; } display (MSG_ERR_CONDITION_FAILED, MT_ERROR); display (formatException(result.value.getWrappedValue()), MT_ERROR); } else { /* condition executed ok */ if (breakpoint.logResult) { display (result.value.stringValue, MT_LOG); } if (breakpoint.resultAction == BREAKPOINT_EARLYRETURN) { rv.value = result.value; return RETURN_VALUE; } if (breakpoint.resultAction == BREAKPOINT_STOPNEVER || (breakpoint.resultAction == BREAKPOINT_STOPTRUE && !result.value.booleanValue)) { return RETURN_CONTINUE; } } } return -1; } const EMODE_IGNORE = 0; const EMODE_TRACE = 1; const EMODE_BREAK = 2; const TMODE_IGNORE = 0; const TMODE_TRACE = 1; const TMODE_BREAK = 2; function debugTrap (frames, type, rv) { var tn = ""; var retcode = jsdIExecutionHook.RETURN_CONTINUE; //dd ("debugTrap"); var frame = frames[0]; $ = new Array(); switch (type) { case jsdIExecutionHook.TYPE_BREAKPOINT: var bpResult = testBreakpoint(frame, rv); if (bpResult != -1) return bpResult; tn = MSG_VAL_BREAKPOINT; break; case jsdIExecutionHook.TYPE_DEBUG_REQUESTED: tn = MSG_VAL_DEBUG; break; case jsdIExecutionHook.TYPE_DEBUGGER_KEYWORD: tn = MSG_VAL_DEBUGGER; break; case jsdIExecutionHook.TYPE_THROW: dd (dumpObjectTree(rv)); display (getMsg(MSN_EXCEPTION_TRACE, [rv.value.stringValue, formatFrame(frame)]), MT_ETRACE); if (rv.value.jsClassName == "Error") display (formatProperty(rv.value.getProperty("message")), MT_EVAL_OUT); if (console.throwMode != TMODE_BREAK) return jsdIExecutionHook.RETURN_CONTINUE_THROW; console.currentException = rv.value; retcode = jsdIExecutionHook.RETURN_CONTINUE_THROW; tn = MSG_VAL_THROW; break; case jsdIExecutionHook.TYPE_INTERRUPTED: if (!frame.script.functionName && isURLFiltered(frame.script.fileName)) { //dd ("filtered url: " + frame.script.fileName); frame.script.flags |= SCRIPT_NOPROFILE | SCRIPT_NODEBUG; return retcode; } var line; if (console.prefs["prettyprint"]) line = frame.script.pcToLine (frame.pc, PCMAP_PRETTYPRINT); else line = frame.line; if (console._stepPast == frames.length + frame.script.fileName + line) { //dd("stepPast: " + console._stepPast); return retcode; } delete console._stepPast; setStopState(false); break; default: /* don't print stop/cont messages for other types */ } console.jsds.functionHook = null; /* set our default return value */ console._continueCodeStack.push (retcode); if (tn) display (getMsg(MSN_STOP, tn), MT_STOP); /* build an array of frames */ console.frames = frames; console.trapType = type; try { console.jsds.enterNestedEventLoop({onNest: eventLoopNested}); } catch (ex) { dd ("caught " + ex + " while nested"); } /* execution pauses here until someone calls exitNestedEventLoop() */ clearCurrentFrame(); rv.value = ("currentException" in console) ? console.currentException : null; delete console.frames; delete console.trapType; delete console.currentException; $ = new Array(); dispatch ("hook-debug-continue"); if (tn) display (getMsg(MSN_CONT, tn), MT_CONT); return console._continueCodeStack.pop(); } function eventLoopNested () { window.focus(); window.getAttention(); dispatch ("hook-debug-stop"); } function getCurrentFrame() { if ("frames" in console) return console.frames[console._currentFrameIndex]; return null; } function getCurrentFrameIndex() { if (typeof console._currentFrameIndex == "undefined") return -1; return console._currentFrameIndex; } function setCurrentFrameByIndex (index) { if (!console.frames) throw new BadMojo (ERR_NO_STACK); ASSERT (index >= 0 && index < console.frames.length, "index out of range"); console._currentFrameIndex = index; var cf = console.frames[console._currentFrameIndex]; dispatch ("set-eval-obj", { jsdValue: cf }); console.stopFile = (cf.isNative) ? MSG_URL_NATIVE : cf.script.fileName; console.stopLine = cf.line; delete console._pp_stopLine; return cf; } function clearCurrentFrame () { if (!console.frames) throw new BadMojo (ERR_NO_STACK); if (console.currentEvalObject instanceof jsdIStackFrame) dispatch ("set-eval-obj", { jsdValue: console.jsdConsole }); delete console.stopLine; delete console._pp_stopLine; delete console.stopFile; delete console._currentFrameIndex; } function formatArguments (v) { if (!v) return ""; var ary = new Array(); var p = new Object(); v.getProperties (p, {}); p = p.value; for (var i = 0; i < p.length; ++i) { if (p[i].flags & jsdIProperty.FLAG_ARGUMENT) ary.push (getMsg(MSN_FMT_ARGUMENT, [p[i].name.stringValue, formatValue(p[i].value, FTYPE_SUMMARY)])); } return ary.join (MSG_COMMASP); } function formatFlags (flags) { var s = ""; if (flags & PROP_ENUMERATE) s += MSG_VF_ENUMERABLE; if (flags & PROP_READONLY) s += MSG_VF_READONLY; if (flags & PROP_PERMANENT) s += MSG_VF_PERMANENT; if (flags & PROP_ALIAS) s += MSG_VF_ALIAS; if (flags & PROP_ARGUMENT) s += MSG_VF_ARGUMENT; if (flags & PROP_VARIABLE) s += MSG_VF_VARIABLE; if (flags & PROP_ERROR) s += MSG_VF_ERROR; if (flags & PROP_EXCEPTION) s += MSG_VF_EXCEPTION; if (flags & PROP_HINTED) s += MSG_VF_HINTED; return s; } function formatProperty (p, formatType) { if (!p) throw new BadMojo (ERR_REQUIRED_PARAM, "p"); var s = formatFlags (p.flags); if (formatType == FTYPE_ARRAY) { var rv = formatValue (p.value, FTYPE_ARRAY); return [p.name.stringValue, rv[1] ? rv[1] : rv[0], rv[2], s]; } return getMsg(MSN_FMT_PROPERTY, [s, p.name.stringValue, formatValue(p.value)]); } function formatScript (script) { if (!script) throw new BadMojo (ERR_REQUIRED_PARAM, "script"); var functionName; if (script.tag in console.scriptWrappers) functionName = console.scriptWrappers[script.tag].functionName; else functionName = script.functionName; return getMsg (MSN_FMT_SCRIPT, [functionName, script.fileName]); } function formatFrame (f) { if (!f) throw new BadMojo (ERR_REQUIRED_PARAM, "f"); var url = (f.isNative) ? MSG_URL_NATIVE : f.script.fileName; return getMsg (MSN_FMT_FRAME, [f.functionName, formatArguments(f.scope), url, f.line]); } function formatValue (v, formatType) { if (!v) throw new BadMojo (ERR_REQUIRED_PARAM, "v"); if (!(v instanceof jsdIValue)) throw new BadMojo (ERR_INVALID_PARAM, "v", String(v)); var type; var value; switch (v.jsType) { case jsdIValue.TYPE_BOOLEAN: type = MSG_TYPE_BOOLEAN; value = String(v.booleanValue); break; case jsdIValue.TYPE_DOUBLE: type = MSG_TYPE_DOUBLE; value = v.doubleValue; break; case jsdIValue.TYPE_INT: type = MSG_TYPE_INT; value = v.intValue; break; case jsdIValue.TYPE_FUNCTION: type = MSG_TYPE_FUNCTION; value = v.jsFunctionName; break; case jsdIValue.TYPE_NULL: type = MSG_TYPE_NULL; value = MSG_TYPE_NULL; break; case jsdIValue.TYPE_OBJECT: if (formatType == FTYPE_STD) { type = MSG_TYPE_OBJECT; value = getMsg(MSN_FMT_OBJECT, String(v.propertyCount)); } else { if (v.jsClassName) if (v.jsClassName == "XPCWrappedNative_NoHelper") type = MSG_CLASS_XPCOBJ; else type = v.jsClassName; else type = MSG_TYPE_OBJECT; value = "{" + String(v.propertyCount) + "}"; } break; case jsdIValue.TYPE_STRING: type = MSG_TYPE_STRING; var strval = v.stringValue; if (strval.length > console.prefs["maxStringLength"]) strval = getMsg(MSN_FMT_LONGSTR, strval.length); else strval = strval.quote() value = strval; break; case jsdIValue.TYPE_VOID: type = MSG_TYPE_VOID; value = MSG_TYPE_VOID; break; default: type = MSG_TYPE_UNKNOWN; value = MSG_TYPE_UNKNOWN; break; } if (formatType == FTYPE_SUMMARY) return getMsg (MSN_FMT_VALUE_SHORT, [type, value]); var className; if (v.jsClassName) if (v.jsClassName == "XPCWrappedNative_NoHelper") /* translate this long, unintuitive, and common class name into * something more palatable. */ className = MSG_CLASS_XPCOBJ; else className = v.jsClassName; if (formatType == FTYPE_ARRAY) return [type, className, value]; if (className) return getMsg (MSN_FMT_VALUE_LONG, [type, v.jsClassName, value]); return getMsg (MSN_FMT_VALUE_MED, [type, value]); } function displayCallStack () { for (var i = 0; i < console.frames.length; ++i) displayFrame (console.frames[i], i); } function displayProperties (v) { if (!v) throw new BadMojo (ERR_REQUIRED_PARAM, "v"); if (!(v instanceof jsdIValue)) throw new BadMojo (ERR_INVALID_PARAM, "v", String(v)); var p = new Object(); v.getProperties (p, {}); for (var i in p.value) display(formatProperty (p.value[i]), MT_EVAL_OUT); } function displaySourceContext (sourceText, line, contextLines) { function onSourceLoaded (status) { if (status == Components.results.NS_OK) displaySourceContext (sourceText, line, contextLines); } if (sourceText.isLoaded) { for (var i = line - contextLines; i <= line + contextLines; ++i) { if (i > 0 && i < sourceText.lines.length) { var sourceLine; if ("charset" in sourceText) { sourceLine = toUnicode(sourceText.lines[i - 1], sourceText.charset); } else { sourceLine = sourceText.lines[i - 1]; } display (getMsg(MSN_SOURCE_LINE, [zeroPad (i, 3), sourceLine]), i == line ? MT_STEP : MT_SOURCE); } } } else { sourceText.loadSource (onSourceLoaded); } } function displayFrame (jsdFrame, idx, showHeader, sourceContext) { if (typeof idx == "undefined") { for (idx = 0; idx < console.frames.length; ++idx) if (jsdFrame == console.frames[idx]) break; if (idx >= console.frames.length) idx = MSG_VAL_UNKNOWN; } if (typeof showHeader == "undefined") showHeader = true; if (typeof sourceContext == "undefined") sourceContext = null; display(getMsg(MSN_FMT_FRAME_LINE, [idx, formatFrame(jsdFrame)]), MT_OUTPUT); if (!jsdFrame.isNative && sourceContext != null) { var jsdScript = jsdFrame.script; var scriptWrapper = getScriptWrapper(jsdScript); if (!ASSERT(scriptWrapper, "Couldn't get a script wrapper")) return; if (console.prefs["prettyprint"] && jsdScript.isValid) { displaySourceContext (scriptWrapper.sourceText, jsdScript.pcToLine(jsdFrame.pc, PCMAP_PRETTYPRINT), sourceContext); } else { displaySourceContext (scriptWrapper.scriptInstance.sourceText, jsdFrame.line, sourceContext); } } } function getFutureBreakpoint (urlPattern, lineNumber) { var key = urlPattern + "#" + lineNumber; if (key in console.fbreaks) return console.fbreaks[key]; return null; } function setFutureBreakpoint (urlPattern, lineNumber, props) { var key = urlPattern + "#" + lineNumber; if (key in console.fbreaks) return false; var url; for (url in console.scriptManagers) { if (url == urlPattern) console.scriptManagers[url].noteFutureBreakpoint(lineNumber, true); } for (url in console.files) { if (url == urlPattern) console.files[url].noteFutureBreakpoint(lineNumber, true); } var fbreak = new FutureBreakpoint (urlPattern, lineNumber); if (props) fbreak.setProperties(props); console.fbreaks[key] = fbreak; dispatch ("hook-fbreak-set", { fbreak: fbreak }); return fbreak; } function clearFutureBreakpoint (urlPattern, lineNumber) { var key = urlPattern + "#" + lineNumber; if (!(key in console.fbreaks)) return false; var i; var fbreak = console.fbreaks[key]; if ("propsWindow" in fbreak) fbreak.propsWindow.close(); delete console.fbreaks[key]; for (i in fbreak.childrenBP) fbreak.childrenBP[i].parentBP = null; var url; for (url in console.scriptManagers) { if (url.indexOf(urlPattern) != -1) console.scriptManagers[url].noteFutureBreakpoint(lineNumber, false); } for (url in console.files) { if (url == urlPattern) console.files[url].noteFutureBreakpoint(lineNumber, false); } dispatch ("hook-fbreak-clear", { fbreak: fbreak }); return true; }