chromium/chrome/browser/resources/gaia_auth_host/channel.js

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

const INTERNAL_REQUEST_MESSAGE = 'internal-request-message';

const INTERNAL_REPLY_MESSAGE = 'internal-reply-message';

/**
 * Channel to the background script.
 */
export class Channel {
  constructor() {
    // Message port to use to communicate with background script.
    this.port_ = null;

    // Registered message callbacks.
    this.messageCallbacks_ = {};

    // Internal request id to track pending requests.
    this.nextInternalRequestId_ = 0;

    // Pending internal request callbacks.
    this.internalRequestCallbacks_ = {};
  }

  /**
   * Initialize the channel with given port for the background script.
   */
  init(port) {
    this.port_ = port;
    this.port_.onMessage.addListener(this.onMessage_.bind(this));
  }

  /**
   * Connects to the background script with the given name.
   */
  connect(name) {
    this.port_ = chrome.runtime.connect({name: name});
    this.port_.onMessage.addListener(this.onMessage_.bind(this));
  }

  /**
   * Associates a message name with a callback. When a message with the name
   * is received, the callback will be invoked with the message as its arg.
   * Note only the last registered callback will be invoked.
   */
  registerMessage(name, callback) {
    this.messageCallbacks_[name] = callback;
  }

  /**
   * Sends a message to the other side of the channel.
   */
  send(msg) {
    this.port_.postMessage(msg);
  }

  /**
   * Sends a message to the other side and invokes the callback with
   * the replied object. Useful for message that expects a returned result.
   */
  sendWithCallback(msg, callback) {
    const requestId = this.nextInternalRequestId_++;
    this.internalRequestCallbacks_[requestId] = callback;
    this.send({
      name: INTERNAL_REQUEST_MESSAGE,
      requestId: requestId,
      payload: msg,
    });
  }

  /**
   * Invokes message callback using given message.
   * @return {*} The return value of the message callback or null.
   */
  invokeMessageCallbacks_(msg) {
    const name = msg.name;
    if (this.messageCallbacks_[name]) {
      return this.messageCallbacks_[name](msg);
    }

    console.error('Error: Unexpected message, name=' + name);
    return null;
  }

  /**
   * Invoked when a message is received.
   */
  onMessage_(msg) {
    const name = msg.name;
    if (name === INTERNAL_REQUEST_MESSAGE) {
      const payload = msg.payload;
      const result = this.invokeMessageCallbacks_(payload);
      this.send({
        name: INTERNAL_REPLY_MESSAGE,
        requestId: msg.requestId,
        result: result,
      });
    } else if (name === INTERNAL_REPLY_MESSAGE) {
      const callback = this.internalRequestCallbacks_[msg.requestId];
      delete this.internalRequestCallbacks_[msg.requestId];
      if (callback) {
        callback(msg.result);
      }
    } else {
      this.invokeMessageCallbacks_(msg);
    }
  }
}