text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  const net = require("net");
  const selenium_webdriver_1 = require("selenium-webdriver");
  const util = require("util");
  const logger_1 = require("./logger");
  let breakpointHook = require('./breakpointhook.js');
  let logger = new logger_1.Logger('protractor');
  class DebugHelper {
      constructor(browserUnderDebug_) {
          this.browserUnderDebug_ = browserUnderDebug_;
      }
      initBlocking(debuggerClientPath, onStartFn, opt_debugPort) {
          this.init_(debuggerClientPath, true, onStartFn, opt_debugPort);
      }
      init(debuggerClientPath, onStartFn, opt_debugPort) {
          this.init_(debuggerClientPath, false, onStartFn, opt_debugPort);
      }
      /**
       *  1) Set up helper functions for debugger clients to call on (e.g.
       *     execute code, get autocompletion).
       *  2) Enter process into debugger mode. (i.e. process._debugProcess).
       *  3) Invoke the debugger client specified by debuggerClientPath.
       *
       * @param {string} debuggerClientPath Absolute path of debugger client to use.
       * @param {boolean} blockUntilExit Whether to block the flow until process exit or resume
       *     immediately.
       * @param {Function} onStartFn Function to call when the debugger starts. The
       *     function takes a single parameter, which represents whether this is the
       *     first time that the debugger is called.
       * @param {number=} opt_debugPort Optional port to use for the debugging
       *     process.
       *
       * @return {Promise} If blockUntilExit, a promise resolved when the debugger process
       *     exits. Otherwise, resolved when the debugger process is ready to begin.
       */
      init_(debuggerClientPath, blockUntilExit, onStartFn, opt_debugPort) {
          const vm_ = require('vm');
          let flow = selenium_webdriver_1.promise.controlFlow();
          let context = { require: require };
          global.list = (locator) => {
              return global.protractor.browser.findElements(locator).then((arr) => {
                  let found = [];
                  for (let i = 0; i < arr.length; ++i) {
                      arr[i].getText().then((text) => {
                          found.push(text);
                      });
                  }
                  return found;
              });
          };
          for (let key in global) {
              context[key] = global[key];
          }
          let sandbox = vm_.createContext(context);
          let debuggingDone = selenium_webdriver_1.promise.defer();
          // We run one flow.execute block for the debugging session. All
          // subcommands should be scheduled under this task.
          let executePromise = flow.execute(() => {
              process['debugPort'] = opt_debugPort || process['debugPort'];
              this.validatePortAvailability_(process['debugPort']).then((firstTime) => {
                  onStartFn(firstTime);
                  let args = [process.pid, process['debugPort']];
                  if (this.browserUnderDebug_.debuggerServerPort) {
                      args.push(this.browserUnderDebug_.debuggerServerPort);
                  }
                  let nodedebug = require('child_process').fork(debuggerClientPath, args);
                  process.on('exit', function () {
                      nodedebug.kill('SIGTERM');
                  });
                  nodedebug
                      .on('message', (m) => {
                      if (m === 'ready') {
                          breakpointHook();
                          if (!blockUntilExit) {
                              debuggingDone.fulfill();
                          }
                      }
                  })
                      .on('exit', () => {
                      // Clear this so that we know it's ok to attach a debugger
                      // again.
                      this.dbgCodeExecutor = null;
                      debuggingDone.fulfill();
                  });
              });
              return debuggingDone.promise;
          }, 'debugging tasks');
          // Helper used only by debuggers at './debugger/modes/*.js' to insert code
          // into the control flow, via debugger 'evaluate' protocol.
          // In order to achieve this, we maintain a task at the top of the control
          // flow, so that we can insert frames into it.
          // To be able to simulate callback/asynchronous code, we poll this object
          // whenever `breakpointHook` is called.
          this.dbgCodeExecutor = {
              execPromise_: undefined,
              execPromiseResult_: undefined,
              execPromiseError_: undefined,
              // A dummy repl server to make use of its completion function.
              replServer_: require('repl').start({
                  input: { on: function () { }, resume: function () { } },
                  // dummy readable stream
                  output: { write: function () { } },
                  useGlobal: true
              }),
              // Execute a function, which could yield a value or a promise,
              // and allow its result to be accessed synchronously
              execute_: function (execFn_) {
                  this.execPromiseResult_ = this.execPromiseError_ = undefined;
                  this.execPromise_ = execFn_();
                  // Note: This needs to be added after setting execPromise to execFn,
                  // or else we cause this.execPromise_ to get stuck in pending mode
                  // at our next breakpoint.
                  this.execPromise_.then((result) => {
                      this.execPromiseResult_ = result;
                      breakpointHook();
                  }, (err) => {
                      this.execPromiseError_ = err;
                      breakpointHook();
                  });
              },
              // Execute a piece of code.
              // Result is a string representation of the evaluation.
              execute: function (code) {
                  let execFn_ = () => {
                      // Run code through vm so that we can maintain a local scope which is
                      // isolated from the rest of the execution.
                      let res;
                      try {
                          res = vm_.runInContext(code, sandbox);
                      }
                      catch (e) {
                          res = selenium_webdriver_1.promise.when('Error while evaluating command: ' + e);
                      }
                      if (!selenium_webdriver_1.promise.isPromise(res)) {
                          res = selenium_webdriver_1.promise.when(res);
                      }
                      return res.then((res) => {
                          if (res === undefined) {
                              return undefined;
                          }
                          else {
                              // The '' forces res to be expanded into a string instead of just
                              // '[Object]'. Then we remove the extra space caused by the ''
                              // using substring.
                              return util.format.apply(this, ['', res]).substring(1);
                          }
                      });
                  };
                  this.execute_(execFn_);
              },
              // Autocomplete for a line.
              // Result is a JSON representation of the autocomplete response.
              complete: function (line) {
                  let execFn_ = () => {
                      let deferred = selenium_webdriver_1.promise.defer();
                      this.replServer_.complete(line, (err, res) => {
                          if (err) {
                              deferred.reject(err);
                          }
                          else {
                              deferred.fulfill(JSON.stringify(res));
                          }
                      });
                      return deferred.promise;
                  };
                  this.execute_(execFn_);
              },
              // Code finished executing.
              resultReady: function () {
                  return !(this.execPromise_.state_ === 'pending');
              },
              // Get asynchronous results synchronously.
              // This will throw if result is not ready.
              getResult: function () {
                  if (!this.resultReady()) {
                      throw new Error('Result not ready');
                  }
                  if (this.execPromiseError_) {
                      throw this.execPromiseError_;
                  }
                  return this.execPromiseResult_;
              }
          };
          return executePromise;
      }
      /**
       * Validates that the port is free to use. This will only validate the first
       * time it is called. The reason is that on subsequent calls, the port will
       * already be bound to the debugger, so it will not be available, but that is
       * okay.
       *
       * @returns {Promise<boolean>} A promise that becomes ready when the
       * validation
       *     is done. The promise will resolve to a boolean which represents whether
       *     this is the first time that the debugger is called.
       */
      validatePortAvailability_(port) {
          if (this.debuggerValidated_) {
              return selenium_webdriver_1.promise.when(false);
          }
          let doneDeferred = selenium_webdriver_1.promise.defer();
          // Resolve doneDeferred if port is available.
          let tester = net.connect({ port: port }, () => {
              doneDeferred.reject('Port ' + port + ' is already in use. Please specify ' +
                  'another port to debug.');
          });
          tester.once('error', (err) => {
              if (err.code === 'ECONNREFUSED') {
                  tester
                      .once('close', () => {
                      doneDeferred.fulfill(true);
                  })
                      .end();
              }
              else {
                  doneDeferred.reject('Unexpected failure testing for port ' + port + ': ' + JSON.stringify(err));
              }
          });
          return doneDeferred.promise.then((firstTime) => {
              this.debuggerValidated_ = true;
              return firstTime;
          }, (err) => {
              console.error(err);
              process.exit(1);
          });
      }
      isAttached() {
          return !!this.dbgCodeExecutor;
      }
  }
  exports.DebugHelper = DebugHelper;
  //# sourceMappingURL=debugger.js.map