1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * Channel to the background script.
7 */
8function Channel() {
9}
10
11/** @const */
12Channel.INTERNAL_REQUEST_MESSAGE = 'internal-request-message';
13
14/** @const */
15Channel.INTERNAL_REPLY_MESSAGE = 'internal-reply-message';
16
17Channel.prototype = {
18  // Message port to use to communicate with background script.
19  port_: null,
20
21  // Registered message callbacks.
22  messageCallbacks_: {},
23
24  // Internal request id to track pending requests.
25  nextInternalRequestId_: 0,
26
27  // Pending internal request callbacks.
28  internalRequestCallbacks_: {},
29
30  /**
31   * Initialize the channel with given port for the background script.
32   */
33  init: function(port) {
34    this.port_ = port;
35    this.port_.onMessage.addListener(this.onMessage_.bind(this));
36  },
37
38  /**
39   * Connects to the background script with the given name.
40   */
41  connect: function(name) {
42    this.port_ = chrome.runtime.connect({name: name});
43    this.port_.onMessage.addListener(this.onMessage_.bind(this));
44  },
45
46  /**
47   * Associates a message name with a callback. When a message with the name
48   * is received, the callback will be invoked with the message as its arg.
49   * Note only the last registered callback will be invoked.
50   */
51  registerMessage: function(name, callback) {
52    this.messageCallbacks_[name] = callback;
53  },
54
55  /**
56   * Sends a message to the other side of the channel.
57   */
58  send: function(msg) {
59    this.port_.postMessage(msg);
60  },
61
62  /**
63   * Sends a message to the other side and invokes the callback with
64   * the replied object. Useful for message that expects a returned result.
65   */
66  sendWithCallback: function(msg, callback) {
67    var requestId = this.nextInternalRequestId_++;
68    this.internalRequestCallbacks_[requestId] = callback;
69    this.send({
70      name: Channel.INTERNAL_REQUEST_MESSAGE,
71      requestId: requestId,
72      payload: msg
73    });
74  },
75
76  /**
77   * Invokes message callback using given message.
78   * @return {*} The return value of the message callback or null.
79   */
80  invokeMessageCallbacks_: function(msg) {
81    var name = msg.name;
82    if (this.messageCallbacks_[name])
83      return this.messageCallbacks_[name](msg);
84
85    console.error('Error: Unexpected message, name=' + name);
86    return null;
87  },
88
89  /**
90   * Invoked when a message is received.
91   */
92  onMessage_: function(msg) {
93    var name = msg.name;
94    if (name == Channel.INTERNAL_REQUEST_MESSAGE) {
95      var payload = msg.payload;
96      var result = this.invokeMessageCallbacks_(payload);
97      this.send({
98        name: Channel.INTERNAL_REPLY_MESSAGE,
99        requestId: msg.requestId,
100        result: result
101      });
102    } else if (name == Channel.INTERNAL_REPLY_MESSAGE) {
103      var callback = this.internalRequestCallbacks_[msg.requestId];
104      delete this.internalRequestCallbacks_[msg.requestId];
105      if (callback)
106        callback(msg.result);
107    } else {
108      this.invokeMessageCallbacks_(msg);
109    }
110  }
111};
112