blob: 08701b608ede3c2d4bd2249b42862400ee2594bc [file] [log] [blame]
/*
* Copyright (C) 2011 Google 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
* OWNER OR 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.DebuggerPresentationModel = function()
{
this._sourceFiles = {};
this._messages = [];
this._breakpointsByDebuggerId = {};
this._breakpointsWithoutSourceFile = {};
this._presentationCallFrames = [];
this._selectedCallFrameIndex = 0;
WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerWasEnabled, this._debuggerWasEnabled, this);
WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this);
WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._failedToParseScriptSource, this);
WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointResolved, this._breakpointResolved, this);
WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerPaused, this._debuggerPaused, this);
WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.DebuggerResumed, this._debuggerResumed, this);
WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.Reset, this._debuggerReset, this);
new WebInspector.DebuggerPresentationModelResourceBinding(this);
}
WebInspector.DebuggerPresentationModel.Events = {
SourceFileAdded: "source-file-added",
SourceFileChanged: "source-file-changed",
ConsoleMessageAdded: "console-message-added",
BreakpointAdded: "breakpoint-added",
BreakpointRemoved: "breakpoint-removed",
DebuggerPaused: "debugger-paused",
DebuggerResumed: "debugger-resumed",
CallFrameSelected: "call-frame-selected"
}
WebInspector.DebuggerPresentationModel.prototype = {
_debuggerWasEnabled: function()
{
if (this._breakpointsRestored)
return;
this._restoreBreakpointsFromSettings();
this._breakpointsRestored = true;
},
sourceFile: function(sourceFileId)
{
return this._sourceFiles[sourceFileId];
},
sourceFileForScriptURL: function(scriptURL)
{
return this._sourceFiles[scriptURL];
},
requestSourceFileContent: function(sourceFileId, callback)
{
this._sourceFiles[sourceFileId].requestContent(callback);
},
_parsedScriptSource: function(event)
{
this._addScript(event.data);
},
_failedToParseScriptSource: function(event)
{
this._addScript(event.data);
},
_addScript: function(script)
{
var sourceFileId = this._createSourceFileId(script.sourceURL, script.sourceID);
var sourceFile = this._sourceFiles[sourceFileId];
if (sourceFile) {
sourceFile.addScript(script);
return;
}
function contentChanged(sourceFile)
{
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileChanged, this._sourceFiles[sourceFileId]);
}
if (!this._formatSourceFiles)
sourceFile = new WebInspector.SourceFile(sourceFileId, script, contentChanged.bind(this));
else
sourceFile = new WebInspector.FormattedSourceFile(sourceFileId, script, contentChanged.bind(this), this._formatter());
this._sourceFiles[sourceFileId] = sourceFile;
this._restoreBreakpoints(sourceFile);
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.SourceFileAdded, sourceFile);
},
_restoreBreakpoints: function(sourceFile)
{
var pendingBreakpoints = this._breakpointsWithoutSourceFile[sourceFile.id];
for (var i = 0; pendingBreakpoints && i < pendingBreakpoints.length; ++i) {
var breakpointData = pendingBreakpoints[i];
if ("debuggerId" in breakpointData) {
var breakpoint = new WebInspector.PresentationBreakpoint(sourceFile, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled);
this._bindDebuggerId(breakpoint, breakpointData.debuggerId);
this._breakpointAdded(breakpoint);
} else
this.setBreakpoint(sourceFile.id, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled, true);
}
delete this._breakpointsWithoutSourceFile[sourceFile.id];
},
canEditScriptSource: function(sourceFileId)
{
if (!Preferences.canEditScriptSource || this._formatSourceFiles)
return false;
var script = this._scriptForSourceFileId(sourceFileId);
return !script.lineOffset && !script.columnOffset;
},
editScriptSource: function(sourceFileId, newSource, callback)
{
var script = this._scriptForSourceFileId(sourceFileId);
var sourceFile = this._sourceFiles[sourceFileId];
function didEditScriptSource(oldSource, error)
{
if (!error) {
sourceFile.content = newSource;
var resource = WebInspector.resourceForURL(sourceFile.url);
if (resource)
resource.addRevision(newSource);
}
callback(error);
if (!error && WebInspector.debuggerModel.callFrames)
this._debuggerPaused();
}
var oldSource = sourceFile.requestContent(didReceiveSource.bind(this));
function didReceiveSource(oldSource)
{
WebInspector.debuggerModel.editScriptSource(script.sourceID, newSource, didEditScriptSource.bind(this, oldSource));
}
},
_updateBreakpointsAfterLiveEdit: function(sourceFileId, oldSource, newSource)
{
var sourceFile = this._sourceFiles[sourceFileId];
// Clear and re-create breakpoints according to text diff.
var diff = Array.diff(oldSource.split("\n"), newSource.split("\n"));
for (var lineNumber in sourceFile.breakpoints) {
var breakpoint = sourceFile.breakpoints[lineNumber];
var lineNumber = breakpoint.lineNumber;
this.removeBreakpoint(sourceFileId, lineNumber);
var newLineNumber = diff.left[lineNumber].row;
if (newLineNumber === undefined) {
for (var i = lineNumber - 1; i >= 0; --i) {
if (diff.left[i].row === undefined)
continue;
var shiftedLineNumber = diff.left[i].row + lineNumber - i;
if (shiftedLineNumber < diff.right.length) {
var originalLineNumber = diff.right[shiftedLineNumber].row;
if (originalLineNumber === lineNumber || originalLineNumber === undefined)
newLineNumber = shiftedLineNumber;
}
break;
}
}
if (newLineNumber !== undefined)
this.setBreakpoint(sourceFileId, newLineNumber, breakpoint.condition, breakpoint.enabled);
}
},
toggleFormatSourceFiles: function()
{
this._formatSourceFiles = !this._formatSourceFiles;
for (var id in this._sourceFiles) {
var sourceFile = this._sourceFiles[id];
for (var line in sourceFile.breakpoints)
this._removeBreakpointFromDebugger(sourceFile.breakpoints[line]);
}
var messages = this._messages;
this._reset();
var scripts = WebInspector.debuggerModel.scripts;
for (var id in scripts)
this._addScript(scripts[id]);
for (var i = 0; i < messages.length; ++i)
this.addConsoleMessage(messages[i]);
if (WebInspector.debuggerModel.callFrames)
this._debuggerPaused();
},
formatSourceFilesToggled: function()
{
return this._formatSourceFiles;
},
_formatter: function()
{
if (!this._scriptFormatter)
this._scriptFormatter = new WebInspector.ScriptFormatter();
return this._scriptFormatter;
},
addConsoleMessage: function(message)
{
this._messages.push(message);
var sourceFile = this._sourceFileForScript(message.url);
if (!sourceFile)
return;
function didRequestSourceMapping(mapping)
{
var presentationMessage = {};
presentationMessage.sourceFileId = sourceFile.id;
presentationMessage.lineNumber = mapping.scriptLocationToSourceLine({lineNumber:message.line - 1, columnNumber:0});
presentationMessage.originalMessage = message;
sourceFile.messages.push(presentationMessage);
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.ConsoleMessageAdded, presentationMessage);
}
sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
},
clearConsoleMessages: function()
{
this._messages = [];
for (var id in this._sourceFiles)
this._sourceFiles[id].messages = [];
},
continueToLine: function(sourceFileId, lineNumber)
{
function didRequestSourceMapping(mapping)
{
var location = mapping.sourceLineToScriptLocation(lineNumber);
WebInspector.debuggerModel.continueToLocation(location);
}
this._sourceFiles[sourceFileId].requestSourceMapping(didRequestSourceMapping.bind(this));
},
breakpointsForSourceFileId: function(sourceFileId)
{
var sourceFile = this.sourceFile(sourceFileId);
if (!sourceFile)
return [];
var breakpoints = [];
for (var lineNumber in sourceFile.breakpoints)
breakpoints.push(sourceFile.breakpoints[lineNumber]);
return breakpoints;
},
setBreakpoint: function(sourceFileId, lineNumber, condition, enabled, dontSaveBreakpoints)
{
var sourceFile = this._sourceFiles[sourceFileId];
if (!sourceFile)
return;
var breakpoint = new WebInspector.PresentationBreakpoint(sourceFile, lineNumber, condition, enabled);
if (!enabled) {
this._breakpointAdded(breakpoint);
if (!dontSaveBreakpoints)
this._saveBreakpoints();
return;
}
function callback()
{
this._breakpointAdded(breakpoint);
if (!dontSaveBreakpoints)
this._saveBreakpoints();
}
this._setBreakpointInDebugger(breakpoint, callback.bind(this));
},
_setBreakpointInDebugger: function(breakpoint, callback)
{
function didSetBreakpoint(breakpointId, locations)
{
if (!breakpointId)
return;
this._bindDebuggerId(breakpoint, breakpointId);
breakpoint.location = locations[0];
callback();
}
function didRequestSourceMapping(mapping)
{
var location = mapping.sourceLineToScriptLocation(breakpoint.lineNumber);
var script = WebInspector.debuggerModel.scriptForSourceID(location.sourceID);
if (script.sourceURL)
WebInspector.debuggerModel.setBreakpoint(script.sourceURL, location.lineNumber, location.columnNumber, breakpoint.condition, didSetBreakpoint.bind(this));
else {
location.sourceID = script.sourceID;
WebInspector.debuggerModel.setBreakpointBySourceId(location, breakpoint.condition, didSetBreakpoint.bind(this));
}
}
breakpoint.sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
},
_removeBreakpointFromDebugger: function(breakpoint, callback)
{
if (!("debuggerId" in breakpoint)) {
if (callback)
callback();
return;
}
function didRemoveBreakpoint()
{
this._unbindDebuggerId(breakpoint);
if (callback)
callback();
}
WebInspector.debuggerModel.removeBreakpoint(breakpoint.debuggerId, didRemoveBreakpoint.bind(this));
},
_bindDebuggerId: function(breakpoint, debuggerId)
{
breakpoint.debuggerId = debuggerId;
this._breakpointsByDebuggerId[debuggerId] = breakpoint;
},
_unbindDebuggerId: function(breakpoint)
{
delete this._breakpointsByDebuggerId[breakpoint.debuggerId];
delete breakpoint.debuggerId;
},
setBreakpointEnabled: function(sourceFileId, lineNumber, enabled)
{
var breakpoint = this.findBreakpoint(sourceFileId, lineNumber);
if (!breakpoint)
return;
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint);
breakpoint.enabled = enabled;
function afterUpdate()
{
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint);
this._saveBreakpoints();
}
if (!enabled)
this._removeBreakpointFromDebugger(breakpoint, afterUpdate.call(this));
else
this._setBreakpointInDebugger(breakpoint, afterUpdate.bind(this));
},
updateBreakpoint: function(sourceFileId, lineNumber, condition, enabled)
{
this.removeBreakpoint(sourceFileId, lineNumber);
this.setBreakpoint(sourceFileId, lineNumber, condition, enabled);
},
removeBreakpoint: function(sourceFileId, lineNumber)
{
var breakpoint = this.findBreakpoint(sourceFileId, lineNumber);
if (!breakpoint)
return;
function callback()
{
this._breakpointRemoved(breakpoint);
this._saveBreakpoints();
}
this._removeBreakpointFromDebugger(breakpoint, callback.bind(this));
},
findBreakpoint: function(sourceFileId, lineNumber)
{
var sourceFile = this.sourceFile(sourceFileId);
if (sourceFile)
return sourceFile.breakpoints[lineNumber];
},
_breakpointAdded: function(breakpoint)
{
var sourceFile = breakpoint.sourceFile;
if (!sourceFile)
return;
function didRequestSourceMapping(mapping)
{
// Refine line number based on resolved location.
if (breakpoint.location)
breakpoint.lineNumber = mapping.scriptLocationToSourceLine(breakpoint.location);
var existingBreakpoint = this.findBreakpoint(sourceFile.id, breakpoint.lineNumber);
if (existingBreakpoint) {
// We can't show more than one breakpoint on a single source file line.
this._removeBreakpointFromDebugger(breakpoint);
return;
}
sourceFile.breakpoints[breakpoint.lineNumber] = breakpoint;
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointAdded, breakpoint);
}
sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
},
_breakpointRemoved: function(breakpoint)
{
var sourceFile = breakpoint.sourceFile;
if (sourceFile.breakpoints[breakpoint.lineNumber] === breakpoint) {
// There can already be a newer breakpoint;
delete sourceFile.breakpoints[breakpoint.lineNumber];
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.BreakpointRemoved, breakpoint);
}
},
_breakpointResolved: function(event)
{
var debuggerId = event.data.breakpointId;
if (!(debuggerId in this._breakpointsByDebuggerId))
return;
var breakpoint = this._breakpointsByDebuggerId[debuggerId];
this._breakpointRemoved(breakpoint);
breakpoint.location = event.data.location;
this._breakpointAdded(breakpoint);
},
_restoreBreakpointsFromSettings: function()
{
var breakpoints = WebInspector.settings.breakpoints;
for (var i = 0; i < breakpoints.length; ++i) {
var breakpointData = breakpoints[i];
var sourceFileId = breakpointData.sourceFileId;
if (!sourceFileId)
continue;
var sourceFile = this._sourceFiles[sourceFileId];
if (sourceFile) {
this.setBreakpoint(sourceFileId, breakpointData.lineNumber, breakpointData.condition, breakpointData.enabled);
continue;
}
// Add breakpoint once source file becomes available.
var pendingBreakpoints = this._breakpointsWithoutSourceFile[sourceFileId];
if (!pendingBreakpoints) {
pendingBreakpoints = [];
this._breakpointsWithoutSourceFile[sourceFileId] = pendingBreakpoints;
}
pendingBreakpoints.push(breakpointData);
}
},
_saveBreakpoints: function()
{
var serializedBreakpoints = [];
// Store added breakpoints.
for (var sourceFileId in this._sourceFiles) {
var sourceFile = this._sourceFiles[sourceFileId];
if (!sourceFile.url)
continue;
for (var lineNumber in sourceFile.breakpoints)
serializedBreakpoints.push(sourceFile.breakpoints[lineNumber].serialize());
}
// Store not added breakpoints.
for (var sourceFileId in this._breakpointsWithoutSourceFile)
serializedBreakpoints = serializedBreakpoints.concat(this._breakpointsWithoutSourceFile[sourceFileId]);
// Sanitize debugger ids.
for (var i = 0; i < serializedBreakpoints.length; ++i) {
var breakpoint = serializedBreakpoints[i];
var breakpointCopy = {};
for (var property in breakpoint) {
if (property !== "debuggerId")
breakpointCopy[property] = breakpoint[property];
}
serializedBreakpoints[i] = breakpointCopy;
}
WebInspector.settings.breakpoints = serializedBreakpoints;
},
_debuggerPaused: function()
{
var callFrames = WebInspector.debuggerModel.callFrames;
this._presentationCallFrames = [];
for (var i = 0; i < callFrames.length; ++i) {
var callFrame = callFrames[i];
var sourceFile;
var script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.sourceID);
if (script)
sourceFile = this._sourceFileForScript(script.sourceURL, script.sourceID);
this._presentationCallFrames.push(new WebInspector.PresenationCallFrame(callFrame, i, sourceFile));
}
var details = WebInspector.debuggerModel.debuggerPausedDetails;
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerPaused, { callFrames: this._presentationCallFrames, details: details });
this.selectedCallFrame = this._presentationCallFrames[this._selectedCallFrameIndex];
},
_debuggerResumed: function()
{
this._presentationCallFrames = [];
this._selectedCallFrameIndex = 0;
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.DebuggerResumed);
},
set selectedCallFrame(callFrame)
{
this._selectedCallFrameIndex = callFrame.index;
callFrame.select();
this.dispatchEventToListeners(WebInspector.DebuggerPresentationModel.Events.CallFrameSelected, callFrame);
},
get selectedCallFrame()
{
return this._presentationCallFrames[this._selectedCallFrameIndex];
},
_sourceFileForScript: function(sourceURL, sourceID)
{
return this._sourceFiles[this._createSourceFileId(sourceURL, sourceID)];
},
_scriptForSourceFileId: function(sourceFileId)
{
function filter(script)
{
return this._createSourceFileId(script.sourceURL, script.sourceID) === sourceFileId;
}
return WebInspector.debuggerModel.queryScripts(filter.bind(this))[0];
},
_createSourceFileId: function(sourceURL, sourceID)
{
var prefix = this._formatSourceFiles ? "deobfuscated:" : "";
return prefix + (sourceURL || sourceID);
},
_reset: function()
{
for (var id in this._sourceFiles) {
var sourceFile = this._sourceFiles[id];
for (var line in sourceFile.breakpoints) {
var breakpoints = this._breakpointsWithoutSourceFile[sourceFile.id];
if (!breakpoints) {
breakpoints = [];
this._breakpointsWithoutSourceFile[sourceFile.id] = breakpoints;
}
breakpoints.push(sourceFile.breakpoints[line].serialize());
}
}
this._sourceFiles = {};
this._messages = [];
this._breakpointsByDebuggerId = {};
},
_debuggerReset: function()
{
this._reset();
this._presentationCallFrames = [];
this._selectedCallFrameIndex = 0;
}
}
WebInspector.DebuggerPresentationModel.prototype.__proto__ = WebInspector.Object.prototype;
WebInspector.PresentationBreakpoint = function(sourceFile, lineNumber, condition, enabled)
{
this.sourceFile = sourceFile;
this.sourceFileId = sourceFile.id;
this.lineNumber = lineNumber;
this.condition = condition;
this.enabled = enabled;
}
WebInspector.PresentationBreakpoint.prototype = {
get url()
{
return this.sourceFile.url;
},
get resolved()
{
return !!this.location;
},
loadSnippet: function(callback)
{
function didRequestContent(mimeType, content)
{
var lineEndings = content.lineEndings();
var snippet = "";
if (this.lineNumber < lineEndings.length)
snippet = content.substring(lineEndings[this.lineNumber - 1], lineEndings[this.lineNumber]);
callback(snippet);
}
if (!this.sourceFile) {
callback(WebInspector.UIString("N/A"));
return;
}
this.sourceFile.requestContent(didRequestContent.bind(this));
},
serialize: function()
{
var serializedBreakpoint = {};
serializedBreakpoint.sourceFileId = this.sourceFile.id;
serializedBreakpoint.lineNumber = this.lineNumber;
serializedBreakpoint.condition = this.condition;
serializedBreakpoint.enabled = this.enabled;
serializedBreakpoint.debuggerId = this.debuggerId;
return serializedBreakpoint;
}
}
WebInspector.PresenationCallFrame = function(callFrame, index, sourceFile)
{
this._callFrame = callFrame;
this._index = index;
this._sourceFile = sourceFile;
this._script = WebInspector.debuggerModel.scriptForSourceID(callFrame.location.sourceID);
}
WebInspector.PresenationCallFrame.prototype = {
get functionName()
{
return this._callFrame.functionName;
},
get type()
{
return this._callFrame.type;
},
get isInternalScript()
{
return !this._script;
},
get url()
{
if (this._sourceFile)
return this._sourceFile.url;
},
get scopeChain()
{
return this._callFrame.scopeChain;
},
get index()
{
return this._index;
},
select: function()
{
if (this._sourceFile)
this._sourceFile.forceLoadContent(this._script);
},
evaluate: function(code, objectGroup, includeCommandLineAPI, callback)
{
function didEvaluateOnCallFrame(error, result)
{
callback(WebInspector.RemoteObject.fromPayload(result));
}
DebuggerAgent.evaluateOnCallFrame(this._callFrame.id, code, objectGroup, includeCommandLineAPI, didEvaluateOnCallFrame.bind(this));
},
sourceLine: function(callback)
{
if (!this._sourceFile) {
callback(undefined, this._callFrame.location.lineNumber);
return;
}
function didRequestSourceMapping(mapping)
{
callback(this._sourceFile.id, mapping.scriptLocationToSourceLine(this._callFrame.location));
}
this._sourceFile.requestSourceMapping(didRequestSourceMapping.bind(this));
}
}
WebInspector.DebuggerPresentationModelResourceBinding = function(model)
{
this._presentationModel = model;
WebInspector.Resource.registerDomainModelBinding(WebInspector.Resource.Type.Script, this);
}
WebInspector.DebuggerPresentationModelResourceBinding.prototype = {
canSetContent: function(resource)
{
var sourceFile = this._presentationModel._sourceFileForScript(resource.url)
if (!sourceFile)
return false;
return this._presentationModel.canEditScriptSource(sourceFile.id);
},
setContent: function(resource, content, majorChange, userCallback)
{
if (!majorChange)
return;
var sourceFile = this._presentationModel._sourceFileForScript(resource.url);
if (!sourceFile) {
userCallback("Resource is not editable");
return;
}
resource.requestContent(this._setContentWithInitialContent.bind(this, sourceFile, content, userCallback));
},
_setContentWithInitialContent: function(sourceFile, content, userCallback, oldContent)
{
function callback(error)
{
if (userCallback)
userCallback(error);
if (!error) {
this._presentationModel._updateBreakpointsAfterLiveEdit(sourceFile.id, oldContent, content);
sourceFile.reload();
}
}
this._presentationModel.editScriptSource(sourceFile.id, content, callback.bind(this));
}
}
WebInspector.DebuggerPresentationModelResourceBinding.prototype.__proto__ = WebInspector.ResourceDomainModelBinding.prototype;