<commit_before>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */

/* More information about these options at jshint.com/docs/options */

/* globals trace, requestTurnServers, sendUrlRequest, sendAsyncUrlRequest,
   requestUserMedia, SignalingChannel, PeerConnectionClient, setupLoopback,
   parseJSON, isChromeApp, WindowPort, Constants */

/* exported Call */

'use strict';

var Call = function(params) {
  this.params_ = params;
  this.roomServer_ = params.roomServer || '';

  this.channel_ = new SignalingChannel(params.wssUrl, params.wssPostUrl);
  this.channel_.onmessage = this.onRecvSignalingChannelMessage_.bind(this);

  this.pcClient_ = null;
  this.localStream_ = null;

  this.startTime = null;

  // Public callbacks. Keep it sorted.
  this.oncallerstarted = null;
  this.onerror = null;
  this.oniceconnectionstatechange = null;
  this.onlocalstreamadded = null;
  this.onnewicecandidate = null;
  this.onremotehangup = null;
  this.onremotesdpset = null;
  this.onremotestreamadded = null;
  this.onsignalingstatechange = null;
  this.onstatusmessage = null;

  this.getMediaPromise_ = null;
  this.getTurnServersPromise_ = null;
  this.requestMediaAndTurnServers_();
};

Call.prototype.requestMediaAndTurnServers_ = function() {
  this.getMediaPromise_ = this.maybeGetMedia_();
  this.getTurnServersPromise_ = this.maybeGetTurnServers_();
};

Call.prototype.isInitiator = function() {
  return this.params_.isInitiator;
};

Call.prototype.start = function(roomId) {
  this.connectToRoom_(roomId);
  if (this.params_.isLoopback) {
    setupLoopback(this.params_.wssUrl, roomId);
  }
};

Call.prototype.queueCleanupMessages_ = function() {
  // Set up the cleanup queue.
  // These steps mirror the cleanup done in hangup(), but will be
  // executed when the Chrome App is closed by background.js.
  WindowPort.sendMessage({
    action: Constants.QUEUEADD_ACTION,
    queueMessage: {
      action: Constants.XHR_ACTION,
      method: 'POST',
      url: this.getLeaveUrl_(),
      body: null
    }
  });

  WindowPort.sendMessage({
    action: Constants.QUEUEADD_ACTION,
    queueMessage: {
      action: Constants.WS_ACTION,
      wsAction: Constants.WS_SEND_ACTION,
      data: JSON.stringify({
        cmd: 'send',
        msg: JSON.stringify({type: 'bye'})
      })
    }
  });

  WindowPort.sendMessage({
    action: Constants.QUEUEADD_ACTION,
    queueMessage: {
      action: Constants.XHR_ACTION,
      method: 'DELETE',
      url: this.channel_.getWssPostUrl(),
      body: null
    }
  });
};

Call.prototype.clearCleanupQueue_ = function() {
  // Clear the cleanup queue.
  WindowPort.sendMessage({action: Constants.QUEUECLEAR_ACTION});
};

Call.prototype.restart = function() {
  // Reinitialize the promises so the media gets hooked up as a result
  // of calling maybeGetMedia_.
  this.requestMediaAndTurnServers_();
  this.start(this.params_.previousRoomId);
};

