text: /*
   * Copyright (C) 2008 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. ``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
   * 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.
   */
  
  /**
   * @implements {Common.ContentProvider}
   * @unrestricted
   */
  export default class Script {
    /**
     * @param {!SDK.DebuggerModel} debuggerModel
     * @param {string} scriptId
     * @param {string} sourceURL
     * @param {number} startLine
     * @param {number} startColumn
     * @param {number} endLine
     * @param {number} endColumn
     * @param {!Protocol.Runtime.ExecutionContextId} executionContextId
     * @param {string} hash
     * @param {boolean} isContentScript
     * @param {boolean} isLiveEdit
     * @param {string|undefined} sourceMapURL
     * @param {boolean} hasSourceURL
     * @param {number} length
     * @param {?Protocol.Runtime.StackTrace} originStackTrace
     */
    constructor(
        debuggerModel, scriptId, sourceURL, startLine, startColumn, endLine, endColumn, executionContextId, hash,
        isContentScript, isLiveEdit, sourceMapURL, hasSourceURL, length, originStackTrace) {
      this.debuggerModel = debuggerModel;
      this.scriptId = scriptId;
      this.sourceURL = sourceURL;
      this.lineOffset = startLine;
      this.columnOffset = startColumn;
      this.endLine = endLine;
      this.endColumn = endColumn;
  
      this.executionContextId = executionContextId;
      this.hash = hash;
      this._isContentScript = isContentScript;
      this._isLiveEdit = isLiveEdit;
      this.sourceMapURL = sourceMapURL;
      this.hasSourceURL = hasSourceURL;
      this.contentLength = length;
      this._originalContentProvider = null;
      this._originalSource = null;
      this.originStackTrace = originStackTrace;
    }
  
    /**
     * @param {string} source
     * @return {string}
     */
    static _trimSourceURLComment(source) {
      let sourceURLIndex = source.lastIndexOf('//# sourceURL=');
      if (sourceURLIndex === -1) {
        sourceURLIndex = source.lastIndexOf('//@ sourceURL=');
        if (sourceURLIndex === -1) {
          return source;
        }
      }
      const sourceURLLineIndex = source.lastIndexOf('\n', sourceURLIndex);
      if (sourceURLLineIndex === -1) {
        return source;
      }
      const sourceURLLine = source.substr(sourceURLLineIndex + 1);
      if (!sourceURLLine.match(sourceURLRegex)) {
        return source;
      }
      return source.substr(0, sourceURLLineIndex);
    }
  
    /**
     * @return {boolean}
     */
    isContentScript() {
      return this._isContentScript;
    }
  
    /**
     * @return {?SDK.ExecutionContext}
     */
    executionContext() {
      return this.debuggerModel.runtimeModel().executionContext(this.executionContextId);
    }
  
    /**
     * @return {boolean}
     */
    isLiveEdit() {
      return this._isLiveEdit;
    }
  
    /**
     * @override
     * @return {string}
     */
    contentURL() {
      return this.sourceURL;
    }
  
    /**
     * @override
     * @return {!Common.ResourceType}
     */
    contentType() {
      return Common.resourceTypes.Script;
    }
  
    /**
     * @override
     * @return {!Promise<boolean>}
     */
    contentEncoded() {
      return Promise.resolve(false);
    }
  
    /**
     * @override
     * @return {!Promise<!Common.DeferredContent>}
     */
    async requestContent() {
      if (this._source) {
        return {content: this._source, isEncoded: false};
      }
      if (!this.scriptId) {
        return {error: ls`Script removed or deleted.`, isEncoded: false};
      }
  
      try {
        const source = await this.debuggerModel.target().debuggerAgent().getScriptSource(this.scriptId);
        if (source && this.hasSourceURL) {
          this._source = SDK.Script._trimSourceURLComment(source);
        } else {
          this._source = source || '';
        }
  
        if (this._originalSource === null) {
          this._originalSource = this._source;
        }
        return {content: this._source, isEncoded: false};
      } catch (err) {
        return {error: ls`Unable to fetch script source.`, isEncoded: false};
      }
    }
  
    /**
     * @return {!Promise<!ArrayBuffer>}
     */
    async getWasmBytecode() {
      const base64 = await this.debuggerModel.target().debuggerAgent().getWasmBytecode(this.scriptId);
      const response = await fetch(`data:application/wasm;base64,${base64}`);
      return response.arrayBuffer();
    }
  
    /**
     * @return {!Common.ContentProvider}
     */
    originalContentProvider() {
      if (!this._originalContentProvider) {
        const lazyContent = () => this.requestContent().then(() => {
          return {
            content: this._originalSource,
            isEncoded: false,
          };
        });
        this._originalContentProvider =
            new Common.StaticContentProvider(this.contentURL(), this.contentType(), lazyContent);
      }
      return this._originalContentProvider;
    }
  
    /**
     * @override
     * @param {string} query
     * @param {boolean} caseSensitive
     * @param {boolean} isRegex
     * @return {!Promise<!Array<!Common.ContentProvider.SearchMatch>>}
     */
    async searchInContent(query, caseSensitive, isRegex) {
      if (!this.scriptId) {
        return [];
      }
  
      const matches =
          await this.debuggerModel.target().debuggerAgent().searchInContent(this.scriptId, query, caseSensitive, isRegex);
      return (matches || []).map(match => new Common.ContentProvider.SearchMatch(match.lineNumber, match.lineContent));
    }
  
    /**
     * @param {string} source
     * @return {string}
     */
    _appendSourceURLCommentIfNeeded(source) {
      if (!this.hasSourceURL) {
        return source;
      }
      return source + '\n //# sourceURL=' + this.sourceURL;
    }
  
    /**
     * @param {string} newSource
     * @param {function(?Protocol.Error, !Protocol.Runtime.ExceptionDetails=, !Array.<!Protocol.Debugger.CallFrame>=, !Protocol.Runtime.StackTrace=, !Protocol.Runtime.StackTraceId=, boolean=)} callback
     */
    async editSource(newSource, callback) {
      newSource = Script._trimSourceURLComment(newSource);
      // We append correct sourceURL to script for consistency only. It's not actually needed for things to work correctly.
      newSource = this._appendSourceURLCommentIfNeeded(newSource);
  
      if (!this.scriptId) {
        callback('Script failed to parse');
        return;
      }
  
      await this.requestContent();
      if (this._source === newSource) {
        callback(null);
        return;
      }
      const response = await this.debuggerModel.target().debuggerAgent().invoke_setScriptSource(
          {scriptId: this.scriptId, scriptSource: newSource});
  
      if (!response[Protocol.Error] && !response.exceptionDetails) {
        this._source = newSource;
      }
  
      const needsStepIn = !!response.stackChanged;
      callback(
          response[Protocol.Error], response.exceptionDetails, response.callFrames, response.asyncStackTrace,
          response.asyncStackTraceId, needsStepIn);
    }
  
    /**
     * @param {number} lineNumber
     * @param {number=} columnNumber
     * @return {?SDK.DebuggerModel.Location}
     */
    rawLocation(lineNumber, columnNumber) {
      if (this.containsLocation(lineNumber, columnNumber)) {
        return new SDK.DebuggerModel.Location(this.debuggerModel, this.scriptId, lineNumber, columnNumber);
      }
      return null;
    }
  
    /**
     *
     * @param {!SDK.DebuggerModel.Location} location
     * @return {!Array.<number>}
     */
    toRelativeLocation(location) {
      console.assert(
          location.scriptId === this.scriptId, '`toRelativeLocation` must be used with location of the same script');
      const relativeLineNumber = location.lineNumber - this.lineOffset;
      const relativeColumnNumber = (location.columnNumber || 0) - (relativeLineNumber === 0 ? this.columnOffset : 0);
      return [relativeLineNumber, relativeColumnNumber];
    }
  
    /**
     * @return {boolean}
     */
    isInlineScript() {
      const startsAtZero = !this.lineOffset && !this.columnOffset;
      return !!this.sourceURL && !startsAtZero;
    }
  
    /**
     * @return {boolean}
     */
    isAnonymousScript() {
      return !this.sourceURL;
    }
  
    /**
     * @return {boolean}
     */
    isInlineScriptWithSourceURL() {
      return !!this.hasSourceURL && this.isInlineScript();
    }
  
    /**
     * @param {!Array<!Protocol.Debugger.ScriptPosition>} positions
     * @return {!Promise<boolean>}
     */
    async setBlackboxedRanges(positions) {
      const response = await this.debuggerModel.target().debuggerAgent().invoke_setBlackboxedRanges(
          {scriptId: this.scriptId, positions});
      return !response[Protocol.Error];
    }
  
    containsLocation(lineNumber, columnNumber) {
      const afterStart =
          (lineNumber === this.lineOffset && columnNumber >= this.columnOffset) || lineNumber > this.lineOffset;
      const beforeEnd = lineNumber < this.endLine || (lineNumber === this.endLine && columnNumber <= this.endColumn);
      return afterStart && beforeEnd;
    }
  }
  
  export const sourceURLRegex = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/;
  
  /* Legacy exported object */
  self.SDK = self.SDK || {};
  
  /* Legacy exported object */
  SDK = SDK || {};
  
  /** @constructor */
  SDK.Script = Script;
  
  SDK.Script.sourceURLRegex = sourceURLRegex;
  