blob: 5267e0a12a9175aff3e3aa33273b2d7edd5929f6 [file] [log] [blame]
/*
* Copyright (C) 2013, 2015 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.
*/
WI.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WI.NavigationSidebarPanel
{
constructor()
{
super("debugger", WI.UIString("Debugger"), true);
WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceAdded, this);
WI.Target.addEventListener(WI.Target.Event.ResourceAdded, this._resourceAdded, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointAdded, this._breakpointAdded, this);
WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointAdded, this._breakpointAdded, this);
WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventBreakpointAdded, this._breakpointAdded, this);
WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.URLBreakpointAdded, this._breakpointAdded, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointRemoved, this._breakpointRemoved, this);
WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, this._breakpointRemoved, this);
WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.EventBreakpointRemoved, this._breakpointRemoved, this);
WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.URLBreakpointRemoved, this._breakpointRemoved, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.BreakpointsEnabledDidChange, this._breakpointsEnabledDidChange, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptAdded, this._scriptAdded, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptRemoved, this._scriptRemoved, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ScriptsCleared, this._scriptsCleared, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, this._debuggerDidPause, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Resumed, this._debuggerDidResume, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.CallFramesDidChange, this._debuggerCallFramesDidChange, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.ActiveCallFrameDidChange, this._debuggerActiveCallFrameDidChange, this);
WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.WaitingToPause, this._debuggerWaitingToPause, this);
WI.DOMBreakpoint.addEventListener(WI.DOMBreakpoint.Event.DOMNodeChanged, this._handleDOMBreakpointDOMNodeChanged, this);
WI.timelineManager.addEventListener(WI.TimelineManager.Event.CapturingStateChanged, this._handleTimelineCapturingStateChanged, this);
WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
WI.targetManager.addEventListener(WI.TargetManager.Event.TargetAdded, this._targetAdded, this);
WI.targetManager.addEventListener(WI.TargetManager.Event.TargetRemoved, this._targetRemoved, this);
this._timelineRecordingWarningElement = document.createElement("div");
this._timelineRecordingWarningElement.classList.add("warning-banner");
this._timelineRecordingWarningElement.append(WI.UIString("Debugger disabled during Timeline recording"), document.createElement("br"));
let timelineStopRecordingLink = this._timelineRecordingWarningElement.appendChild(document.createElement("a"));
timelineStopRecordingLink.textContent = WI.UIString("Stop recording");
timelineStopRecordingLink.addEventListener("click", () => { WI.timelineManager.stopCapturing(); });
this._auditTestWarningElement = document.createElement("div");
this._auditTestWarningElement.classList.add("warning-banner");
this._auditTestWarningElement.append(WI.UIString("Debugger disabled during Audit"), document.createElement("br"));
let auditStopRecordingLink = this._auditTestWarningElement.appendChild(document.createElement("a"));
auditStopRecordingLink.textContent = WI.UIString("Stop Audit");
auditStopRecordingLink.addEventListener("click", () => { WI.auditManager.stop(); });
this._breakpointsDisabledWarningElement = document.createElement("div");
this._breakpointsDisabledWarningElement.classList.add("warning-banner");
this._breakpointsDisabledWarningElement.append(WI.UIString("Breakpoints disabled"), document.createElement("br"));
let enableBreakpointsLink = this._breakpointsDisabledWarningElement.appendChild(document.createElement("a"));
enableBreakpointsLink.textContent = WI.UIString("Enable breakpoints");
enableBreakpointsLink.addEventListener("click", () => { WI.debuggerToggleBreakpoints(); });
this._navigationBar = new WI.NavigationBar;
this.addSubview(this._navigationBar);
var breakpointsImage = {src: "Images/Breakpoints.svg", width: 15, height: 15};
var pauseImage = {src: "Images/Pause.svg", width: 15, height: 15};
var resumeImage = {src: "Images/Resume.svg", width: 15, height: 15};
var stepOverImage = {src: "Images/StepOver.svg", width: 15, height: 15};
var stepIntoImage = {src: "Images/StepInto.svg", width: 15, height: 15};
var stepOutImage = {src: "Images/StepOut.svg", width: 15, height: 15};
var toolTip = WI.UIString("Enable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName);
var altToolTip = WI.UIString("Disable all breakpoints (%s)").format(WI.toggleBreakpointsKeyboardShortcut.displayName);
this._debuggerBreakpointsButtonItem = new WI.ActivateButtonNavigationItem("debugger-breakpoints", toolTip, altToolTip, breakpointsImage.src, breakpointsImage.width, breakpointsImage.height);
this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
this._debuggerBreakpointsButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerToggleBreakpoints, this);
this._navigationBar.addNavigationItem(this._debuggerBreakpointsButtonItem);
toolTip = WI.UIString("Pause script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName);
altToolTip = WI.UIString("Continue script execution (%s or %s)").format(WI.pauseOrResumeKeyboardShortcut.displayName, WI.pauseOrResumeAlternateKeyboardShortcut.displayName);
this._debuggerPauseResumeButtonItem = new WI.ToggleButtonNavigationItem("debugger-pause-resume", toolTip, altToolTip, pauseImage.src, resumeImage.src, pauseImage.width, pauseImage.height);
this._debuggerPauseResumeButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerPauseResumeToggle, this);
this._navigationBar.addNavigationItem(this._debuggerPauseResumeButtonItem);
this._debuggerStepOverButtonItem = new WI.ButtonNavigationItem("debugger-step-over", WI.UIString("Step over (%s or %s)").format(WI.stepOverKeyboardShortcut.displayName, WI.stepOverAlternateKeyboardShortcut.displayName), stepOverImage.src, stepOverImage.width, stepOverImage.height);
this._debuggerStepOverButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOver, this);
this._debuggerStepOverButtonItem.enabled = false;
this._navigationBar.addNavigationItem(this._debuggerStepOverButtonItem);
this._debuggerStepIntoButtonItem = new WI.ButtonNavigationItem("debugger-step-into", WI.UIString("Step into (%s or %s)").format(WI.stepIntoKeyboardShortcut.displayName, WI.stepIntoAlternateKeyboardShortcut.displayName), stepIntoImage.src, stepIntoImage.width, stepIntoImage.height);
this._debuggerStepIntoButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepInto, this);
this._debuggerStepIntoButtonItem.enabled = false;
this._navigationBar.addNavigationItem(this._debuggerStepIntoButtonItem);
this._debuggerStepOutButtonItem = new WI.ButtonNavigationItem("debugger-step-out", WI.UIString("Step out (%s or %s)").format(WI.stepOutKeyboardShortcut.displayName, WI.stepOutAlternateKeyboardShortcut.displayName), stepOutImage.src, stepOutImage.width, stepOutImage.height);
this._debuggerStepOutButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, WI.debuggerStepOut, this);
this._debuggerStepOutButtonItem.enabled = false;
this._navigationBar.addNavigationItem(this._debuggerStepOutButtonItem);
function showResourcesWithIssuesOnlyFilterFunction(treeElement)
{
// Issues are only shown in the scripts tree outline.
if (treeElement.treeOutline !== this._scriptsContentTreeOutline)
return true;
// Keep issues.
if (treeElement instanceof WI.IssueTreeElement)
return true;
// Keep resources with issues.
if (treeElement.hasChildren) {
for (let child of treeElement.children) {
if (child instanceof WI.IssueTreeElement)
return true;
}
}
return false;
}
this.filterBar.addFilterBarButton("debugger-show-resources-with-issues-only", showResourcesWithIssuesOnlyFilterFunction.bind(this), false, WI.UIString("Only show resources with issues"), WI.UIString("Show all resources"), "Images/Errors.svg", 15, 15);
this._breakpointsContentTreeOutline = this.contentTreeOutline;
let breakpointsRow = new WI.DetailsSectionRow;
breakpointsRow.element.appendChild(this._breakpointsContentTreeOutline.element);
let breakpointNavigationBarWrapper = document.createElement("div");
let breakpointNavigationBar = new WI.NavigationBar;
breakpointNavigationBarWrapper.appendChild(breakpointNavigationBar.element);
this._createBreakpointButton = new WI.ButtonNavigationItem("create-breakpoint", WI.UIString("Create Breakpoint"), "Images/Plus13.svg", 13, 13);
this._createBreakpointButton.element.addEventListener("mousedown", this._handleCreateBreakpointMouseDown.bind(this));
breakpointNavigationBar.addNavigationItem(this._createBreakpointButton);
let breakpointsGroup = new WI.DetailsSectionGroup([breakpointsRow]);
this._breakpointsSection = new WI.DetailsSection("breakpoints", WI.UIString("Breakpoints"), [breakpointsGroup], breakpointNavigationBarWrapper);
this.contentView.element.appendChild(this._breakpointsSection.element);
this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementAdded, this._handleBreakpointElementAddedOrRemoved, this);
this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.ElementRemoved, this._handleBreakpointElementAddedOrRemoved, this);
this._breakpointsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
this._breakpointsContentTreeOutline.ondelete = this._breakpointTreeOutlineDeleteTreeElement.bind(this);
this._breakpointsContentTreeOutline.populateContextMenu = function(contextMenu, event, treeElement) {
this._breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement);
WI.TreeOutline.prototype.populateContextMenu(contextMenu, event, treeElement);
}.bind(this);
this._scriptsContentTreeOutline = this.createContentTreeOutline();
this._scriptsContentTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
this._scriptsContentTreeOutline.includeSourceMapResourceChildren = true;
let scriptsRow = new WI.DetailsSectionRow;
scriptsRow.element.appendChild(this._scriptsContentTreeOutline.element);
let scriptsGroup = new WI.DetailsSectionGroup([scriptsRow]);
this._scriptsSection = new WI.DetailsSection("scripts", WI.UIString("Sources"), [scriptsGroup]);
this.contentView.element.appendChild(this._scriptsSection.element);
this._callStackTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
this._callStackTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
this._callStackRow = new WI.DetailsSectionRow;
this._callStackRow.element.appendChild(this._callStackTreeOutline.element);
this._callStackGroup = new WI.DetailsSectionGroup([this._callStackRow]);
this._callStackSection = new WI.DetailsSection("call-stack", WI.UIString("Call Stack"), [this._callStackGroup]);
this._mainTargetTreeElement = null;
this._activeCallFrameTreeElement = null;
this._pauseReasonTreeOutline = null;
this._pauseReasonLinkContainerElement = document.createElement("span");
this._pauseReasonTextRow = new WI.DetailsSectionTextRow;
this._pauseReasonGroup = new WI.DetailsSectionGroup([this._pauseReasonTextRow]);
this._pauseReasonSection = new WI.DetailsSection("paused-reason", WI.UIString("Pause Reason"), [this._pauseReasonGroup], this._pauseReasonLinkContainerElement);
WI.Breakpoint.addEventListener(WI.Breakpoint.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
WI.IssueMessage.addEventListener(WI.IssueMessage.Event.DisplayLocationDidChange, this._handleDebuggerObjectDisplayLocationDidChange, this);
WI.consoleManager.addEventListener(WI.ConsoleManager.Event.IssueAdded, this._handleIssueAdded, this);
WI.consoleManager.addEventListener(WI.ConsoleManager.Event.Cleared, this._handleIssuesCleared, this);
WI.debuggerManager.addBreakpoint(WI.debuggerManager.allExceptionsBreakpoint);
WI.debuggerManager.addBreakpoint(WI.debuggerManager.uncaughtExceptionsBreakpoint);
// COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
if (InspectorBackend.domains.Debugger.setPauseOnAssertions && WI.settings.showAssertionFailuresBreakpoint.value)
WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
for (let target of WI.targets)
this._addTarget(target);
this._updateCallStackTreeOutline();
if (WI.networkManager.mainFrame)
this._addResourcesRecursivelyForFrame(WI.networkManager.mainFrame);
for (let script of WI.debuggerManager.knownNonResourceScripts)
this._addScript(script);
if (WI.domDebuggerManager.supported) {
if (WI.settings.showAllRequestsBreakpoint.value)
WI.domDebuggerManager.addURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint);
for (let eventBreakpoint of WI.domDebuggerManager.eventBreakpoints)
this._addBreakpoint(eventBreakpoint);
for (let domBreakpoint of WI.domDebuggerManager.domBreakpoints)
this._addBreakpoint(domBreakpoint);
for (let eventListenerBreakpoint of WI.domManager.eventListenerBreakpoints)
this._addBreakpoint(eventListenerBreakpoint);
for (let urlBreakpoints of WI.domDebuggerManager.urlBreakpoints)
this._addBreakpoint(urlBreakpoints);
}
if (WI.debuggerManager.paused)
this._debuggerDidPause(null);
if (WI.debuggerManager.breakpointsDisabledTemporarily) {
this._handleTimelineCapturingStateChanged();
if (WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping)
this._handleAuditManagerTestScheduled();
}
this._updateBreakpointsDisabledBanner();
}
// Public
get minimumWidth()
{
return this._navigationBar.minimumWidth;
}
closed()
{
super.closed();
WI.Frame.removeEventListener(null, null, this);
WI.Target.removeEventListener(null, null, this);
WI.debuggerManager.removeEventListener(null, null, this);
WI.domDebuggerManager.removeEventListener(null, null, this);
WI.DOMBreakpoint.removeEventListener(null, null, this);
WI.timelineManager.removeEventListener(null, null, this);
WI.targetManager.removeEventListener(null, null, this);
WI.Breakpoint.removeEventListener(null, null, this);
WI.IssueMessage.removeEventListener(null, null, this);
}
showDefaultContentView()
{
if (WI.networkManager.mainFrame) {
let mainTreeElement = this._scriptsContentTreeOutline.findTreeElement(WI.networkManager.mainFrame.mainResource);
if (mainTreeElement && this.showDefaultContentViewForTreeElement(mainTreeElement))
return;
}
let currentTreeElement = this._scriptsContentTreeOutline.children[0];
while (currentTreeElement && !currentTreeElement.root) {
if (currentTreeElement instanceof WI.ResourceTreeElement || currentTreeElement instanceof WI.ScriptTreeElement) {
if (this.showDefaultContentViewForTreeElement(currentTreeElement))
return;
}
currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
}
}
treeElementForRepresentedObject(representedObject)
{
// The main resource is used as the representedObject instead of Frame in our tree.
if (representedObject instanceof WI.Frame)
representedObject = representedObject.mainResource;
let treeElement = this._breakpointsContentTreeOutline.findTreeElement(representedObject);
if (!treeElement)
treeElement = this._scriptsContentTreeOutline.findTreeElement(representedObject);
if (treeElement)
return treeElement;
// Only special case Script objects.
if (!(representedObject instanceof WI.Script)) {
console.error("Didn't find a TreeElement for representedObject", representedObject);
return null;
}
// If the Script has a URL we should have found it earlier.
if (representedObject.url) {
console.error("Didn't find a ScriptTreeElement for a Script with a URL.");
return null;
}
// Since the Script does not have a URL we consider it an 'anonymous' script. These scripts happen from calls to
// window.eval() or browser features like Auto Fill and Reader. They are not normally added to the sidebar, but since
// we have a ScriptContentView asking for the tree element we will make a ScriptTreeElement on demand and add it.
return this._addScript(representedObject);
}
createContentTreeOutline(options = {})
{
let treeOutline = super.createContentTreeOutline(options)
treeOutline.addEventListener(WI.TreeOutline.Event.ElementRevealed, (event) => {
let treeElement = event.data.element;
let detailsSections = [this._pauseReasonSection, this._callStackSection, this._breakpointsSection, this._scriptsSection];
let detailsSection = detailsSections.find((detailsSection) => detailsSection.element.contains(treeElement.listItemElement));
if (!detailsSection)
return;
// Revealing a TreeElement at the scroll container's topmost edge with
// scrollIntoViewIfNeeded may result in the element being covered by the
// DetailsSection header, which uses sticky positioning. Detect this case,
// and adjust the sidebar content's scroll position to compensate.
let offset = detailsSection.headerElement.totalOffsetBottom - treeElement.listItemElement.totalOffsetTop;
if (offset > 0)
this.scrollElement.scrollBy(0, -offset);
});
return treeOutline;
}
// Protected
saveStateToCookie(cookie)
{
console.assert(cookie);
var selectedTreeElement = this._breakpointsContentTreeOutline.selectedTreeElement;
if (!selectedTreeElement) {
super.saveStateToCookie(cookie);
return;
}
var representedObject = selectedTreeElement.representedObject;
if (representedObject === WI.debuggerManager.allExceptionsBreakpoint) {
cookie[WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey] = true;
return;
}
if (representedObject === WI.debuggerManager.uncaughtExceptionsBreakpoint) {
cookie[WI.DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey] = true;
return;
}
if (representedObject === WI.debuggerManager.assertionFailuresBreakpoint) {
cookie[WI.DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey] = true;
return;
}
if (representedObject === WI.domDebuggerManager.allRequestsBreakpoint) {
cookie[WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey] = true;
return;
}
super.saveStateToCookie(cookie);
}
restoreStateFromCookie(cookie, relaxedMatchDelay)
{
console.assert(cookie);
function revealAndSelect(treeOutline, breakpoint) {
let treeElement = treeOutline.findTreeElement(breakpoint);
if (!treeElement)
return;
treeElement.revealAndSelect();
}
// Eagerly resolve the special breakpoints; otherwise, use the default behavior.
if (cookie[WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey])
revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.allExceptionsBreakpoint);
else if (cookie[WI.DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey])
revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.uncaughtExceptionsBreakpoint);
else if (cookie[WI.DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey])
revealAndSelect(this._breakpointsContentTreeOutline, WI.debuggerManager.assertionFailuresBreakpoint);
else if (cookie[WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey])
revealAndSelect(this._breakpointsContentTreeOutline, WI.domDebuggerManager.allRequestsBreakpoint);
else
super.restoreStateFromCookie(cookie, relaxedMatchDelay);
}
// Popover delegate
willDismissPopover(popover)
{
let breakpoint = popover.breakpoint;
if (!breakpoint)
return;
if (breakpoint instanceof WI.EventBreakpoint)
WI.domDebuggerManager.addEventBreakpoint(breakpoint);
else if (breakpoint instanceof WI.URLBreakpoint)
WI.domDebuggerManager.addURLBreakpoint(breakpoint);
}
// Private
_debuggerWaitingToPause(event)
{
this._debuggerPauseResumeButtonItem.enabled = false;
}
_debuggerDidPause(event)
{
this.contentView.element.insertBefore(this._callStackSection.element, this.contentView.element.firstChild);
if (this._updatePauseReason())
this.contentView.element.insertBefore(this._pauseReasonSection.element, this.contentView.element.firstChild);
this._debuggerPauseResumeButtonItem.enabled = true;
this._debuggerPauseResumeButtonItem.toggled = true;
this._debuggerStepOverButtonItem.enabled = true;
this._debuggerStepIntoButtonItem.enabled = true;
this._debuggerStepOutButtonItem.enabled = true;
this.element.classList.add(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
}
_debuggerDidResume(event)
{
this._callStackSection.element.remove();
this._pauseReasonSection.element.remove();
this._debuggerPauseResumeButtonItem.enabled = true;
this._debuggerPauseResumeButtonItem.toggled = false;
this._debuggerStepOverButtonItem.enabled = false;
this._debuggerStepIntoButtonItem.enabled = false;
this._debuggerStepOutButtonItem.enabled = false;
this.element.classList.remove(WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName);
}
_breakpointsEnabledDidChange(event)
{
this._debuggerBreakpointsButtonItem.activated = WI.debuggerManager.breakpointsEnabled;
this._updateBreakpointsDisabledBanner();
}
_addBreakpoint(breakpoint)
{
let constructor = WI.BreakpointTreeElement;
let options = {};
let parentTreeElement = this._breakpointsContentTreeOutline;
let getDOMNodeTreeElement = (domNode) => {
console.assert(domNode, "Missing DOMNode for identifier", breakpoint.domNodeIdentifier);
if (!domNode)
return null;
let domNodeTreeElement = this._breakpointsContentTreeOutline.findTreeElement(domNode);
if (!domNodeTreeElement) {
domNodeTreeElement = new WI.DOMNodeTreeElement(domNode);
this._addTreeElement(domNodeTreeElement, parentTreeElement);
}
return domNodeTreeElement;
};
if (breakpoint === WI.debuggerManager.allExceptionsBreakpoint) {
options.className = WI.DebuggerSidebarPanel.ExceptionIconStyleClassName;
options.title = WI.repeatedUIString.allExceptions();
} else if (breakpoint === WI.debuggerManager.uncaughtExceptionsBreakpoint) {
options.className = WI.DebuggerSidebarPanel.ExceptionIconStyleClassName;
options.title = WI.repeatedUIString.uncaughtExceptions();
} else if (breakpoint === WI.debuggerManager.assertionFailuresBreakpoint) {
options.className = WI.DebuggerSidebarPanel.AssertionIconStyleClassName;
options.title = WI.repeatedUIString.assertionFailures();
} else if (breakpoint instanceof WI.DOMBreakpoint) {
if (!breakpoint.domNodeIdentifier)
return null;
constructor = WI.DOMBreakpointTreeElement;
let domNode = WI.domManager.nodeForId(breakpoint.domNodeIdentifier);
parentTreeElement = getDOMNodeTreeElement(domNode);
} else if (breakpoint instanceof WI.EventBreakpoint) {
constructor = WI.EventBreakpointTreeElement;
if (breakpoint.eventListener) {
let eventTargetTreeElement = null;
if (breakpoint.eventListener.onWindow) {
if (!DebuggerSidebarPanel.__windowEventTargetRepresentedObject)
DebuggerSidebarPanel.__windowEventTargetRepresentedObject = {__window: true};
eventTargetTreeElement = this._breakpointsContentTreeOutline.findTreeElement(DebuggerSidebarPanel.__windowEventTargetRepresentedObject);
if (!eventTargetTreeElement) {
const subtitle = null;
eventTargetTreeElement = new WI.GeneralTreeElement(["event-target-window"], WI.unlocalizedString("window"), subtitle, DebuggerSidebarPanel.__windowEventTargetRepresentedObject);
this._addTreeElement(eventTargetTreeElement, parentTreeElement);
}
} else if (breakpoint.eventListener.node)
eventTargetTreeElement = getDOMNodeTreeElement(breakpoint.eventListener.node);
if (eventTargetTreeElement)
parentTreeElement = eventTargetTreeElement;
}
} else if (breakpoint instanceof WI.URLBreakpoint) {
constructor = WI.URLBreakpointTreeElement;
if (breakpoint === WI.domDebuggerManager.allRequestsBreakpoint)
options.title = WI.repeatedUIString.allRequests();
} else {
let sourceCode = breakpoint.sourceCodeLocation && breakpoint.sourceCodeLocation.displaySourceCode;
if (!sourceCode)
return null;
if (this._breakpointsContentTreeOutline.findTreeElement(breakpoint))
return null;
parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(sourceCode, this._breakpointsContentTreeOutline);
// Mark disabled breakpoints as resolved if there is source code loaded with that URL.
// This gives the illusion the breakpoint was resolved, but since we don't send disabled
// breakpoints to the backend we don't know for sure. If the user enables the breakpoint
// it will be resolved properly.
if (breakpoint.disabled)
breakpoint.resolved = true;
}
let breakpointTreeElement = new constructor(breakpoint, options);
this._addTreeElement(breakpointTreeElement, parentTreeElement);
if (parentTreeElement.children.length === 1)
parentTreeElement.expand();
return breakpointTreeElement;
}
_removeBreakpoint(breakpoint)
{
if (this._pauseReasonTreeOutline) {
let pauseReasonBreakpointTreeElement = this._pauseReasonTreeOutline.getCachedTreeElement(breakpoint);
if (pauseReasonBreakpointTreeElement)
pauseReasonBreakpointTreeElement.status = null;
}
let breakpointTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(breakpoint);
if (!breakpointTreeElement)
return;
this._removeDebuggerTreeElement(breakpointTreeElement);
}
_addBreakpointsForSourceCode(sourceCode)
{
for (let breakpoint of WI.debuggerManager.breakpointsForSourceCode(sourceCode))
this._addBreakpoint(breakpoint, sourceCode);
}
_addIssuesForSourceCode(sourceCode)
{
var issues = WI.consoleManager.issuesForSourceCode(sourceCode);
for (var issue of issues)
this._addIssue(issue);
}
_addTreeElementForSourceCodeToTreeOutline(sourceCode, treeOutline)
{
let treeElement = treeOutline.getCachedTreeElement(sourceCode);
if (!treeElement) {
if (sourceCode instanceof WI.SourceMapResource)
treeElement = new WI.SourceMapResourceTreeElement(sourceCode);
else if (sourceCode instanceof WI.Resource)
treeElement = new WI.ResourceTreeElement(sourceCode);
else if (sourceCode instanceof WI.Script)
treeElement = new WI.ScriptTreeElement(sourceCode);
}
if (!treeElement) {
console.error("Unknown sourceCode instance", sourceCode);
return null;
}
if (!treeElement.parent) {
treeElement.hasChildren = false;
treeElement.expand();
this._addTreeElement(treeElement, treeOutline);
}
return treeElement;
}
_addResourcesRecursivelyForFrame(frame)
{
this._addResource(frame.mainResource);
for (let resource of frame.resourceCollection)
this._addResource(resource);
for (let childFrame of frame.childFrameCollection)
this._addResourcesRecursivelyForFrame(childFrame);
}
_resourceAdded(event)
{
this._addResource(event.data.resource);
}
_addResource(resource)
{
if (![WI.Resource.Type.Document, WI.Resource.Type.Script].includes(resource.type))
return;
let treeElement = this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline);
this._addBreakpointsForSourceCode(resource);
this._addIssuesForSourceCode(resource);
if (this.parentSidebar && !this.contentBrowser.currentContentView)
this.showDefaultContentViewForTreeElement(treeElement);
}
_mainResourceDidChange(event)
{
if (event.target.isMainFrame()) {
// Aggressively prune resources now so the old resources are removed before
// the new main resource is added below. This avoids a visual flash when the
// prune normally happens on a later event loop cycle.
this.pruneStaleResourceTreeElements();
this.contentBrowser.contentViewContainer.closeAllContentViews();
for (let domBreakpoint of WI.domDebuggerManager.domBreakpoints)
this._removeBreakpoint(domBreakpoint);
}
if (!event.data.oldMainResource) {
var resource = event.target.mainResource;
this._addTreeElementForSourceCodeToTreeOutline(resource, this._scriptsContentTreeOutline);
this._addBreakpointsForSourceCode(resource);
this._addIssuesForSourceCode(resource);
}
}
_handleTimelineCapturingStateChanged(event)
{
this._updateTemporarilyDisabledBreakpointsButtons();
switch (WI.timelineManager.capturingState) {
case WI.TimelineManager.CapturingState.Starting:
this.contentView.element.insertBefore(this._timelineRecordingWarningElement, this.contentView.element.firstChild);
break;
case WI.TimelineManager.CapturingState.Inactive:
this._timelineRecordingWarningElement.remove();
break;
}
this._updateBreakpointsDisabledBanner();
}
_handleAuditManagerTestScheduled(event)
{
this._updateTemporarilyDisabledBreakpointsButtons();
this.contentView.element.insertBefore(this._auditTestWarningElement, this.contentView.element.firstChild);
this._updateBreakpointsDisabledBanner();
}
_handleAuditManagerTestCompleted(event)
{
this._updateTemporarilyDisabledBreakpointsButtons();
this._auditTestWarningElement.remove();
this._updateBreakpointsDisabledBanner();
}
_updateTemporarilyDisabledBreakpointsButtons()
{
let breakpointsDisabledTemporarily = WI.debuggerManager.breakpointsDisabledTemporarily;
this._debuggerBreakpointsButtonItem.enabled = !breakpointsDisabledTemporarily;
this._debuggerPauseResumeButtonItem.enabled = !breakpointsDisabledTemporarily;
}
_updateBreakpointsDisabledBanner()
{
let breakpointsEnabled = WI.debuggerManager.breakpointsEnabled;
let timelineWarningShowing = !!this._timelineRecordingWarningElement.parentElement;
let auditWarningShowing = !!this._auditTestWarningElement.parentElement;
if (!breakpointsEnabled && !timelineWarningShowing && !auditWarningShowing)
this.contentView.element.insertBefore(this._breakpointsDisabledWarningElement, this.contentView.element.firstChild);
else
this._breakpointsDisabledWarningElement.remove();
}
_scriptAdded(event)
{
this._addScript(event.data.script);
}
_addScript(script)
{
// COMPATIBILITY(iOS 9): Backends could send the frontend built-in code, filter out JSC internals.
if (!script.url && !script.sourceURL)
return null;
// In general, do not show dynamically added script elements.
if (script.dynamicallyAddedScriptElement && !script.sourceURL)
return null;
// Don't add breakpoints if the script is represented by a Resource. They were
// already added by _resourceAdded.
if (script.resource)
return null;
let treeElement = this._addTreeElementForSourceCodeToTreeOutline(script, this._scriptsContentTreeOutline);
this._addBreakpointsForSourceCode(script);
this._addIssuesForSourceCode(script);
if (this.parentSidebar && !this.contentBrowser.currentContentView)
this.showDefaultContentViewForTreeElement(treeElement);
return treeElement;
}
_scriptRemoved(event)
{
function removeScript(script, treeOutline)
{
let scriptTreeElement = treeOutline.getCachedTreeElement(script);
if (scriptTreeElement)
scriptTreeElement.parent.removeChild(scriptTreeElement);
}
let script = event.data.script;
removeScript(script, this._breakpointsContentTreeOutline);
removeScript(script, this._scriptsContentTreeOutline);
}
_scriptsCleared(event)
{
for (var i = this._breakpointsContentTreeOutline.children.length - 1; i >= 0; --i) {
var treeElement = this._breakpointsContentTreeOutline.children[i];
if (!(treeElement instanceof WI.ScriptTreeElement))
continue;
this._breakpointsContentTreeOutline.removeChildAtIndex(i, true, true);
}
this._scriptsContentTreeOutline.removeChildren();
this._addResourcesRecursivelyForFrame(WI.networkManager.mainFrame);
}
_breakpointAdded(event)
{
var breakpoint = event.data.breakpoint;
this._addBreakpoint(breakpoint);
}
_breakpointRemoved(event)
{
var breakpoint = event.data.breakpoint;
this._removeBreakpoint(breakpoint);
}
_findThreadTreeElementForTarget(target)
{
for (let child of this._callStackTreeOutline.children) {
if (child.target === target)
return child;
}
return null;
}
_targetAdded(event)
{
this._addTarget(event.data.target);
}
_addTarget(target)
{
let treeElement = new WI.ThreadTreeElement(target);
this._callStackTreeOutline.appendChild(treeElement);
// FIXME: When WI.mainTarget changes?
if (target === WI.mainTarget)
this._mainTargetTreeElement = treeElement;
this._updateCallStackTreeOutline();
}
_targetRemoved(event)
{
let target = event.data.target;
let treeElement = this._findThreadTreeElementForTarget(target);
this._callStackTreeOutline.removeChild(treeElement);
this._updateCallStackTreeOutline();
}
_updateCallStackTreeOutline()
{
let singleThreadShowing = WI.targets.length <= 1;
this._callStackTreeOutline.element.classList.toggle("single-thread", singleThreadShowing);
if (this._mainTargetTreeElement)
this._mainTargetTreeElement.selectable = !singleThreadShowing;
}
_handleDebuggerObjectDisplayLocationDidChange(event)
{
var debuggerObject = event.target;
if (event.data.oldDisplaySourceCode === debuggerObject.sourceCodeLocation.displaySourceCode)
return;
// A known debugger object (breakpoint, issueMessage, etc.) moved between resources. Remove
// the old tree element and create a new tree element with the updated file.
let wasSelected = false;
let oldDebuggerTreeElement = this._breakpointsContentTreeOutline.getCachedTreeElement(debuggerObject);
if (oldDebuggerTreeElement)
wasSelected = oldDebuggerTreeElement.selected;
let newDebuggerTreeElement = null;
if (debuggerObject instanceof WI.Breakpoint)
newDebuggerTreeElement = this._addBreakpoint(debuggerObject);
else if (debuggerObject instanceof WI.IssueMessage)
newDebuggerTreeElement = this._addIssue(debuggerObject);
if (!newDebuggerTreeElement)
return;
if (oldDebuggerTreeElement)
this._removeDebuggerTreeElement(oldDebuggerTreeElement);
if (wasSelected)
newDebuggerTreeElement.revealAndSelect(true, false, true);
}
_removeDebuggerTreeElement(debuggerTreeElement)
{
// If this is a BreakpointTreeElement being deleted because of a cause
// outside of the TreeOutline then deselect if it is selected to avoid
// TreeOutline selection changes causing unexpected ContentView changes.
if (!debuggerTreeElement.__deletedViaDeleteKeyboardShortcut)
debuggerTreeElement.deselect();
let parentTreeElement = debuggerTreeElement.parent;
parentTreeElement.removeChild(debuggerTreeElement);
if (parentTreeElement.children.length || parentTreeElement === this._breakpointsContentTreeOutline)
return;
parentTreeElement.treeOutline.removeChild(parentTreeElement);
}
_debuggerCallFramesDidChange(event)
{
let target = event.data.target;
let treeElement = this._findThreadTreeElementForTarget(target);
treeElement.refresh();
let activeCallFrameTreeElement = this._callStackTreeOutline.findTreeElement(WI.debuggerManager.activeCallFrame);
if (activeCallFrameTreeElement)
activeCallFrameTreeElement.reveal();
}
_debuggerActiveCallFrameDidChange()
{
if (this._activeCallFrameTreeElement) {
this._activeCallFrameTreeElement.isActiveCallFrame = false;
this._activeCallFrameTreeElement = null;
}
if (!WI.debuggerManager.activeCallFrame)
return;
this._activeCallFrameTreeElement = this._callStackTreeOutline.findTreeElement(WI.debuggerManager.activeCallFrame);
if (this._activeCallFrameTreeElement)
this._activeCallFrameTreeElement.isActiveCallFrame = true;
}
_breakpointsBeneathTreeElement(treeElement)
{
console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
return [];
var breakpoints = [];
var breakpointTreeElements = treeElement.children;
for (var i = 0; i < breakpointTreeElements.length; ++i) {
console.assert(breakpointTreeElements[i] instanceof WI.BreakpointTreeElement);
console.assert(breakpointTreeElements[i].breakpoint);
var breakpoint = breakpointTreeElements[i].breakpoint;
if (breakpoint)
breakpoints.push(breakpoint);
}
return breakpoints;
}
_removeAllBreakpoints(breakpoints)
{
for (var i = 0; i < breakpoints.length; ++i) {
var breakpoint = breakpoints[i];
if (WI.debuggerManager.isBreakpointRemovable(breakpoint))
WI.debuggerManager.removeBreakpoint(breakpoint);
}
}
_toggleAllBreakpoints(breakpoints, disabled)
{
for (var i = 0; i < breakpoints.length; ++i)
breakpoints[i].disabled = disabled;
}
_breakpointTreeOutlineDeleteTreeElement(treeElement)
{
console.assert(treeElement.selected);
if (treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject) {
let eventBreakpointsOnWindow = WI.domManager.eventListenerBreakpoints.filter((eventBreakpoint) => eventBreakpoint.eventListener.onWindow);
for (let eventBreakpoint of eventBreakpointsOnWindow)
WI.domManager.removeBreakpointForEventListener(eventBreakpoint.eventListener);
return true;
}
console.assert(treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement);
if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
return false;
var wasTopResourceTreeElement = treeElement.previousSibling === this._assertionsBreakpointTreeElement || treeElement.previousSibling === this._allUncaughtExceptionsBreakpointTreeElement;
var nextSibling = treeElement.nextSibling;
var breakpoints = this._breakpointsBeneathTreeElement(treeElement);
this._removeAllBreakpoints(breakpoints);
if (wasTopResourceTreeElement && nextSibling)
nextSibling.select(true, true);
return true;
}
_breakpointTreeOutlineContextMenuTreeElement(contextMenu, event, treeElement)
{
// This check is necessary since the context menu is created by the TreeOutline, meaning
// that any child could be the target of the context menu event.
if (!(treeElement instanceof WI.ResourceTreeElement) && !(treeElement instanceof WI.ScriptTreeElement))
return;
let breakpoints = this._breakpointsBeneathTreeElement(treeElement);
let shouldDisable = breakpoints.some((breakpoint) => !breakpoint.disabled);
let removeAllResourceBreakpoints = () => {
this._removeAllBreakpoints(breakpoints);
};
let toggleAllResourceBreakpoints = () => {
this._toggleAllBreakpoints(breakpoints, shouldDisable);
};
if (shouldDisable)
contextMenu.appendItem(WI.UIString("Disable Breakpoints"), toggleAllResourceBreakpoints);
else
contextMenu.appendItem(WI.UIString("Enable Breakpoints"), toggleAllResourceBreakpoints);
contextMenu.appendItem(WI.UIString("Delete Breakpoints"), removeAllResourceBreakpoints);
}
_treeSelectionDidChange(event)
{
if (!this.selected)
return;
let treeElement = event.target.selectedTreeElement;
if (!treeElement)
return;
if (treeElement instanceof WI.DOMNodeTreeElement
|| treeElement instanceof WI.DOMBreakpointTreeElement
|| treeElement instanceof WI.EventBreakpointTreeElement
|| treeElement instanceof WI.URLBreakpointTreeElement)
return;
if (treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject)
return;
const options = {
ignoreNetworkTab: true,
ignoreSearchTab: true,
};
if (treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement) {
WI.showSourceCode(treeElement.representedObject, options);
return;
}
if (treeElement instanceof WI.CallFrameTreeElement) {
let callFrame = treeElement.callFrame;
if (callFrame.id)
WI.debuggerManager.activeCallFrame = callFrame;
if (callFrame.sourceCodeLocation)
WI.showSourceCodeLocation(callFrame.sourceCodeLocation, options);
return;
}
if (treeElement instanceof WI.IssueTreeElement) {
WI.showSourceCodeLocation(treeElement.issueMessage.sourceCodeLocation, options);
return;
}
if (!(treeElement instanceof WI.BreakpointTreeElement))
return;
let breakpoint = treeElement.breakpoint;
if (treeElement.treeOutline === this._pauseReasonTreeOutline) {
WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options);
return;
}
if (!treeElement.parent.representedObject)
return;
console.assert(treeElement.parent.representedObject instanceof WI.SourceCode);
if (!(treeElement.parent.representedObject instanceof WI.SourceCode))
return;
WI.showSourceCodeLocation(breakpoint.sourceCodeLocation, options);
}
_addTreeElement(treeElement, parentTreeElement)
{
if (!parentTreeElement)
parentTreeElement = this._breakpointsContentTreeOutline;
let comparator = (a, b) => {
const rankFunctions = [
(treeElement) => treeElement.representedObject === WI.debuggerManager.allExceptionsBreakpoint,
(treeElement) => treeElement.representedObject === WI.debuggerManager.uncaughtExceptionsBreakpoint,
(treeElement) => treeElement.representedObject === WI.debuggerManager.assertionFailuresBreakpoint,
(treeElement) => treeElement instanceof WI.BreakpointTreeElement || treeElement instanceof WI.ResourceTreeElement || treeElement instanceof WI.ScriptTreeElement,
(treeElement) => treeElement instanceof WI.EventBreakpointTreeElement,
(treeElement) => treeElement instanceof WI.DOMNodeTreeElement,
(treeElement) => treeElement.representedObject === DebuggerSidebarPanel.__windowEventTargetRepresentedObject,
(treeElement) => treeElement instanceof WI.DOMBreakpointTreeElement,
(treeElement) => treeElement.representedObject === WI.domDebuggerManager.allRequestsBreakpoint,
(treeElement) => treeElement instanceof WI.URLBreakpointTreeElement,
];
let aRank = rankFunctions.findIndex((rankFunction) => rankFunction(a));
let bRank = rankFunctions.findIndex((rankFunction) => rankFunction(b));
if (aRank >= 0 && bRank >= 0) {
if (aRank < bRank)
return -1;
if (bRank < aRank)
return 1;
}
if (a instanceof WI.BreakpointTreeElement && b instanceof WI.BreakpointTreeElement)
return this._compareBreakpointTreeElements(a, b);
return a.mainTitle.extendedLocaleCompare(b.mainTitle) || a.subtitle.extendedLocaleCompare(b.subtitle);
};
parentTreeElement.insertChild(treeElement, insertionIndexForObjectInListSortedByFunction(treeElement, parentTreeElement.children, comparator));
}
_compareBreakpointTreeElements(a, b)
{
if (!a.representedObject || !b.representedObject)
return 0;
let aLocation = a.representedObject.sourceCodeLocation;
let bLocation = b.representedObject.sourceCodeLocation;
if (!aLocation || !bLocation)
return 0;
var comparisonResult = aLocation.displayLineNumber - bLocation.displayLineNumber;
if (comparisonResult !== 0)
return comparisonResult;
return aLocation.displayColumnNumber - bLocation.displayColumnNumber;
}
_updatePauseReason()
{
this._pauseReasonTreeOutline = null;
this._updatePauseReasonGotoArrow();
return this._updatePauseReasonSection();
}
_updatePauseReasonSection()
{
let target = WI.debuggerManager.activeCallFrame.target;
let targetData = WI.debuggerManager.dataForTarget(target);
let {pauseReason, pauseData} = targetData;
switch (pauseReason) {
case WI.DebuggerManager.PauseReason.AnimationFrame:
console.assert(pauseData, "Expected data with an animation frame, but found none.");
if (!pauseData)
return false;
var eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.AnimationFrame, pauseData.eventName);
console.assert(eventBreakpoint, "Expected AnimationFrame breakpoint for event name.", pauseData.eventName);
if (!eventBreakpoint)
return false;
this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
title: WI.UIString("%s Fired").format(pauseData.eventName),
});
this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
var eventBreakpointRow = new WI.DetailsSectionRow;
eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
this._pauseReasonGroup.rows = [eventBreakpointRow];
return true;
case WI.DebuggerManager.PauseReason.Assertion:
// FIXME: We should include the assertion condition string.
console.assert(pauseData, "Expected data with an assertion, but found none.");
if (pauseData && pauseData.message) {
this._pauseReasonTextRow.text = WI.UIString("Assertion with message: %s").format(pauseData.message);
return true;
}
this._pauseReasonTextRow.text = WI.UIString("Assertion Failed");
this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
return true;
case WI.DebuggerManager.PauseReason.Breakpoint:
console.assert(pauseData, "Expected breakpoint identifier, but found none.");
if (pauseData && pauseData.breakpointId) {
this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
this._pauseReasonTreeOutline.addEventListener(WI.TreeOutline.Event.SelectionDidChange, this._treeSelectionDidChange, this);
let breakpoint = WI.debuggerManager.breakpointForIdentifier(pauseData.breakpointId);
let breakpointTreeElement = new WI.BreakpointTreeElement(breakpoint, {
className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
title: WI.UIString("Triggered Breakpoint"),
});
let breakpointDetailsSection = new WI.DetailsSectionRow;
this._pauseReasonTreeOutline.appendChild(breakpointTreeElement);
breakpointDetailsSection.element.appendChild(this._pauseReasonTreeOutline.element);
this._pauseReasonGroup.rows = [breakpointDetailsSection];
return true;
}
break;
case WI.DebuggerManager.PauseReason.CSPViolation:
console.assert(pauseData, "Expected data with a CSP Violation, but found none.");
if (pauseData) {
// COMPATIBILITY (iOS 8): 'directive' was 'directiveText'.
this._pauseReasonTextRow.text = WI.UIString("Content Security Policy violation of directive: %s").format(pauseData.directive || pauseData.directiveText);
this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
return true;
}
break;
case WI.DebuggerManager.PauseReason.DebuggerStatement:
this._pauseReasonTextRow.text = WI.UIString("Debugger Statement");
this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
return true;
case WI.DebuggerManager.PauseReason.DOM:
console.assert(WI.domDebuggerManager.supported);
console.assert(pauseData, "Expected DOM breakpoint data, but found none.");
if (pauseData && pauseData.nodeId) {
let domNode = WI.domManager.nodeForId(pauseData.nodeId);
let domBreakpoints = WI.domDebuggerManager.domBreakpointsForNode(domNode);
let domBreakpoint;
for (let breakpoint of domBreakpoints) {
if (breakpoint.type === pauseData.type) {
domBreakpoint = breakpoint;
break;
}
}
if (!domBreakpoint)
return;
this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
let type = WI.DOMBreakpointTreeElement.displayNameForType(domBreakpoint.type);
let domBreakpointTreeElement = new WI.DOMBreakpointTreeElement(domBreakpoint, {
className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
title: type,
});
let domBreakpointRow = new WI.DetailsSectionRow;
this._pauseReasonTreeOutline.appendChild(domBreakpointTreeElement);
domBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
let ownerElementRow = new WI.DetailsSectionSimpleRow(WI.UIString("Element"), WI.linkifyNodeReference(domNode));
this._pauseReasonGroup.rows = [domBreakpointRow, ownerElementRow];
if (domBreakpoint.type !== WI.DOMBreakpoint.Type.SubtreeModified)
return true;
console.assert(pauseData.targetNode);
let remoteObject = WI.RemoteObject.fromPayload(pauseData.targetNode, target);
remoteObject.pushNodeToFrontend((nodeId) => {
if (!nodeId)
return;
let node = WI.domManager.nodeForId(nodeId);
console.assert(node, "Missing node for id.", nodeId);
if (!node)
return;
let fragment = document.createDocumentFragment();
let description = pauseData.insertion ? WI.UIString("Child added to ") : WI.UIString("Removed descendant ");
fragment.append(description, WI.linkifyNodeReference(node));
let targetDescriptionRow = new WI.DetailsSectionSimpleRow(WI.UIString("Details"), fragment);
targetDescriptionRow.element.classList.add("target-description");
this._pauseReasonGroup.rows = this._pauseReasonGroup.rows.concat(targetDescriptionRow);
});
return true;
}
break;
case WI.DebuggerManager.PauseReason.EventListener:
console.assert(pauseData, "Expected data with an event listener, but found none.");
if (!pauseData)
return false;
var eventBreakpoint = null;
if (pauseData.eventListenerId)
eventBreakpoint = WI.domManager.breakpointForEventListenerId(pauseData.eventListenerId);
if (!eventBreakpoint)
eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.Listener, pauseData.eventName);
console.assert(eventBreakpoint, "Expected Event Listener breakpoint for event name.", pauseData.eventName);
if (!eventBreakpoint)
return false;
this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
title: WI.UIString("\u201C%s\u201D Event Fired").format(pauseData.eventName),
});
this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
var eventBreakpointRow = new WI.DetailsSectionRow;
eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
var rows = [eventBreakpointRow];
var eventListener = eventBreakpoint.eventListener;
if (eventListener) {
console.assert(eventListener.eventListenerId === pauseData.eventListenerId);
let value = null;
if (eventListener.onWindow)
value = WI.unlocalizedString("window");
else if (eventListener.node)
value = WI.linkifyNodeReference(eventListener.node);
if (value)
rows.push(new WI.DetailsSectionSimpleRow(WI.UIString("Target"), value));
}
this._pauseReasonGroup.rows = rows;
return true;
case WI.DebuggerManager.PauseReason.Exception:
console.assert(pauseData, "Expected data with an exception, but found none.");
if (pauseData) {
// FIXME: We should improve the appearance of thrown objects. This works well for exception strings.
var data = WI.RemoteObject.fromPayload(pauseData, target);
this._pauseReasonTextRow.text = WI.UIString("Exception with thrown value: %s").format(data.description);
this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
return true;
}
break;
case WI.DebuggerManager.PauseReason.PauseOnNextStatement:
this._pauseReasonTextRow.text = WI.UIString("Immediate Pause Requested");
this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
return true;
case WI.DebuggerManager.PauseReason.Timer:
console.assert(pauseData, "Expected data with a timer, but found none.");
if (!pauseData)
return false;
var eventBreakpoint = WI.domDebuggerManager.eventBreakpointForTypeAndEventName(WI.EventBreakpoint.Type.Timer, pauseData.eventName);
console.assert(eventBreakpoint, "Expected Timer breakpoint for event name.", pauseData.eventName);
if (!eventBreakpoint)
return false;
this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
var eventBreakpointTreeElement = new WI.EventBreakpointTreeElement(eventBreakpoint, {
className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
title: WI.UIString("%s Fired").format(pauseData.eventName),
});
this._pauseReasonTreeOutline.appendChild(eventBreakpointTreeElement);
var eventBreakpointRow = new WI.DetailsSectionRow;
eventBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
this._pauseReasonGroup.rows = [eventBreakpointRow];
return true;
case WI.DebuggerManager.PauseReason.Fetch:
case WI.DebuggerManager.PauseReason.XHR:
console.assert(WI.domDebuggerManager.supported);
console.assert(pauseData, "Expected URL breakpoint data, but found none.");
if (pauseData) {
if (pauseData.breakpointURL) {
let urlBreakpoint = WI.domDebuggerManager.urlBreakpointForURL(pauseData.breakpointURL);
console.assert(urlBreakpoint, "Expected URL breakpoint for URL.", pauseData.breakpointURL);
this._pauseReasonTreeOutline = this.createContentTreeOutline({suppressFiltering: true});
let urlBreakpointTreeElement = new WI.URLBreakpointTreeElement(urlBreakpoint, {
className: WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName,
title: WI.DOMDebuggerManager.supportsURLBreakpoints() ? WI.UIString("Triggered URL Breakpoint") : WI.UIString("Triggered XHR Breakpoint"),
});
let urlBreakpointRow = new WI.DetailsSectionRow;
this._pauseReasonTreeOutline.appendChild(urlBreakpointTreeElement);
urlBreakpointRow.element.appendChild(this._pauseReasonTreeOutline.element);
this._pauseReasonTextRow.text = pauseData.url;
this._pauseReasonGroup.rows = [urlBreakpointRow, this._pauseReasonTextRow];
} else {
console.assert(pauseData.breakpointURL === "", "Should be the All Requests breakpoint which has an empty URL");
this._pauseReasonTextRow.text = WI.UIString("Requesting: %s").format(pauseData.url);
this._pauseReasonGroup.rows = [this._pauseReasonTextRow];
}
this._pauseReasonTextRow.element.title = pauseData.url;
return true;
}
break;
case WI.DebuggerManager.PauseReason.Other:
console.error("Paused for unknown reason. We should always have a reason.");
break;
}
return false;
}
_updatePauseReasonGotoArrow()
{
this._pauseReasonLinkContainerElement.removeChildren();
var activeCallFrame = WI.debuggerManager.activeCallFrame;
if (!activeCallFrame)
return;
var sourceCodeLocation = activeCallFrame.sourceCodeLocation;
if (!sourceCodeLocation)
return;
const options = {
useGoToArrowButton: true,
ignoreNetworkTab: true,
ignoreSearchTab: true,
};
let linkElement = WI.createSourceCodeLocationLink(sourceCodeLocation, options);
this._pauseReasonLinkContainerElement.appendChild(linkElement);
}
_addIssue(issueMessage)
{
let issueTreeElement = this._scriptsContentTreeOutline.findTreeElement(issueMessage);
if (issueTreeElement)
return issueTreeElement;
let parentTreeElement = this._addTreeElementForSourceCodeToTreeOutline(issueMessage.sourceCodeLocation.sourceCode, this._scriptsContentTreeOutline);
if (!parentTreeElement)
return null;
issueTreeElement = new WI.IssueTreeElement(issueMessage);
parentTreeElement.insertChild(issueTreeElement, insertionIndexForObjectInListSortedByFunction(issueTreeElement, parentTreeElement.children, this._compareBreakpointTreeElements));
if (parentTreeElement.children.length === 1)
parentTreeElement.expand();
return issueTreeElement;
}
_handleDOMBreakpointDOMNodeChanged(event)
{
let breakpoint = event.target;
if (breakpoint.domNodeIdentifier)
this._addBreakpoint(breakpoint);
else
this._removeBreakpoint(breakpoint);
}
_handleIssueAdded(event)
{
var issue = event.data.issue;
// We only want to show issues originating from JavaScript source code.
if (!issue.sourceCodeLocation || !issue.sourceCodeLocation.sourceCode || (issue.source !== "javascript" && issue.source !== "console-api"))
return;
this._addIssue(issue);
}
_handleIssuesCleared(event)
{
let currentTreeElement = this._scriptsContentTreeOutline.children[0];
let issueTreeElements = [];
while (currentTreeElement && !currentTreeElement.root) {
if (currentTreeElement instanceof WI.IssueTreeElement)
issueTreeElements.push(currentTreeElement);
currentTreeElement = currentTreeElement.traverseNextTreeElement(false, null, true);
}
issueTreeElements.forEach((treeElement) => treeElement.parent.removeChild(treeElement));
}
_handleBreakpointElementAddedOrRemoved(event)
{
let treeElement = event.data.element;
let setting = null;
if (treeElement.breakpoint === WI.debuggerManager.assertionFailuresBreakpoint)
setting = WI.settings.showAssertionFailuresBreakpoint;
else if (treeElement.representedObject === WI.domDebuggerManager.allRequestsBreakpoint)
setting = WI.settings.showAllRequestsBreakpoint;
if (setting)
setting.value = !!treeElement.parent;
}
_handleCreateBreakpointMouseDown(event)
{
if (this._ignoreCreateBreakpointMouseDown)
return;
this._ignoreCreateBreakpointMouseDown = true;
let contextMenu = WI.ContextMenu.createFromEvent(event);
contextMenu.addBeforeShowCallback(() => {
this._ignoreCreateBreakpointMouseDown = false;
});
// COMPATIBILITY (iOS 10): DebuggerAgent.setPauseOnAssertions did not exist yet.
if (InspectorBackend.domains.Debugger.setPauseOnAssertions) {
let assertionFailuresBreakpointShown = WI.settings.showAssertionFailuresBreakpoint.value;
contextMenu.appendCheckboxItem(WI.repeatedUIString.assertionFailures(), () => {
if (assertionFailuresBreakpointShown)
WI.debuggerManager.removeBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
else {
WI.debuggerManager.assertionFailuresBreakpoint.disabled = false;
WI.debuggerManager.addBreakpoint(WI.debuggerManager.assertionFailuresBreakpoint);
}
}, assertionFailuresBreakpointShown);
}
if (WI.domDebuggerManager.supported) {
contextMenu.appendSeparator();
contextMenu.appendItem(WI.UIString("Event Breakpoint\u2026"), () => {
let popover = new WI.EventBreakpointPopover(this);
popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
});
contextMenu.appendSeparator();
let allRequestsBreakpointShown = WI.settings.showAllRequestsBreakpoint.value;
contextMenu.appendCheckboxItem(WI.repeatedUIString.allRequests(), () => {
if (allRequestsBreakpointShown)
WI.domDebuggerManager.removeURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint);
else {
WI.domDebuggerManager.allRequestsBreakpoint.disabled = false;
WI.domDebuggerManager.addURLBreakpoint(WI.domDebuggerManager.allRequestsBreakpoint);
}
}, allRequestsBreakpointShown);
contextMenu.appendItem(WI.DOMDebuggerManager.supportsURLBreakpoints() ? WI.UIString("URL Breakpoint\u2026") : WI.UIString("XHR Breakpoint\u2026"), () => {
let popover = new WI.URLBreakpointPopover(this);
popover.show(this._createBreakpointButton.element, [WI.RectEdge.MAX_Y, WI.RectEdge.MIN_Y, WI.RectEdge.MAX_X]);
});
}
contextMenu.show();
}
};
WI.DebuggerSidebarPanel.DebuggerPausedStyleClassName = "paused";
WI.DebuggerSidebarPanel.ExceptionIconStyleClassName = "breakpoint-exception-icon";
WI.DebuggerSidebarPanel.AssertionIconStyleClassName = "breakpoint-assertion-icon";
WI.DebuggerSidebarPanel.PausedBreakpointIconStyleClassName = "breakpoint-paused-icon";
WI.DebuggerSidebarPanel.SelectedAllExceptionsCookieKey = "debugger-sidebar-panel-all-exceptions-breakpoint";
WI.DebuggerSidebarPanel.SelectedUncaughtExceptionsCookieKey = "debugger-sidebar-panel-uncaught-exceptions-breakpoint";
WI.DebuggerSidebarPanel.SelectedAssertionFailuresCookieKey = "debugger-sidebar-panel-assertion-failures-breakpoint";
WI.DebuggerSidebarPanel.SelectedAllRequestsCookieKey = "debugger-sidebar-panel-all-requests-breakpoint";