Call.prototype.hangup = function(async) {
  this.startTime = null;

  if (isChromeApp()) {
    this.clearCleanupQueue_();
  }

  if (this.localStream_) {
    this.localStream_.stop();
    this.localStream_ = null;
  }

  if (!this.params_.roomId) {
    return;
  }

  if (this.pcClient_) {
    this.pcClient_.close();
    this.pcClient_ = null;
  }

  // Send 'leave' to GAE. This must complete before saying BYE to other client.
  // When the other client sees BYE it attempts to post offer and candidates to
  // GAE. GAE needs to know that we're disconnected at that point otherwise
  // it will forward messages to this client instead of storing them.

  // This section of code is executed in both sync and async depending on
  // where it is called from. When the browser is closed, the requests must
  // be executed as sync to finish before the browser closes. When called
  // from pressing the hang up button, the requests are executed async.

  // If you modify the steps used to hang up a call, you must also modify
  // the clean up queue steps set up in queueCleanupMessages_.');

  var sendLeaveStep = function() {
    // Send POST request to /leave.
    var path = this.getLeaveUrl_();
    return sendUrlRequest('POST', path, async);
  }.bind(this);
  var sendLeaveErrorString = 'Error sending /leave:';

  var sendByeStep = function() {
    // Send bye to the other client.
    this.channel_.send(JSON.stringify({type: 'bye'}));
  }.bind(this);
  var sendByeErrorString = 'Error sending bye:';

  var closeChannelStep = function() {
    // Close signaling channel.
    return this.channel_.close(async);
  }.bind(this);
  var closeChannelErrorString = 'Error closing signaling channel:';

  var setParamsStep = function() {
    this.params_.previousRoomId = this.params_.roomId;
    this.params_.roomId = null;
    this.params_.clientId = null;
  }.bind(this);
  var setParamsErrorString = 'Error setting params:';

  if (async) {
    var handleError = function(errorString, error) {
      trace(errorString + ' ' + error.message);
    };
    var sendLeave = sendLeaveStep().catch(
        handleError.bind(this, sendLeaveErrorString));
    var sendBye = sendLeave.then(sendByeStep).catch(
        handleError.bind(this, sendByeErrorString));
    var closeChannel = sendBye.then(closeChannelStep).catch(
        handleError.bind(this, closeChannelErrorString));
    var setParams = closeChannel.then(setParamsStep).catch(
        handleError.bind(this, setParamsErrorString));
    return setParams;
  } else {
    // Execute the cleanup steps.
    var executeStep = function(executor, errorString) {
      try {
        executor();
      } catch (ex) {
        trace(errorString + ' ' + ex);
      }
    };

    executeStep(sendLeaveStep, sendLeaveErrorString);
    executeStep(sendByeStep, sendByeErrorString);
    executeStep(closeChannelStep, closeChannelErrorString);
    executeStep(setParamsStep, setParamsErrorString);

    if (this.params_.roomId !== null || this.params_.clientId !== null) {
      trace('ERROR: sync cleanup tasks did not complete successfully.');
    } else {
      trace('Cleanup completed.');
    }
    return Promise.resolve();
  }
};

Call.prototype.getLeaveUrl_ = function() {
  return this.roomServer_ + '/leave/' + this.params_.roomId +
      '/' + this.params_.clientId;
};

Call.prototype.onRemoteHangup = function() {
  this.startTime = null;

  // On remote hangup this client becomes the new initiator.
  this.params_.isInitiator = true;

  if (this.pcClient_) {
    this.pcClient_.close();
    this.pcClient_ = null;
  }

  this.startSignaling_();
};
<commit_msg>Move to array of steps.<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */

/* More information about these options at jshint.com/docs/options */

/* globals trace, requestTurnServers, sendUrlRequest, sendAsyncUrlRequest,
   requestUserMedia, SignalingChannel, PeerConnectionClient, setupLoopback,
   parseJSON, isChromeApp, WindowPort, Constants */

/* exported Call */

'use strict';

var Call = function(params) {
  this.params_ = params;
  this.roomServer_ = params.roomServer || '';

  this.channel_ = new SignalingChannel(params.wssUrl, params.wssPostUrl);
  this.channel_.onmessage = this.onRecvSignalingChannelMessage_.bind(this);

  this.pcClient_ = null;
  this.localStream_ = null;

  this.startTime = null;

  // Public callbacks. Keep it sorted.
  this.oncallerstarted = null;
  this.onerror = null;
  this.oniceconnectionstatechange = null;
  this.onlocalstreamadded = null;
  this.onnewicecandidate = null;
  this.onremotehangup = null;
  this.onremotesdpset = null;
  this.onremotestreamadded = null;
  this.onsignalingstatechange = null;
  this.onstatusmessage = null;

  this.getMediaPromise_ = null;
  this.getTurnServersPromise_ = null;
  this.requestMediaAndTurnServers_();
};

Call.prototype.requestMediaAndTurnServers_ = function() {
  this.getMediaPromise_ = this.maybeGetMedia_();
  this.getTurnServersPromise_ = this.maybeGetTurnServers_();
};

Call.prototype.isInitiator = function() {
  return this.params_.isInitiator;
};

Call.prototype.start = function(roomId) {
  this.connectToRoom_(roomId);
  if (this.params_.isLoopback) {
    setupLoopback(this.params_.wssUrl, roomId);
  }
};

