UNPKG

@react-native/debugger-frontend

Version:
163 lines (162 loc) 36.2 kB
import"../../../ui/components/icon_button/icon_button.js";import*as e from"../../../core/common/common.js";import*as t from"../../../core/host/host.js";import*as n from"../../../core/i18n/i18n.js";import*as o from"../../../core/platform/platform.js";import{assertNotNullOrUndefined as i}from"../../../core/platform/platform.js";import*as a from"../../../core/sdk/sdk.js";import*as r from"../../../models/bindings/bindings.js";import*as s from"../../../models/breakpoints/breakpoints.js";import*as l from"../../../models/text_utils/text_utils.js";import*as c from"../../../models/workspace/workspace.js";import*as d from"../../../ui/components/input/input.js";import*as p from"../../../ui/components/legacy_wrapper/legacy_wrapper.js";import*as h from"../../../ui/components/render_coordinator/render_coordinator.js";import*as u from"../../../ui/legacy/legacy.js";import*as g from"../../../ui/lit/lit.js";import*as m from"../../../ui/visual_logging/visual_logging.js";import*as b from"../../../models/persistence/persistence.js";import"../../../ui/components/buttons/buttons.js";import*as k from"../../../ui/components/helpers/helpers.js";var v={cssText:`:host{flex:auto;display:flex;flex-direction:column}.code-snippet{width:100%;font-family:var(--source-code-font-family);font-size:var(--source-code-font-size);color:var(--sys-color-token-subtle);text-overflow:ellipsis;overflow:hidden;white-space:nowrap;flex-shrink:100;cursor:pointer}.code-snippet:hover{color:var(--sys-color-on-surface);text-decoration:underline}input{height:12px;width:12px;flex-shrink:0;margin:3px 0}details{border-top:1px solid var(--sys-color-divider);padding:2px 0}details:not(.active){background-color:var(--sys-color-state-disabled-container);opacity:30%}details > summary{min-height:20px;list-style:none;display:flex;padding:0 8px 0 6px;align-items:center}details > summary:hover{background-color:var(--sys-color-state-hover-on-subtle)}details > summary::before{display:block;user-select:none;mask-image:var(--image-file-arrow-collapse);background-color:var(--icon-default);content:"";height:var(--sys-size-8);min-width:var(--sys-size-8);max-width:var(--sys-size-8);margin-top:calc(-1 * var(--sys-size-2));margin-left:calc(-1 * var(--sys-size-3));overflow:hidden}details[open] > summary::before{mask-image:var(--image-file-arrow-drop-down)}.group-header{display:inline-flex;align-items:center;width:100%;padding-right:8px;overflow:hidden}.group-icon-or-disable{justify-content:center;display:flex;width:16px;margin-left:2px}.group-header-title{margin-left:4px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.group-header-differentiator{font-weight:normal;color:var(--sys-color-state-disabled);margin-left:8px}.group-hover-actions{display:flex;align-items:center;justify-content:right;font-size:10px;font-weight:500}.breakpoint-item-location-or-actions{min-width:20px;flex:0 0 auto;display:flex;padding-left:8px;justify-content:right}button{cursor:pointer;width:13px;height:13px;border:none;background-color:transparent;display:none;align-items:center;justify-content:center}button + span{padding-left:6px}button + button{padding-left:11px}summary:hover button{display:flex}devtools-icon{width:16px;height:16px;button:hover &{color:var(--icon-default-hover)}}.type-indicator{--override-color-conditional-breakpoint:var(--ref-palette-orange70);--override-color-logpoint:var(--ref-palette-pink60);border-right:4px solid;border-radius:0 2px 2px 0;border-color:transparent;height:16px}.breakpoint-item{display:flex;align-items:center;line-height:13px;height:20px;padding-right:8px}.breakpoint-item.hit{background-color:var(--sys-color-yellow-container);color:var(--sys-color-on-yellow-container)}.breakpoint-item.hit:focus{background-color:var(--sys-color-tonal-container)}.theme-with-dark-background .type-indicator,\n:host-context(.theme-with-dark-background) .type-indicator{--override-color-conditional-breakpoint:var(--ref-palette-yellow60);--override-color-logpoint:var(--ref-palette-pink70)}.breakpoint-item.logpoint > label > .type-indicator{border-color:var(--override-color-logpoint)}.breakpoint-item.conditional-breakpoint > label > .type-indicator{border-color:var(--override-color-conditional-breakpoint)}.checkbox-label{display:flex;align-items:center}.checkbox-label > input{margin-left:16px;margin-right:6px}devtools-icon[name="file-script"]{color:var(--icon-file-script);width:18px;height:18px;summary:hover &{display:none}}input.group-checkbox{margin:0;display:none}summary:hover .group-checkbox{display:flex}.location{line-height:14px;text-overflow:ellipsis;overflow:hidden}.breakpoint-item:hover button{display:flex}.pause-on-uncaught-exceptions{margin-top:3px}.pause-on-caught-exceptions{margin-bottom:3px}input:disabled + span{color:var(--sys-color-state-disabled)}.pause-on-caught-exceptions > .checkbox-label > input,\n.pause-on-uncaught-exceptions > .checkbox-label > input{margin-left:6px}.pause-on-caught-exceptions > .checkbox-label > span,\n.pause-on-uncaught-exceptions > .checkbox-label > span{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.pause-on-uncaught-exceptions,\n.pause-on-caught-exceptions{line-height:13px;height:18px;padding-right:8px;& > label{width:fit-content}}details > summary:focus,\n.breakpoint-item:focus,\n.pause-on-uncaught-exceptions:focus,\n.pause-on-caught-exceptions:focus{background-color:var(--sys-color-tonal-container);outline-width:0}\n/*# sourceURL=${import.meta.resolve("./breakpointsView.css")} */\n`};const f=e=>null!==e.getAttribute("data-first-pause")||null!==e.getAttribute("data-last-pause"),x=e=>!(e=>"treeitem"===e.getAttribute("role"))(e),y=e=>null!==e.getAttribute("open"),w=e=>e.querySelector("[data-first-breakpoint]"),S=e=>{const t=E(e);return t&&t instanceof HTMLDetailsElement?t?.querySelector("summary"):null},C=e=>e.querySelector("summary"),E=e=>{const t=e.nextElementSibling;return t&&t instanceof HTMLDetailsElement?t:null};async function I(e,t,n){if(f(e))return function(e,t){console.assert(f(e));let n=null;switch(t){case"ArrowUp":{const t=e.previousElementSibling;t instanceof HTMLElement&&(n=t,console.assert(f(n)));break}case"ArrowDown":{const t=e.nextElementSibling;if(t instanceof HTMLElement)if("tree"===t.getAttribute("role")){const e=t.querySelector("[data-first-group]");e&&(n=C(e))}else n=t,console.assert(f(n));break}}return n}(e,t);const o=e.parentElement;if(!(o&&o instanceof HTMLDetailsElement))throw new Error("The selected nodes should be direct children of an HTMLDetails element.");let i=null;switch(t){case"ArrowLeft":if(!x(e))return C(o);y(o)&&await n(o,!1);break;case"ArrowRight":if(x(e)){if(y(o))return w(o);await n(o,!0)}break;case"ArrowDown":if(x(e))i=y(o)?w(o):S(o);else{const t=e.nextElementSibling;i=t&&t instanceof HTMLDivElement?t:S(o)}break;case"ArrowUp":if(x(e)){const e=(e=>{const t=e.previousElementSibling;return t&&t instanceof HTMLDetailsElement?t:null})(o);if(e)i=y(e)?(e=>e.querySelector("[data-last-breakpoint]"))(e):C(e);else{const e=o.parentElement?.previousElementSibling;e instanceof HTMLElement&&(i=e)}}else{const t=e.previousElementSibling;t instanceof HTMLElement&&(i=t)}}return i}function B(e,t,n){const o=[];let i=t.filter((t=>t!==e));for(let t=n;t<e.length;++t){const n=e[t];if(o.push(n),i=i.filter((e=>e.length>t&&e[t]===n)),0===i.length)break}return o}function T(t,n){const o=t.map((t=>{const n=e.ParsedURL.ParsedURL.fromString(t)?.folderPathComponents.slice(1);return i(n),n.split("/").reverse()})),a=function(e){const t=e[0];let n=-1;for(let o=0;o<t.length&&-1===n;++o){const i=t[o];for(let t=1;t<e.length;++t){const a=e[t];if(a.length<=o||a[o]!==i){n=o;break}}}return-1===n?t.length:n}(o);for(let e=0;e<o.length;++e){const i=B(o[e],o,a).reverse().join("/");0===a?n.set(t[e],i+"/"):n.set(t[e],i+"/…/")}console.assert(new Set(n.values()).size===t.length,"Differentiators should be unique.")}function L(e){const t=new Map,n=new Map;for(const{name:n,url:o}of e)t.has(n)||t.set(n,[]),t.get(n)?.push(o);for(const e of t.values())e.length>1&&T(e,n);return n}var $=Object.freeze({__proto__:null,findNextNodeForKeyboardNavigation:I,getDifferentiatingPathMap:L});const A=new CSSStyleSheet;A.replaceSync(v.cssText);const{html:O,Directives:{ifDefined:M,repeat:D,classMap:H,live:R}}=g,j={pauseOnUncaughtExceptions:"Pause on uncaught exceptions",pauseOnCaughtExceptions:"Pause on caught exceptions",checked:"checked",unchecked:"unchecked",indeterminate:"mixed",breakpointHit:"{PH1} breakpoint hit",removeAllBreakpointsInFile:"Remove all breakpoints in file",disableAllBreakpointsInFile:"Disable all breakpoints in file",enableAllBreakpointsInFile:"Enable all breakpoints in file",editCondition:"Edit condition",editLogpoint:"Edit logpoint",disableAllBreakpoints:"Disable all breakpoints",enableAllBreakpoints:"Enable all breakpoints",removeBreakpoint:"Remove breakpoint",removeAllBreakpoints:"Remove all breakpoints",removeOtherBreakpoints:"Remove other breakpoints",revealLocation:"Reveal location",conditionCode:"Condition: {PH1}",logpointCode:"Logpoint: {PH1}"},N=n.i18n.registerUIStrings("panels/sources/components/BreakpointsView.ts",j),P=n.i18n.getLocalizedString.bind(void 0,N);let F,U;class W{#e;#t=new WeakMap;#n;#o;#i;#a;#r;#s;#l=!1;#c=!1;constructor(t,n){this.#a=e.Settings.Settings.instance().createSetting("collapsed-files",[]),this.#r=new Set(this.#a.get()),this.#e=t,this.#e.addEventListener(s.BreakpointManager.Events.BreakpointAdded,this.#d,this),this.#e.addEventListener(s.BreakpointManager.Events.BreakpointRemoved,this.#p,this),this.#n=n.moduleSetting("breakpoints-active"),this.#n.addChangeListener(this.update,this),this.#o=n.moduleSetting("pause-on-uncaught-exception"),this.#o.addChangeListener(this.update,this),this.#i=n.moduleSetting("pause-on-caught-exception"),this.#i.addChangeListener(this.update,this)}static instance({forceNew:t,breakpointManager:n,settings:o}={forceNew:null,breakpointManager:s.BreakpointManager.BreakpointManager.instance(),settings:e.Settings.Settings.instance()}){return U&&!t||(U=new W(n,o)),U}static removeInstance(){U=null}static targetSupportsIndependentPauseOnExceptionToggles(){return!a.TargetManager.TargetManager.instance().targets().some((e=>e.type()===a.Target.Type.NODE))}flavorChanged(e){this.update()}breakpointEditFinished(e,n){this.#s&&this.#s===e&&(n&&t.userMetrics.actionTaken(t.UserMetrics.Action.BreakpointConditionEditedFromSidebar),this.#s=void 0)}breakpointStateChanged(e,t){this.#h(e).forEach((e=>{e.breakpoint.setEnabled(t)}))}async breakpointEdited(t,n){const o=this.#h(t);let i;for(const e of o)(!i||e.uiLocation.compareTo(i.uiLocation)<0)&&(i=e);i&&(n&&(this.#s=i.breakpoint),await e.Revealer.reveal(i))}breakpointsRemoved(e){e.flatMap((e=>this.#h(e))).forEach((e=>e?.breakpoint.remove(!1)))}expandedStateChanged(e,t){t?this.#r.delete(e):this.#r.add(e),this.#u()}async jumpToSource(t){const n=this.#h(t).map((e=>e.uiLocation));let o;for(const e of n)(!o||e.compareTo(o)<0)&&(o=e);o&&await e.Revealer.reveal(o)}setPauseOnUncaughtExceptions(e){this.#o.set(e)}setPauseOnCaughtExceptions(e){this.#i.set(e)}async update(){if(this.#l=!0,!this.#c){for(this.#c=!0;this.#l;){this.#l=!1;const e=await this.getUpdatedBreakpointViewData();_.instance().data=e}this.#c=!1}}async getUpdatedBreakpointViewData(){const e=this.#n.get(),t=W.targetSupportsIndependentPauseOnExceptionToggles(),n=this.#o.get(),o=this.#i.get(),i=this.#g();if(!i.length)return{breakpointsActive:e,pauseOnCaughtExceptions:o,pauseOnUncaughtExceptions:n,independentPauseToggles:t,groups:[]};const a=this.#m(i),r=this.#b(i),[s,c]=await Promise.all([this.#k(a),this.#v()]),d=new Map;for(let e=0;e<a.length;e++){const t=a[e],n=t[0],o=n.uiLocation.uiSourceCode.url(),i=n.uiLocation.uiSourceCode.canonicalScriptId(),p=n.uiLocation,h=null!==c&&t.some((e=>e.uiLocation.id()===c.id())),u=r.get(p.lineId()).size>1,g=p.lineAndColumnText(u),m=s[e],b=m instanceof l.WasmDisassembly.WasmDisassembly?m.lines[m.bytecodeOffsetToLineNumber(p.columnNumber??0)]??"":m.textObj.lineAt(p.lineNumber);h&&this.#r.has(o)&&(this.#r.delete(o),this.#u());const k=!this.#r.has(o),v=this.#f(t),{type:f,hoverText:x}=this.#x(t),y={id:n.breakpoint.breakpointStorageId(),location:g,codeSnippet:b,isHit:h,status:v,type:f,hoverText:x};this.#t.set(y,t);let w=d.get(i);if(w)w.breakpointItems.push(y),w.expanded||=k;else{const e=this.#e.supportsConditionalBreakpoints(p.uiSourceCode);w={url:o,name:p.uiSourceCode.displayName(),editable:e,expanded:k,breakpointItems:[y]},d.set(i,w)}}return{breakpointsActive:e,pauseOnCaughtExceptions:o,pauseOnUncaughtExceptions:n,independentPauseToggles:t,groups:Array.from(d.values())}}#d(e){const t=e.data.breakpoint;return"USER_ACTION"===t.origin&&this.#r.has(t.url())&&(this.#r.delete(t.url()),this.#u()),this.update()}#p(e){const t=e.data.breakpoint;if(this.#r.has(t.url())){s.BreakpointManager.BreakpointManager.instance().allBreakpointLocations().some((e=>e.breakpoint.url()===t.url()))||(this.#r.delete(t.url()),this.#u())}return this.update()}#u(){this.#a.set(Array.from(this.#r.values()))}#x(e){const t=e.find((e=>Boolean(e.breakpoint.condition()))),n=t?.breakpoint;if(!n||!n.condition())return{type:"REGULAR_BREAKPOINT"};const o=n.condition();return n.isLogpoint()?{type:"LOGPOINT",hoverText:o}:{type:"CONDITIONAL_BREAKPOINT",hoverText:o}}#h(e){const t=this.#t.get(e);return i(t),t}async#v(){const e=u.Context.Context.instance().flavor(a.DebuggerModel.DebuggerPausedDetails);return e?.callFrames.length?await r.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().rawLocationToUILocation(e.callFrames[0].location()):null}#g(){const e=this.#e.allBreakpointLocations().filter((e=>e.uiLocation.uiSourceCode.project().type()!==c.Workspace.projectTypes.Debugger));e.sort(((e,t)=>e.uiLocation.compareTo(t.uiLocation)));const t=[];let n=null,o=null;for(const i of e)(i.breakpoint!==n||o&&i.uiLocation.compareTo(o))&&(t.push(i),n=i.breakpoint,o=i.uiLocation);return t}#m(e){const t=new o.MapUtilities.Multimap;for(const n of e){const e=n.uiLocation;t.set(e.id(),n)}const n=[];for(const e of t.keysArray()){const o=Array.from(t.get(e));o.length&&n.push(o)}return n}#b(e){const t=new o.MapUtilities.Multimap;for(const n of e){const e=n.uiLocation;t.set(e.lineId(),e.id())}return t}#f(e){const t=e.some((e=>e.breakpoint.enabled())),n=e.some((e=>!e.breakpoint.enabled()));let o;return o=t?n?"INDETERMINATE":"ENABLED":"DISABLED",o}#k(e){return Promise.all(e.map((async([{uiLocation:{uiSourceCode:e}}])=>{const t=await e.requestContentData({cachedWasmOnly:!0});return l.ContentData.ContentData.contentDataOrEmpty(t)})))}}class _ extends p.LegacyWrapper.WrappableComponent{#y;static instance({forceNew:e}={forceNew:!1}){return F&&!e||(F=p.LegacyWrapper.legacyWrapper(u.Widget.Widget,new _)),F.getComponent()}constructor(){super(),this.#y=W.instance(),this.setAttribute("jslog",`${m.section("sources.js-breakpoints")}`),this.#y.update()}#w=this.attachShadow({mode:"open"});#S=!1;#C=!1;#E=!1;#I=!0;#B=[];#T=new Map;set data(e){this.#S=e.pauseOnUncaughtExceptions,this.#C=e.pauseOnCaughtExceptions,this.#E=e.independentPauseToggles,this.#I=e.breakpointsActive,this.#B=e.groups;const t=[];for(const n of e.groups)t.push({name:n.name,url:n.url});this.#T=L(t),this.render()}connectedCallback(){this.#w.adoptedStyleSheets=[d.checkboxStyles,A]}async render(){await h.write("BreakpointsView render",(()=>{const e=async e=>{const t=e.currentTarget;await this.#L(t),e.consume()},t=(this.#E||this.#S)&&this.#C,n=!this.#E&&!this.#S,o=O` <div class='pause-on-uncaught-exceptions' tabindex='0' @click=${e} @keydown=${this.#$} role='checkbox' aria-checked=${this.#S} data-first-pause> <label class='checkbox-label'> <input type='checkbox' tabindex=-1 class="small" ?checked=${this.#S} @change=${this.#A.bind(this)} jslog=${m.toggle("pause-uncaught").track({change:!0})}> <span>${P(j.pauseOnUncaughtExceptions)}</span> </label> </div> <div class='pause-on-caught-exceptions' tabindex='-1' @click=${e} @keydown=${this.#$} role='checkbox' aria-checked=${t} data-last-pause> <label class='checkbox-label'> <input data-pause-on-caught-checkbox type='checkbox' class="small" tabindex=-1 ?checked=${t} ?disabled=${n} @change=${this.#O.bind(this)} jslog=${m.toggle("pause-on-caught-exception").track({change:!0})}> <span>${P(j.pauseOnCaughtExceptions)}</span> </label> </div> <div role=tree> ${D(this.#B,(e=>e.url),((e,t)=>O`${this.#M(e,t)}`))} </div>`;g.render(o,this.#w,{host:this})})),await h.write("BreakpointsView make pause-on-exceptions focusable",(()=>{if(null===this.#w.querySelector('[tabindex="0"]')){const e=this.#w.querySelector("[data-first-pause]");e?.setAttribute("tabindex","0")}}))}async#$(e){if(e.target&&e.target instanceof HTMLElement){if("Home"===e.key||"End"===e.key)return e.consume(!0),await this.#D(e.key);if(o.KeyboardUtilities.keyIsArrowKey(e.key))return e.consume(!0),await this.#H(e.key,e.target);if(o.KeyboardUtilities.isEnterOrSpaceKey(e)){const t=e.currentTarget;await this.#L(t);const n=t.querySelector("input");n&&n.click(),e.consume()}}}async#L(e){e&&h.write("BreakpointsView focus on selected element",(()=>{const t=this.#w.querySelector('[tabindex="0"]');t?.setAttribute("tabindex","-1"),e.setAttribute("tabindex","0"),e.focus()}))}async#H(e,t){const n=await I(t,e,((e,t)=>t?h.write("BreakpointsView expand",(()=>{e.setAttribute("open","")})):h.write("BreakpointsView expand",(()=>{e.removeAttribute("open")}))));return await this.#L(n)}async#D(e){if("Home"===e){const e=this.#w.querySelector("[data-first-pause]");return await this.#L(e)}if("End"===e){const e=this.#B.length;if(0===e){const e=this.#w.querySelector("[data-last-pause]");return await this.#L(e)}const t=e-1;if(this.#B[t].expanded){const e=this.#w.querySelector("[data-last-group] > [data-last-breakpoint]");return await this.#L(e)}const n=this.#w.querySelector("[data-last-group] > summary");return await this.#L(n)}}#R(e){const t="LOGPOINT"===e.type?P(j.editLogpoint):P(j.editCondition);return O` <button data-edit-breakpoint @click=${t=>{this.#y.breakpointEdited(e,!0),t.consume()}} title=${t} jslog=${m.action("edit-breakpoint").track({click:!0})}> <devtools-icon name="edit"></devtools-icon> </button> `}#j(e,n,o){return O` <button data-remove-breakpoint @click=${n=>{t.userMetrics.actionTaken(o),this.#y.breakpointsRemoved(e),n.consume()}} title=${n} aria-label=${n} jslog=${m.action("remove-breakpoint").track({click:!0})}> <devtools-icon name="bin"></devtools-icon> </button> `}#N(e,n){const{breakpointItems:o}=n,i=new u.ContextMenu.ContextMenu(e);i.defaultSection().appendItem(P(j.removeAllBreakpointsInFile),(()=>{t.userMetrics.actionTaken(t.UserMetrics.Action.BreakpointsInFileRemovedFromContextMenu),this.#y.breakpointsRemoved(o)}),{jslogContext:"remove-file-breakpoints"});const a=this.#B.filter((e=>e!==n));i.defaultSection().appendItem(P(j.removeOtherBreakpoints),(()=>{const e=a.map((({breakpointItems:e})=>e)).flat();this.#y.breakpointsRemoved(e)}),{disabled:0===a.length,jslogContext:"remove-other-breakpoints"}),i.defaultSection().appendItem(P(j.removeAllBreakpoints),(()=>{const e=this.#B.map((({breakpointItems:e})=>e)).flat();this.#y.breakpointsRemoved(e)}),{jslogContext:"remove-all-breakpoints"});const r=o.filter((e=>"ENABLED"!==e.status));i.debugSection().appendItem(P(j.enableAllBreakpointsInFile),(()=>{t.userMetrics.actionTaken(t.UserMetrics.Action.BreakpointsInFileEnabledDisabledFromContextMenu);for(const e of r)this.#y.breakpointStateChanged(e,!0)}),{disabled:0===r.length,jslogContext:"enable-file-breakpoints"});const s=o.filter((e=>"DISABLED"!==e.status));i.debugSection().appendItem(P(j.disableAllBreakpointsInFile),(()=>{t.userMetrics.actionTaken(t.UserMetrics.Action.BreakpointsInFileEnabledDisabledFromContextMenu);for(const e of s)this.#y.breakpointStateChanged(e,!1)}),{disabled:0===s.length,jslogContext:"disable-file-breakpoints"}),i.show()}#M(e,n){return O` <details class=${H({active:this.#I})} ?data-first-group=${0===n} ?data-last-group=${n===this.#B.length-1} role=group aria-label='${e.name}' aria-description='${e.url}' ?open=${R(e.expanded)} @toggle=${t=>{const n=t.target;e.expanded=n.open,this.#y.expandedStateChanged(e.url,e.expanded)}}> <summary @contextmenu=${t=>{this.#N(t,e),t.consume()}} tabindex='-1' @keydown=${this.#$} @click=${async e=>{const n=e.currentTarget;await this.#L(n),t.userMetrics.actionTaken(t.UserMetrics.Action.BreakpointGroupExpandedStateChanged),e.consume()}}> <span class='group-header' aria-hidden=true><span class='group-icon-or-disable'>${this.#P()}${this.#F(e)}</span><span class='group-header-title' title='${e.url}'>${e.name}<span class='group-header-differentiator'>${this.#T.get(e.url)}</span></span></span> <span class='group-hover-actions'> ${this.#j(e.breakpointItems,P(j.removeAllBreakpointsInFile),t.UserMetrics.Action.BreakpointsInFileRemovedFromRemoveButton)} </span> </summary> ${D(e.breakpointItems,(e=>e.id),((t,o)=>this.#U(t,e.editable,n,o)))} </details> `}#F(e){const n=e.breakpointItems.some((e=>"ENABLED"===e.status));return O` <input class='group-checkbox small' type='checkbox' aria-label='' .checked=${n} @change=${n=>{t.userMetrics.actionTaken(t.UserMetrics.Action.BreakpointsInFileCheckboxToggled);const o=n.target,i=o.checked?"ENABLED":"DISABLED";e.breakpointItems.filter((e=>e.status!==i)).forEach((e=>{this.#y.breakpointStateChanged(e,o.checked)})),n.consume()}} tabindex=-1 jslog=${m.toggle("breakpoint-group").track({change:!0})}> `}#P(){return O`<devtools-icon name="file-script"></devtools-icon>`}#W(e,n,o){const i=this.#B.map((({breakpointItems:e})=>e)).flat(),a=i.filter((e=>e!==n)),r=new u.ContextMenu.ContextMenu(e),s="LOGPOINT"===n.type?P(j.editLogpoint):P(j.editCondition);r.revealSection().appendItem(P(j.revealLocation),(()=>{this.#y.jumpToSource(n)}),{jslogContext:"jump-to-breakpoint"}),r.editSection().appendItem(s,(()=>{this.#y.breakpointEdited(n,!1)}),{disabled:!o,jslogContext:"edit-breakpoint"}),r.defaultSection().appendItem(P(j.enableAllBreakpoints),i.forEach.bind(i,(e=>this.#y.breakpointStateChanged(e,!0))),{disabled:i.every((e=>"ENABLED"===e.status)),jslogContext:"enable-all-breakpoints"}),r.defaultSection().appendItem(P(j.disableAllBreakpoints),i.forEach.bind(i,(e=>this.#y.breakpointStateChanged(e,!1))),{disabled:i.every((e=>"DISABLED"===e.status)),jslogContext:"disable-all-breakpoints"}),r.footerSection().appendItem(P(j.removeBreakpoint),(()=>{t.userMetrics.actionTaken(t.UserMetrics.Action.BreakpointRemovedFromContextMenu),this.#y.breakpointsRemoved([n])}),{jslogContext:"remove-breakpoint"}),r.footerSection().appendItem(P(j.removeOtherBreakpoints),(()=>{this.#y.breakpointsRemoved(a)}),{disabled:0===a.length,jslogContext:"remove-other-breakpoints"}),r.footerSection().appendItem(P(j.removeAllBreakpoints),(()=>{const e=this.#B.map((({breakpointItems:e})=>e)).flat();this.#y.breakpointsRemoved(e)}),{jslogContext:"remove-all-breakpoints"}),r.show()}#U(e,n,i,a){const r=this.#_(e),s=o.StringUtilities.trimEndWithMaxLength(e.codeSnippet,200),l=this.#G(e.type,e.hoverText),c=this.#B[i].breakpointItems;return O` <div class=${H({"breakpoint-item":!0,hit:e.isHit,"conditional-breakpoint":"CONDITIONAL_BREAKPOINT"===e.type,logpoint:"LOGPOINT"===e.type})} ?data-first-breakpoint=${0===a} ?data-last-breakpoint=${a===c.length-1} aria-label=${r} role=treeitem tabindex='-1' @contextmenu=${t=>{this.#W(t,e,n),t.consume()}} @click=${async e=>{const t=e.currentTarget;await this.#L(t),e.consume()}} @keydown=${this.#$}> <label class='checkbox-label'> <span class='type-indicator'></span> <input type='checkbox' aria-label=${e.location} class='small' ?indeterminate=${"INDETERMINATE"===e.status} .checked=${"ENABLED"===e.status} @change=${t=>this.#V(t,e)} tabindex=-1 jslog=${m.toggle("breakpoint").track({change:!0})}> </label> <span class='code-snippet' @click=${t=>{this.#y.jumpToSource(e),t.consume()}} title=${M(l)} jslog=${m.action("sources.jump-to-breakpoint").track({click:!0})}>${s}</span> <span class='breakpoint-item-location-or-actions'> ${n?this.#R(e):g.nothing} ${this.#j([e],P(j.removeBreakpoint),t.UserMetrics.Action.BreakpointRemovedFromRemoveButton)} <span class='location'>${e.location}</span> </span> </div> `}#G(e,t){switch(e){case"REGULAR_BREAKPOINT":return;case"CONDITIONAL_BREAKPOINT":return i(t),P(j.conditionCode,{PH1:t});case"LOGPOINT":return i(t),P(j.logpointCode,{PH1:t})}}#_(e){let t;switch(e.status){case"ENABLED":t=P(j.checked);break;case"DISABLED":t=P(j.unchecked);break;case"INDETERMINATE":t=P(j.indeterminate)}return e.isHit?P(j.breakpointHit,{PH1:t}):t}#V(e,t){const n=e.target;this.#y.breakpointStateChanged(t,n.checked)}#O(e){const{checked:t}=e.target;this.#y.setPauseOnCaughtExceptions(t)}#A(e){const{checked:t}=e.target;if(!this.#E){const e=this.#w.querySelector("[data-pause-on-caught-checkbox]");i(e),!t&&e.checked&&e.click(),h.write("BreakpointsView update pause-on-uncaught-exception",(()=>{e.disabled=!t}))}this.#y.setPauseOnUncaughtExceptions(t)}}customElements.define("devtools-breakpoint-view",_);var G=Object.freeze({__proto__:null,BreakpointsSidebarController:W,BreakpointsView:_}),V={cssText:`:host{flex-grow:1;padding:6px}.row{display:flex;flex-direction:row;color:var(--sys-color-token-property-special);font-family:var(--monospace-font-family);font-size:var(--monospace-font-size);align-items:center;line-height:24px}.row devtools-button{line-height:1;margin-left:0.1em}.row devtools-button:nth-of-type(1){margin-left:0.8em}.padded{margin-left:2em}.separator{margin-right:0.5em;color:var(--sys-color-on-surface)}.editable{cursor:text;color:var(--sys-color-on-surface);overflow-wrap:break-word;min-height:18px;line-height:18px;min-width:0.5em;background:transparent;border:none;outline:none;display:inline-block}.editable.red{color:var(--sys-color-token-property-special)}.editable:hover,\n.editable:focus{border:1px solid var(--sys-color-neutral-outline);border-radius:2px}.row .inline-button{opacity:0%;visibility:hidden;transition:opacity 200ms}.row:focus-within .inline-button:not([hidden]),\n.row:hover .inline-button:not([hidden]){opacity:100%;visibility:visible}.center-wrapper{height:100%;display:flex;justify-content:center;align-items:center}.centered{margin:1em;max-width:300px;text-align:center}.error-header{font-weight:bold;margin-bottom:1em}.error-body{line-height:1.5em;color:var(--sys-color-token-subtle)}.add-block{margin-top:3px}.header-name,\n.header-value{min-width:min-content}.link{color:var(--sys-color-primary);text-decoration:underline;cursor:pointer;outline-offset:2px;padding:0}.learn-more-row{line-height:24px}\n/*# sourceURL=${import.meta.resolve("./HeadersView.css")} */\n`};const q=new CSSStyleSheet;q.replaceSync(V.cssText);const{html:z}=g,K={addHeader:"Add a header",removeHeader:"Remove this header",removeBlock:"Remove this '`ApplyTo`'-section",errorWhenParsing:"Error when parsing ''{PH1}''.",parsingErrorExplainer:"This is most likely due to a syntax error in ''{PH1}''. Try opening this file in an external editor to fix the error or delete the file and re-create the override.",addOverrideRule:"Add override rule",learnMore:"Learn more"},J=n.i18n.registerUIStrings("panels/sources/components/HeadersView.ts",K),Q=n.i18n.getLocalizedString.bind(void 0,J),X="header value",Y=e=>`header-name-${e}`;class Z extends u.View.SimpleView{#q=new ee;#z;constructor(e){super(n.i18n.lockedString("HeadersView")),this.element.setAttribute("jslog",`${m.pane("headers-view")}`),this.#z=e,this.#z.addEventListener(c.UISourceCode.Events.WorkingCopyChanged,this.#K,this),this.#z.addEventListener(c.UISourceCode.Events.WorkingCopyCommitted,this.#J,this),this.element.appendChild(this.#q),this.#Q()}async#Q(){const e=await this.#z.requestContent();this.#X(e.content||"")}#X(e){let t=!1,n=[];e=e||"[]";try{if(n=JSON.parse(e),!n.every(b.NetworkPersistenceManager.isHeaderOverride))throw new Error("Type mismatch after parsing")}catch{console.error("Failed to parse",this.#z.url(),"for locally overriding headers."),t=!0}this.#q.data={headerOverrides:n,uiSourceCode:this.#z,parsingError:t}}#K(){this.#X(this.#z.workingCopy())}#J(){this.#X(this.#z.workingCopy())}getComponent(){return this.#q}dispose(){this.#z.removeEventListener(c.UISourceCode.Events.WorkingCopyChanged,this.#K,this),this.#z.removeEventListener(c.UISourceCode.Events.WorkingCopyCommitted,this.#J,this)}}class ee extends HTMLElement{#w=this.attachShadow({mode:"open"});#Y=this.#Z.bind(this);#ee=[];#z=null;#te=!1;#ne=null;#oe="";constructor(){super(),this.#w.addEventListener("focusin",this.#ie.bind(this)),this.#w.addEventListener("focusout",this.#ae.bind(this)),this.#w.addEventListener("click",this.#re.bind(this)),this.#w.addEventListener("input",this.#se.bind(this)),this.#w.addEventListener("keydown",this.#le.bind(this)),this.#w.addEventListener("paste",this.#ce.bind(this)),this.addEventListener("contextmenu",this.#de.bind(this))}connectedCallback(){this.#w.adoptedStyleSheets=[q]}set data(e){this.#ee=e.headerOverrides,this.#z=e.uiSourceCode,this.#te=e.parsingError,k.ScheduledRender.scheduleRender(this,this.#Y)}#le(e){const t=e.target;if(!t.matches(".editable"))return;const n=e;!t.matches(".header-name")||""!==t.innerText||"Enter"!==n.key&&"Tab"!==n.key?"Enter"===n.key?(e.preventDefault(),t.blur(),this.#pe(t)):"Escape"===n.key&&(e.consume(),t.innerText=this.#oe,t.blur(),this.#he(t)):(e.preventDefault(),t.blur())}#pe(e){const t=Array.from(this.#w.querySelectorAll(".editable")),n=t.indexOf(e);-1!==n&&n+1<t.length&&t[n+1].focus()}#ue(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t?.removeAllRanges(),t?.addRange(n)}#ie(e){const t=e.target;t.matches(".editable")&&(this.#ue(t),this.#oe=t.innerText)}#ae(e){const t=e.target;if(""===t.innerText){const e=t.closest(".row"),n=Number(e.dataset.blockIndex),o=Number(e.dataset.headerIndex);t.matches(".apply-to")?(t.innerText="*",this.#ee[n].applyTo="*",this.#ge()):t.matches(".header-name")&&this.#me(n,o)}const n=window.getSelection();n?.removeAllRanges(),this.#z?.commitWorkingCopy()}#de(e){if(!this.#z)return;const t=new u.ContextMenu.ContextMenu(e);t.appendApplicableItems(this.#z),t.show()}#be(e){const t=new Set(e.map((e=>e.name)));let n=1;for(;t.has(Y(n));)n++;return Y(n)}#re(e){const t=e.target,n=t.closest(".row"),o=Number(n?.dataset.blockIndex||0),i=Number(n?.dataset.headerIndex||0);t.matches(".add-header")?(this.#ee[o].headers.splice(i+1,0,{name:this.#be(this.#ee[o].headers),value:X}),this.#ne={blockIndex:o,headerIndex:i+1},this.#ge()):t.matches(".remove-header")?this.#me(o,i):t.matches(".add-block")?(this.#ee.push({applyTo:"*",headers:[{name:Y(1),value:X}]}),this.#ne={blockIndex:this.#ee.length-1},this.#ge()):t.matches(".remove-block")&&(this.#ee.splice(o,1),this.#ge())}#ke(e,t){return!(0===t&&1===this.#ee[e].headers.length&&this.#ee[e].headers[t].name===Y(1)&&this.#ee[e].headers[t].value===X)}#me(e,t){this.#ee[e].headers.splice(t,1),0===this.#ee[e].headers.length&&this.#ee[e].headers.push({name:this.#be(this.#ee[e].headers),value:X}),this.#ge()}#se(e){this.#he(e.target)}#he(e){const t=e.closest(".row"),n=Number(t.dataset.blockIndex),o=Number(t.dataset.headerIndex);e.matches(".header-name")&&(this.#ee[n].headers[o].name=e.innerText,this.#ge()),e.matches(".header-value")&&(this.#ee[n].headers[o].value=e.innerText,this.#ge()),e.matches(".apply-to")&&(this.#ee[n].applyTo=e.innerText,this.#ge())}#ge(){this.#z?.setWorkingCopy(JSON.stringify(this.#ee,null,2)),t.userMetrics.actionTaken(t.UserMetrics.Action.HeaderOverrideHeadersFileEdited)}#ce(e){const t=e;if(e.preventDefault(),t.clipboardData){const n=t.clipboardData.getData("text/plain"),o=this.#w.getSelection()?.getRangeAt(0);if(!o)return;o.deleteContents();const i=document.createTextNode(n);o.insertNode(i),o.selectNodeContents(i),o.collapse(!1);const a=window.getSelection();a?.removeAllRanges(),a?.addRange(o),this.#he(e.target)}}#Z(){if(!k.ScheduledRender.isScheduledRender(this))throw new Error("HeadersView render was not scheduled");if(this.#te){const e=this.#z?.name()||".headers";g.render(z` <div class="center-wrapper"> <div class="centered"> <div class="error-header">${Q(K.errorWhenParsing,{PH1:e})}</div> <div class="error-body">${Q(K.parsingErrorExplainer,{PH1:e})}</div> </div> </div> `,this.#w,{host:this})}else if(g.render(z` ${this.#ee.map(((e,t)=>z` ${this.#ve(e.applyTo,t)} ${e.headers.map(((e,n)=>z` ${this.#fe(e,t,n)} `))} `))} <devtools-button .variant=${"outlined"} .jslogContext=${"headers-view.add-override-rule"} class="add-block"> ${Q(K.addOverrideRule)} </devtools-button> <div class="learn-more-row"> <x-link href="https://goo.gle/devtools-override" class="link" jslog=${m.link("learn-more").track({click:!0})}>${Q(K.learnMore)}</x-link> </div> `,this.#w,{host:this}),this.#ne){let e=null;e=this.#ne.headerIndex?this.#w.querySelector(`[data-block-index="${this.#ne.blockIndex}"][data-header-index="${this.#ne.headerIndex}"] .header-name`):this.#w.querySelector(`[data-block-index="${this.#ne.blockIndex}"] .apply-to`),e&&e.focus(),this.#ne=null}}#ve(e,t){return z` <div class="row" data-block-index=${t} jslog=${m.treeItem("*"===e?e:void 0)}> <div>${n.i18n.lockedString("Apply to")}</div> <div class="separator">:</div> ${this.#xe(e,"apply-to")} <devtools-button title=${Q(K.removeBlock)} .size=${"SMALL"} .iconName=${"bin"} .iconWidth=${"14px"} .iconHeight=${"14px"} .variant=${"icon"} .jslogContext=${"headers-view.remove-apply-to-section"} class="remove-block inline-button" ></devtools-button> </div> `}#fe(e,t,n){return z` <div class="row padded" data-block-index=${t} data-header-index=${n} jslog=${m.treeItem(e.name).parent("headers-editor-row-parent")}> ${this.#xe(e.name,"header-name red",!0)} <div class="separator">:</div> ${this.#xe(e.value,"header-value")} <devtools-button title=${Q(K.addHeader)} .size=${"SMALL"} .iconName=${"plus"} .variant=${"icon"} .jslogContext=${"headers-view.add-header"} class="add-header inline-button" ></devtools-button> <devtools-button title=${Q(K.removeHeader)} .size=${"SMALL"} .iconName=${"bin"} .variant=${"icon"} ?hidden=${!this.#ke(t,n)} .jslogContext=${"headers-view.remove-header"} class="remove-header inline-button" ></devtools-button> </div> `}#xe(e,t,n){const o=n?m.key():m.value();return z`<span jslog=${o.track({change:!0,keydown:"Enter|Escape|Tab",click:!0})} contenteditable="true" class="editable ${t}" tabindex="0" .innerText=${g.Directives.live(e)}></span>`}}m.registerParentProvider("headers-editor-row-parent",(e=>{for(;e.previousElementSibling?.classList?.contains("padded");)e=e.previousElementSibling;return e.previousElementSibling||void 0})),customElements.define("devtools-sources-headers-view",ee);var te=Object.freeze({__proto__:null,HeadersView:Z,HeadersViewComponent:ee});export{G as BreakpointsView,$ as BreakpointsViewUtils,te as HeadersView};