1/* vim: set ts=2 sw=2 sts=2 et tw=80: */ 2/* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6"use strict"; 7 8this.EXPORTED_SYMBOLS = [ "ContentPrefServiceChild" ]; 9 10const Ci = Components.interfaces; 11const Cc = Components.classes; 12const Cu = Components.utils; 13 14Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 15Cu.import("resource://gre/modules/ContentPrefUtils.jsm"); 16Cu.import("resource://gre/modules/ContentPrefStore.jsm"); 17 18// We only need one bit of information out of the context. 19function contextArg(context) { 20 return (context && context.usePrivateBrowsing) ? 21 { usePrivateBrowsing: true } : 22 null; 23} 24 25function NYI() { 26 throw new Error("Do not add any new users of these functions"); 27} 28 29function CallbackCaller(callback) { 30 this._callback = callback; 31} 32 33CallbackCaller.prototype = { 34 handleResult: function(contentPref) { 35 cbHandleResult(this._callback, 36 new ContentPref(contentPref.domain, 37 contentPref.name, 38 contentPref.value)); 39 }, 40 41 handleError: function(result) { 42 cbHandleError(this._callback, result); 43 }, 44 45 handleCompletion: function(reason) { 46 cbHandleCompletion(this._callback, reason); 47 }, 48}; 49 50var ContentPrefServiceChild = { 51 QueryInterface: XPCOMUtils.generateQI([ Ci.nsIContentPrefService2 ]), 52 53 // Map from pref name -> set of observers 54 _observers: new Map(), 55 56 _mm: Cc["@mozilla.org/childprocessmessagemanager;1"] 57 .getService(Ci.nsIMessageSender), 58 59 _getRandomId: function() { 60 return Cc["@mozilla.org/uuid-generator;1"] 61 .getService(Ci.nsIUUIDGenerator).generateUUID().toString(); 62 }, 63 64 // Map from random ID string -> CallbackCaller, per request 65 _requests: new Map(), 66 67 init: function() { 68 this._mm.addMessageListener("ContentPrefs:HandleResult", this); 69 this._mm.addMessageListener("ContentPrefs:HandleError", this); 70 this._mm.addMessageListener("ContentPrefs:HandleCompletion", this); 71 }, 72 73 receiveMessage: function(msg) { 74 let data = msg.data; 75 let callback; 76 switch (msg.name) { 77 case "ContentPrefs:HandleResult": 78 callback = this._requests.get(data.requestId); 79 callback.handleResult(data.contentPref); 80 break; 81 82 case "ContentPrefs:HandleError": 83 callback = this._requests.get(data.requestId); 84 callback.handleError(data.error); 85 break; 86 87 case "ContentPrefs:HandleCompletion": 88 callback = this._requests.get(data.requestId); 89 this._requests.delete(data.requestId); 90 callback.handleCompletion(data.reason); 91 break; 92 93 case "ContentPrefs:NotifyObservers": { 94 let observerList = this._observers.get(data.name); 95 if (!observerList) 96 break; 97 98 for (let observer of observerList) { 99 safeCallback(observer, data.callback, data.args); 100 } 101 102 break; 103 } 104 } 105 }, 106 107 _callFunction: function(call, args, callback) { 108 let requestId = this._getRandomId(); 109 let data = { call: call, args: args, requestId: requestId }; 110 111 this._mm.sendAsyncMessage("ContentPrefs:FunctionCall", data); 112 113 this._requests.set(requestId, new CallbackCaller(callback)); 114 }, 115 116 getCachedByDomainAndName: NYI, 117 getCachedBySubdomainAndName: NYI, 118 getCachedGlobal: NYI, 119 120 addObserverForName: function(name, observer) { 121 let set = this._observers.get(name); 122 if (!set) { 123 set = new Set(); 124 if (this._observers.size === 0) { 125 // This is the first observer of any kind. Start listening for changes. 126 this._mm.addMessageListener("ContentPrefs:NotifyObservers", this); 127 } 128 129 // This is the first observer for this name. Start listening for changes 130 // to it. 131 this._mm.sendAsyncMessage("ContentPrefs:AddObserverForName", { name: name }); 132 this._observers.set(name, set); 133 } 134 135 set.add(observer); 136 }, 137 138 removeObserverForName: function(name, observer) { 139 let set = this._observers.get(name); 140 if (!set) 141 return; 142 143 set.delete(observer); 144 if (set.size === 0) { 145 // This was the last observer for this name. Stop listening for changes. 146 this._mm.sendAsyncMessage("ContentPrefs:RemoveObserverForName", { name: name }); 147 148 this._observers.delete(name); 149 if (this._observers.size === 0) { 150 // This was the last observer for this process. Stop listing for all 151 // changes. 152 this._mm.removeMessageListener("ContentPrefs:NotifyObservers", this); 153 } 154 } 155 }, 156 157 extractDomain: NYI 158}; 159 160function forwardMethodToParent(method, signature, ...args) { 161 // Ignore superfluous arguments 162 args = args.slice(0, signature.length); 163 164 // Process context argument for forwarding 165 let contextIndex = signature.indexOf("context"); 166 if (contextIndex > -1) { 167 args[contextIndex] = contextArg(args[contextIndex]); 168 } 169 // Take out the callback argument, if present. 170 let callbackIndex = signature.indexOf("callback"); 171 let callback = null; 172 if (callbackIndex > -1 && args.length > callbackIndex) { 173 callback = args.splice(callbackIndex, 1)[0]; 174 } 175 this._callFunction(method, args, callback); 176} 177 178for (let [method, signature] of _methodsCallableFromChild) { 179 ContentPrefServiceChild[method] = forwardMethodToParent.bind(ContentPrefServiceChild, method, signature); 180} 181 182ContentPrefServiceChild.init(); 183