summaryrefslogtreecommitdiff
path: root/toolkit/components/contentprefs/ContentPrefServiceParent.jsm
blob: 32e31a7890582548d5f938055c3879199778109e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* vim: set ts=2 sw=2 sts=2 et tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

this.EXPORTED_SYMBOLS = [ "ContentPrefServiceParent" ];

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;

Cu.import("resource://gre/modules/ContentPrefUtils.jsm");

var ContentPrefServiceParent = {
  _cps2: null,

  init: function() {
    let globalMM = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                     .getService(Ci.nsIMessageListenerManager);

    this._cps2 = Cc["@mozilla.org/content-pref/service;1"]
                  .getService(Ci.nsIContentPrefService2);

    globalMM.addMessageListener("ContentPrefs:FunctionCall", this);

    let observerChangeHandler = this.handleObserverChange.bind(this);
    globalMM.addMessageListener("ContentPrefs:AddObserverForName", observerChangeHandler);
    globalMM.addMessageListener("ContentPrefs:RemoveObserverForName", observerChangeHandler);
    globalMM.addMessageListener("child-process-shutdown", observerChangeHandler);
  },

  // Map from message manager -> content pref observer.
  _observers: new Map(),

  handleObserverChange: function(msg) {
    let observer = this._observers.get(msg.target);
    if (msg.name === "child-process-shutdown") {
      // If we didn't have any observers for this child process, don't do
      // anything.
      if (!observer)
        return;

      for (let i of observer._names) {
        this._cps2.removeObserverForName(i, observer);
      }

      this._observers.delete(msg.target);
      return;
    }

    let prefName = msg.data.name;
    if (msg.name === "ContentPrefs:AddObserverForName") {
      // The child process is responsible for not adding multiple parent
      // observers for the same name.
      if (!observer) {
        observer = {
          onContentPrefSet: function(group, name, value, isPrivate) {
            msg.target.sendAsyncMessage("ContentPrefs:NotifyObservers",
                                        { name: name, callback: "onContentPrefSet",
                                          args: [ group, name, value, isPrivate ] });
          },

          onContentPrefRemoved: function(group, name, isPrivate) {
            msg.target.sendAsyncMessage("ContentPrefs:NotifyObservers",
                                        { name: name, callback: "onContentPrefRemoved",
                                          args: [ group, name, isPrivate ] });
          },

          // The names we're using this observer object for, used to keep track
          // of the number of names we care about as well as for removing this
          // observer if its associated process goes away.
          _names: new Set()
        };

        this._observers.set(msg.target, observer);
      }

      observer._names.add(prefName);

      this._cps2.addObserverForName(prefName, observer);
    } else {
      // RemoveObserverForName

      // We must have an observer.
      this._cps2.removeObserverForName(prefName, observer);

      observer._names.delete(prefName);
      if (observer._names.size === 0) {
        // This was the last use for this observer.
        this._observers.delete(msg.target);
      }
    }
  },

  receiveMessage: function(msg) {
    let data = msg.data;

    if (!_methodsCallableFromChild.some(([method, args]) => method == data.call)) {
      throw new Error(`Can't call ${data.call} from child!`);
    }

    let args = data.args;
    let requestId = data.requestId;

    let listener = {
      handleResult: function(pref) {
        msg.target.sendAsyncMessage("ContentPrefs:HandleResult",
                                    { requestId: requestId,
                                      contentPref: {
                                        domain: pref.domain,
                                        name: pref.name,
                                        value: pref.value
                                      }
                                    });
      },

      handleError: function(error) {
        msg.target.sendAsyncMessage("ContentPrefs:HandleError",
                                    { requestId: requestId,
                                      error: error });
      },
      handleCompletion: function(reason) {
        msg.target.sendAsyncMessage("ContentPrefs:HandleCompletion",
                                    { requestId: requestId,
                                      reason: reason });
      }
    };

    // Push our special listener.
    args.push(listener);

    // And call the function.
    this._cps2[data.call](...args);
  }
};