/*
 * Copyright (C) 2013 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

WebInspector.DebuggerManager = function()
{
    WebInspector.Object.call(this);

    if (window.DebuggerAgent)
        DebuggerAgent.enable();

    WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisplayLocationDidChange, this._breakpointDisplayLocationDidChange, this);
    WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.DisabledStateDidChange, this._breakpointDisabledStateDidChange, this);
    WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ConditionDidChange, this._breakpointEditablePropertyDidChange, this);
    WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.AutoContinueDidChange, this._breakpointEditablePropertyDidChange, this);
    WebInspector.Breakpoint.addEventListener(WebInspector.Breakpoint.Event.ActionsDidChange, this._breakpointEditablePropertyDidChange, this);

    window.addEventListener("pagehide", this._inspectorClosing.bind(this));

    this._allExceptionsBreakpointEnabledSetting = new WebInspector.Setting("break-on-all-exceptions", false);
    this._allUncaughtExceptionsBreakpointEnabledSetting = new WebInspector.Setting("break-on-all-uncaught-exceptions", false);

    var specialBreakpointLocation = new WebInspector.SourceCodeLocation(null, Infinity, Infinity);

    this._allExceptionsBreakpoint = new WebInspector.Breakpoint(specialBreakpointLocation, !this._allExceptionsBreakpointEnabledSetting.value);
    this._allExceptionsBreakpoint.resolved = true;

    this._allUncaughtExceptionsBreakpoint = new WebInspector.Breakpoint(specialBreakpointLocation, !this._allUncaughtExceptionsBreakpointEnabledSetting.value);

    this._breakpoints = [];
    this._breakpointURLMap = {};
    this._breakpointScriptIdentifierMap = {};
    this._breakpointIdMap = {};

    this._scriptIdMap = {};
    this._scriptURLMap = {};

    this._breakpointsSetting = new WebInspector.Setting("breakpoints", []);
    this._breakpointsEnabledSetting = new WebInspector.Setting("breakpoints-enabled", true);

    if (window.DebuggerAgent)
        DebuggerAgent.setBreakpointsActive(this._breakpointsEnabledSetting.value);

    this._updateBreakOnExceptionsState();

    var savedBreakpoints = this._breakpointsSetting.value;
    for (var i = 0; i < savedBreakpoints.length; ++i) {
        var breakpoint = new WebInspector.Breakpoint(savedBreakpoints[i]);
        this.addBreakpoint(breakpoint, true);
    }
};

WebInspector.DebuggerManager.Event = {
    BreakpointAdded: "debugger-manager-breakpoint-added",
    BreakpointRemoved: "debugger-manager-breakpoint-removed",
    BreakpointMoved: "debugger-manager-breakpoint-moved",
    Paused: "debugger-manager-paused",
    Resumed: "debugger-manager-resumed",
    CallFramesDidChange: "debugger-manager-call-frames-did-change",
    ActiveCallFrameDidChange: "debugger-manager-active-call-frame-did-change",
    ScriptAdded: "debugger-manager-script-added",
    ScriptsCleared: "debugger-manager-scripts-cleared"
};

WebInspector.DebuggerManager.prototype = {
    constructor: WebInspector.DebuggerManager,

    // Public

    get breakpointsEnabled()
    {
        return this._breakpointsEnabledSetting.value;
    },

    set breakpointsEnabled(enabled)
    {
        if (this._breakpointsEnabled === enabled)
            return;

        this._breakpointsEnabledSetting.value = enabled;

        this._allExceptionsBreakpoint.dispatchEventToListeners(WebInspector.Breakpoint.Event.ResolvedStateDidChange);
        this._allUncaughtExceptionsBreakpoint.dispatchEventToListeners(WebInspector.Breakpoint.Event.ResolvedStateDidChange);

        for (var i = 0; i < this._breakpoints.length; ++i)
            this._breakpoints[i].dispatchEventToListeners(WebInspector.Breakpoint.Event.ResolvedStateDidChange);

        DebuggerAgent.setBreakpointsActive(enabled);

        this._updateBreakOnExceptionsState();
    },

    get paused()
    {
        return this._paused;
    },

    get callFrames()
    {
        return this._callFrames;
    },

    get activeCallFrame()
    {
        return this._activeCallFrame;
    },

    set activeCallFrame(callFrame)
    {
        if (callFrame === this._activeCallFrame)
            return;

        this._activeCallFrame = callFrame || null;

        this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange);
    },

    pause: function()
    {
        DebuggerAgent.pause();
    },

    resume: function()
    {
        DebuggerAgent.resume();
    },

    stepOver: function()
    {
        DebuggerAgent.stepOver();
    },

    stepInto: function()
    {
        DebuggerAgent.stepInto();
    },

    stepOut: function()
    {
        DebuggerAgent.stepOut();
    },

    get allExceptionsBreakpoint()
    {
        return this._allExceptionsBreakpoint;
    },

    get allUncaughtExceptionsBreakpoint()
    {
        return this._allUncaughtExceptionsBreakpoint;
    },

    get breakpoints()
    {
        return this._breakpoints;
    },

    breakpointsForSourceCode: function(sourceCode)
    {
        console.assert(sourceCode instanceof WebInspector.Resource || sourceCode instanceof WebInspector.Script);

        if (sourceCode instanceof WebInspector.SourceMapResource) {
            var mappedResourceBreakpoints = [];
            var originalSourceCodeBreakpoints = this.breakpointsForSourceCode(sourceCode.sourceMap.originalSourceCode);
            return originalSourceCodeBreakpoints.filter(function(breakpoint) {
                return breakpoint.sourceCodeLocation.displaySourceCode === sourceCode;
            });
        }

        if (sourceCode.url in this._breakpointURLMap) {
            var urlBreakpoint = this._breakpointURLMap[sourceCode.url] || [];
            this._associateBreakpointsWithSourceCode(urlBreakpoint, sourceCode);
            return urlBreakpoint;
        }

        if (sourceCode instanceof WebInspector.Script && sourceCode.id in this._breakpointScriptIdentifierMap) {
            var scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[sourceCode.id] || [];
            this._associateBreakpointsWithSourceCode(scriptIdentifierBreakpoints, sourceCode);
            return scriptIdentifierBreakpoints;
        }

        return [];
    },

    scriptForIdentifier: function(id)
    {
        return this._scriptIdMap[id] || null;
    },

    scriptsForURL: function(url)
    {
        // FIXME: This may not be safe. A Resource's URL may differ from a Script's URL.
        return this._scriptURLMap[url] || [];
    },

    continueToLocation: function(scriptIdentifier, lineNumber, columnNumber)
    {
        DebuggerAgent.continueToLocation({scriptId: scriptIdentifier, lineNumber: lineNumber, columnNumber: columnNumber});
    },

    addBreakpoint: function(breakpoint, skipEventDispatch)
    {
        console.assert(breakpoint);
        if (!breakpoint)
            return;

        if (breakpoint.url) {
            var urlBreakpoints = this._breakpointURLMap[breakpoint.url];
            if (!urlBreakpoints)
                urlBreakpoints = this._breakpointURLMap[breakpoint.url] = [];
            urlBreakpoints.push(breakpoint);
        }

        if (breakpoint.scriptIdentifier) {
            var scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier];
            if (!scriptIdentifierBreakpoints)
                scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier] = [];
            scriptIdentifierBreakpoints.push(breakpoint);
        }

        this._breakpoints.push(breakpoint);

        if (!breakpoint.disabled)
            this._setBreakpoint(breakpoint);

        if (!skipEventDispatch)
            this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointAdded, {breakpoint: breakpoint});
    },

    removeBreakpoint: function(breakpoint)
    {
        console.assert(breakpoint);
        if (!breakpoint)
            return;

        console.assert(this.isBreakpointRemovable(breakpoint));
        if (!this.isBreakpointRemovable(breakpoint))
            return;

        this._breakpoints.remove(breakpoint);

        if (breakpoint.id)
            this._removeBreakpoint(breakpoint);

        if (breakpoint.url) {
            var urlBreakpoints = this._breakpointURLMap[breakpoint.url];
            if (urlBreakpoints) {
                urlBreakpoints.remove(breakpoint);
                if (!urlBreakpoints.length)
                    delete this._breakpointURLMap[breakpoint.url];
            }
        }

        if (breakpoint.scriptIdentifier) {
            var scriptIdentifierBreakpoints = this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier];
            if (scriptIdentifierBreakpoints) {
                scriptIdentifierBreakpoints.remove(breakpoint);
                if (!scriptIdentifierBreakpoints.length)
                    delete this._breakpointScriptIdentifierMap[breakpoint.scriptIdentifier];
            }
        }

        this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointRemoved, {breakpoint: breakpoint});
    },

    breakpointResolved: function(breakpointIdentifier, location)
    {
        // Called from WebInspector.DebuggerObserver.

        var breakpoint = this._breakpointIdMap[breakpointIdentifier];
        console.assert(breakpoint);
        if (!breakpoint)
            return;

        console.assert(breakpoint.id === breakpointIdentifier);

        breakpoint.resolved = true;
    },

    reset: function()
    {
        // Called from WebInspector.DebuggerObserver.

        var wasPaused = this._paused;

        WebInspector.Script.resetUniqueDisplayNameNumbers();

        this._paused = false;
        this._scriptIdMap = {};
        this._scriptURLMap = {};

        this._ignoreBreakpointDisplayLocationDidChangeEvent = true;

        // Mark all the breakpoints as unresolved. They will be reported as resolved when
        // breakpointResolved is called as the page loads.
        for (var i = 0; i < this._breakpoints.length; ++i) {
            var breakpoint = this._breakpoints[i];
            breakpoint.resolved = false;
            if (breakpoint.sourceCodeLocation.sourceCode)
                breakpoint.sourceCodeLocation.sourceCode = null;
        }

        delete this._ignoreBreakpointDisplayLocationDidChangeEvent;

        this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ScriptsCleared);

        if (wasPaused)
            this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.Resumed);
    },

    debuggerDidPause: function(callFramesPayload)
    {
        // Called from WebInspector.DebuggerObserver.

        if (this._delayedResumeTimeout) {
            clearTimeout(this._delayedResumeTimeout);
            delete this._delayedResumeTimeout;
        }

        var wasStillPaused = this._paused;

        this._paused = true;
        this._callFrames = [];

        for (var i = 0; i < callFramesPayload.length; ++i) {
            var callFramePayload = callFramesPayload[i];
            var sourceCodeLocation = this._sourceCodeLocationFromPayload(callFramePayload.location);
            // Exclude the case where the call frame is in the inspector code.
            if (!sourceCodeLocation || !sourceCodeLocation._sourceCode || !sourceCodeLocation._sourceCode._url || sourceCodeLocation._sourceCode._url.indexOf("__WebInspector") === 0)
                continue;
            var thisObject = WebInspector.RemoteObject.fromPayload(callFramePayload.this);
            var scopeChain = this._scopeChainFromPayload(callFramePayload.scopeChain);
            var callFrame = new WebInspector.CallFrame(callFramePayload.callFrameId, sourceCodeLocation, callFramePayload.functionName, thisObject, scopeChain);
            this._callFrames.push(callFrame);
        }

        if (!this._callFrames.length) {
            this.resume();
            return;
        }

        this._activeCallFrame = this._callFrames[0];

        if (!wasStillPaused)
            this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.Paused);
        this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.CallFramesDidChange);
        this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange);
    },

    debuggerDidResume: function()
    {
        // Called from WebInspector.DebuggerObserver.

        function delayedWork()
        {
            delete this._delayedResumeTimeout;

            this._paused = false;
            this._callFrames = null;
            this._activeCallFrame = null;

            this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.Resumed);
            this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.CallFramesDidChange);
            this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ActiveCallFrameDidChange);
        }

        // We delay clearing the state and firing events so the user interface does not flash
        // between brief steps or successive breakpoints.
        this._delayedResumeTimeout = setTimeout(delayedWork.bind(this), 50);
    },

    scriptDidParse: function(scriptIdentifier, url, isContentScript, startLine, startColumn, endLine, endColumn, sourceMapURL)
    {
        // Don't add the script again if it is already known.
        if (this._scriptIdMap[scriptIdentifier]) {
            console.assert(this._scriptIdMap[scriptIdentifier].url === url);
            console.assert(this._scriptIdMap[scriptIdentifier].range.startLine === startLine);
            console.assert(this._scriptIdMap[scriptIdentifier].range.startColumn === startColumn);
            console.assert(this._scriptIdMap[scriptIdentifier].range.endLine === endLine);
            console.assert(this._scriptIdMap[scriptIdentifier].range.endColumn === endColumn);
            return;
        }

        var script = new WebInspector.Script(scriptIdentifier, new WebInspector.TextRange(startLine, startColumn, endLine, endColumn), url, isContentScript, sourceMapURL);

        this._scriptIdMap[scriptIdentifier] = script;

        if (script.url) {
            var scripts = this._scriptURLMap[script.url];
            if (!scripts)
                scripts = this._scriptURLMap[script.url] = [];
            scripts.push(script);
        }

        this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.ScriptAdded, {script: script});
    },

    isBreakpointRemovable: function(breakpoint)
    {
        return breakpoint !== this._allExceptionsBreakpoint && breakpoint !== this._allUncaughtExceptionsBreakpoint;
    },

    isBreakpointEditable: function(breakpoint)
    {
        return this.isBreakpointRemovable(breakpoint);
    },

    // Private

    _sourceCodeLocationFromPayload: function(payload)
    {
        var script = this._scriptIdMap[payload.scriptId];
        console.assert(script);
        if (!script)
            return null;

        return script.createSourceCodeLocation(payload.lineNumber, payload.columnNumber);
    },

    _scopeChainFromPayload: function(payload)
    {
        var scopeChain = [];
        for (var i = 0; i < payload.length; ++i)
            scopeChain.push(this._scopeChainNodeFromPayload(payload[i]));
        return scopeChain;
    },

    _scopeChainNodeFromPayload: function(payload)
    {
        var type = null;
        switch (payload.type) {
        case "local":
            type = WebInspector.ScopeChainNode.Type.Local;
            break;
        case "global":
            type = WebInspector.ScopeChainNode.Type.Global;
            break;
        case "with":
            type = WebInspector.ScopeChainNode.Type.With;
            break;
        case "closure":
            type = WebInspector.ScopeChainNode.Type.Closure;
            break;
        case "catch":
            type = WebInspector.ScopeChainNode.Type.Catch;
            break;
        default:
            console.error("Unknown type: " + payload.type);
        }

        var object = WebInspector.RemoteObject.fromPayload(payload.object);
        return new WebInspector.ScopeChainNode(type, object);
    },

    _debuggerBreakpointActionType: function(type)
    {
        switch (type) {
        case WebInspector.BreakpointAction.Type.Log:
            return DebuggerAgent.BreakpointActionType.Log;
        case WebInspector.BreakpointAction.Type.Evaluate:
            return DebuggerAgent.BreakpointActionType.Evaluate;
        case WebInspector.BreakpointAction.Type.Sound:
            return DebuggerAgent.BreakpointActionType.Sound;
        default:
            console.assert(false);
            return DebuggerAgent.BreakpointActionType.Log;
        }
    },

    _setBreakpoint: function(breakpoint, callback)
    {
        console.assert(!breakpoint.id);
        console.assert(!breakpoint.disabled);

        if (breakpoint.id || breakpoint.disabled)
            return;

        function didSetBreakpoint(error, breakpointIdentifier)
        {
            if (error)
                return;

            this._breakpointIdMap[breakpointIdentifier] = breakpoint;

            breakpoint.id = breakpointIdentifier;
            breakpoint.resolved = true;

            if (typeof callback === "function")
                callback();
        }

        // The breakpoint will be resolved again by calling DebuggerAgent, so mark it as unresolved.
        // If something goes wrong it will stay unresolved and show up as such in the user interface.
        breakpoint.resolved = false;

        // Convert BreakpointAction types to DebuggerAgent protocol types.
        // NOTE: Breakpoint.options returns new objects each time, so it is safe to modify.
        var options;
        if (DebuggerAgent.BreakpointActionType) {
            options = breakpoint.options;
            if (options.actions.length) {
                for (var i = 0; i < options.actions.length; ++i)
                    options.actions[i].type = this._debuggerBreakpointActionType(options.actions[i].type);
            }
        }

        // COMPATIBILITY (iOS 7): iOS 7 and earlier, DebuggerAgent.setBreakpoint* took a "condition" string argument.
        // This has been replaced with an "options" BreakpointOptions object.
        if (breakpoint.url) {
            DebuggerAgent.setBreakpointByUrl.invoke({
                lineNumber: breakpoint.sourceCodeLocation.lineNumber,
                url: breakpoint.url,
                urlRegex: undefined,
                columnNumber: breakpoint.sourceCodeLocation.columnNumber,
                condition: breakpoint.condition,
                options: options
            }, didSetBreakpoint.bind(this));
        } else if (breakpoint.scriptIdentifier) {
            DebuggerAgent.setBreakpoint.invoke({
                location: {scriptId: breakpoint.scriptIdentifier, lineNumber: breakpoint.sourceCodeLocation.lineNumber, columnNumber: breakpoint.sourceCodeLocation.columnNumber},
                condition: breakpoint.condition,
                options: options
            }, didSetBreakpoint.bind(this));
        }
    },

    _removeBreakpoint: function(breakpoint, callback)
    {
        console.assert(breakpoint.id);

        if (!breakpoint.id)
            return;

        function didRemoveBreakpoint(error)
        {
            if (error)
                console.error(error);

            delete this._breakpointIdMap[breakpoint.id];

            breakpoint.id = null;

            // Don't reset resolved here since we want to keep disabled breakpoints looking like they
            // are resolved in the user interface. They will get marked as unresolved in reset.

            if (typeof callback === "function")
                callback();
        }

        DebuggerAgent.removeBreakpoint(breakpoint.id, didRemoveBreakpoint.bind(this));
    },

    _breakpointDisplayLocationDidChange: function(event)
    {
        if (this._ignoreBreakpointDisplayLocationDidChangeEvent)
            return;

        var breakpoint = event.target;
        if (!breakpoint.id || breakpoint.disabled)
            return;

        // Remove the breakpoint with its old id.
        this._removeBreakpoint(breakpoint, breakpointRemoved.bind(this));

        function breakpointRemoved()
        {
            // Add the breakpoint at its new lineNumber and get a new id.
            this._setBreakpoint(breakpoint);

            this.dispatchEventToListeners(WebInspector.DebuggerManager.Event.BreakpointMoved, {breakpoint: breakpoint});
        }
    },

    _breakpointDisabledStateDidChange: function(event)
    {
        var breakpoint = event.target;

        if (breakpoint === this._allExceptionsBreakpoint) {
            this._allExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled;
            this._updateBreakOnExceptionsState();
            return;
        }

        if (breakpoint === this._allUncaughtExceptionsBreakpoint) {
            this._allUncaughtExceptionsBreakpointEnabledSetting.value = !breakpoint.disabled;
            this._updateBreakOnExceptionsState();
            return;
        }

        if (breakpoint.disabled)
            this._removeBreakpoint(breakpoint);
        else
            this._setBreakpoint(breakpoint);
    },

    _breakpointEditablePropertyDidChange: function(event)
    {
        var breakpoint = event.target;
        if (breakpoint.disabled)
            return;

        console.assert(this.isBreakpointEditable(breakpoint));
        if (!this.isBreakpointEditable(breakpoint))
            return;

        // Remove the breakpoint with its old id.
        this._removeBreakpoint(breakpoint, breakpointRemoved.bind(this));

        function breakpointRemoved()
        {
            // Add the breakpoint with its new condition and get a new id.
            this._setBreakpoint(breakpoint);
        }
    },

    _updateBreakOnExceptionsState: function()
    {
        var state = "none";

        if (this._breakpointsEnabledSetting.value) {
            if (!this._allExceptionsBreakpoint.disabled)
                state = "all";
            else if (!this._allUncaughtExceptionsBreakpoint.disabled)
                state = "uncaught";
        }

        switch (state) {
        case "all":
            // Mark the uncaught breakpoint as unresolved since "all" includes "uncaught".
            // That way it is clear in the user interface that the breakpoint is ignored.
            this._allUncaughtExceptionsBreakpoint.resolved = false;
            break;
        case "uncaught":
        case "none":
            // Mark the uncaught breakpoint as resolved again.
            this._allUncaughtExceptionsBreakpoint.resolved = true;
            break;
        }

        DebuggerAgent.setPauseOnExceptions(state);
    },

    _inspectorClosing: function(event)
    {
        this._saveBreakpoints();
    },

    _saveBreakpoints: function()
    {
        var savedBreakpoints = [];

        for (var i = 0; i < this._breakpoints.length; ++i) {
            var breakpoint = this._breakpoints[i];

            // Only breakpoints with URLs can be saved. Breakpoints for transient scripts can't.
            if (!breakpoint.url)
                continue;

            savedBreakpoints.push(breakpoint.info);
        }

        this._breakpointsSetting.value = savedBreakpoints;
    },

    _associateBreakpointsWithSourceCode: function(breakpoints, sourceCode)
    {
        this._ignoreBreakpointDisplayLocationDidChangeEvent = true;

        for (var i = 0; i < breakpoints.length; ++i) {
            var breakpoint = breakpoints[i];
            if (breakpoint.sourceCodeLocation.sourceCode === null)
                breakpoint.sourceCodeLocation.sourceCode = sourceCode;
            // SourceCodes can be unequal if the SourceCodeLocation is associated with a Script and we are looking at the Resource.
            console.assert(breakpoint.sourceCodeLocation.sourceCode === sourceCode || breakpoint.sourceCodeLocation.sourceCode.url === sourceCode.url);
        }

        delete this._ignoreBreakpointDisplayLocationDidChangeEvent;
    }
};

WebInspector.DebuggerManager.prototype.__proto__ = WebInspector.Object.prototype;