Call.prototype.queueCleanupMessages_ = function() {
  // Set up the cleanup queue.
  // These steps mirror the cleanup done in hangup(), but will be
  // executed when the Chrome App is closed by background.js.
  WindowPort.sendMessage({
    action: Constants.QUEUEADD_ACTION,
    queueMessage: {
      action: Constants.XHR_ACTION,
      method: 'POST',
      url: this.getLeaveUrl_(),
      body: null
    }
  });

  WindowPort.sendMessage({
    action: Constants.QUEUEADD_ACTION,
    queueMessage: {
      action: Constants.WS_ACTION,
      wsAction: Constants.WS_SEND_ACTION,
      data: JSON.stringify({
        cmd: 'send',
        msg: JSON.stringify({type: 'bye'})
      })
    }
  });

  WindowPort.sendMessage({
    action: Constants.QUEUEADD_ACTION,
    queueMessage: {
      action: Constants.XHR_ACTION,
      method: 'DELETE',
      url: this.channel_.getWssPostUrl(),
      body: null
    }
  });
};

Call.prototype.clearCleanupQueue_ = function() {
  // Clear the cleanup queue.
  WindowPort.sendMessage({action: Constants.QUEUECLEAR_ACTION});
};

Call.prototype.restart = function() {
  // Reinitialize the promises so the media gets hooked up as a result
  // of calling maybeGetMedia_.
  this.requestMediaAndTurnServers_();
  this.start(this.params_.previousRoomId);
};

Call.prototype.hangup = function(async) {
  this.startTime = null;

  if (isChromeApp()) {
    this.clearCleanupQueue_();
  }

  if (this.localStream_) {
    this.localStream_.stop();
    this.localStream_ = null;
  }

  if (!this.params_.roomId) {
    return;
  }

  if (this.pcClient_) {
    this.pcClient_.close();
    this.pcClient_ = null;
  }

  // Send 'leave' to GAE. This must complete before saying BYE to other client.
  // When the other client sees BYE it attempts to post offer and candidates to
  // GAE. GAE needs to know that we're disconnected at that point otherwise
  // it will forward messages to this client instead of storing them.

  // This section of code is executed in both sync and async depending on
  // where it is called from. When the browser is closed, the requests must
  // be executed as sync to finish before the browser closes. When called
  // from pressing the hang up button, the requests are executed async.

  // If you modify the steps used to hang up a call, you must also modify
  // the clean up queue steps set up in queueCleanupMessages_.');

  var steps = [];
  steps.push({
    step: function() {
        // Send POST request to /leave.
        var path = this.getLeaveUrl_();
        return sendUrlRequest('POST', path, async);
      }.bind(this),
    errorString: 'Error sending /leave:'
  });
  steps.push({
    step: function() {
        // Send bye to the other client.
        this.channel_.send(JSON.stringify({type: 'bye'}));
      }.bind(this),
    errorString: 'Error sending bye:'
  });
  steps.push({
    step: function() {
        // Close signaling channel.
        return this.channel_.close(async);
      }.bind(this),
    errorString: 'Error closing signaling channel:'
  });
  steps.push({
    step: function() {
        this.params_.previousRoomId = this.params_.roomId;
        this.params_.roomId = null;
        this.params_.clientId = null;
      }.bind(this),
    errorString: 'Error setting params:'
  });

  if (async) {
    var errorHandler = function(errorString, error) {
      trace(errorString + ' ' + error.message);
    };
    var promise = Promise.resolve();
    for (var i = 0; i < steps.length; ++i) {
      promise = promise.then(steps[i].step).catch(
        errorHandler.bind(this, steps[i].errorString));
    }

    return promise;
  } else {
    // Execute the cleanup steps.
    var executeStep = function(executor, errorString) {
      try {
        executor();
      } catch (ex) {
        trace(errorString + ' ' + ex);
      }
    };

    for (var j = 0; j < steps.length; ++j) {
      executeStep(steps[j].step, steps[j].errorString);
    }

    if (this.params_.roomId !== null || this.params_.clientId !== null) {
      trace('ERROR: sync cleanup tasks did not complete successfully.');
    } else {
      trace('Cleanup completed.');
    }
    return Promise.resolve();
  }
};

Call.prototype.getLeaveUrl_ = function() {
  return this.roomServer_ + '/leave/' + this.params_.roomId +
      '/' + this.params_.clientId;
};

Call.prototype.onRemoteHangup = function() {
  this.startTime = null;

  // On remote hangup this client becomes the new initiator.
  this.params_.isInitiator = true;

  if (this.pcClient_) {
    this.pcClient_.close();
    this.pcClient_ = null;
  }

  this.startSignaling_();
};

{'fraction_non_alphanumeric': 0.11549338836658259, 'fraction_numerical': 0.0007643506840938623, 'mean_word_length': 3.2535760728218466, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6778101', 'n_tokens_mistral': 4068, 'n_tokens_neox': 3966, 'n_words': 1057}