testing/specialpowers/content/SpecialPowersObserverAPI.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     4 "use strict";
     6 Components.utils.import("resource://gre/modules/Services.jsm");
     8 if (typeof(Ci) == 'undefined') {
     9   var Ci = Components.interfaces;
    10 }
    12 if (typeof(Cc) == 'undefined') {
    13   var Cc = Components.classes;
    14 }
    16 /**
    17  * Special Powers Exception - used to throw exceptions nicely
    18  **/
    19 function SpecialPowersException(aMsg) {
    20   this.message = aMsg;
    21   this.name = "SpecialPowersException";
    22 }
    24 SpecialPowersException.prototype.toString = function() {
    25   return this.name + ': "' + this.message + '"';
    26 };
    28 this.SpecialPowersObserverAPI = function SpecialPowersObserverAPI() {
    29   this._crashDumpDir = null;
    30   this._processCrashObserversRegistered = false;
    31   this._chromeScriptListeners = [];
    32 }
    34 function parseKeyValuePairs(text) {
    35   var lines = text.split('\n');
    36   var data = {};
    37   for (let i = 0; i < lines.length; i++) {
    38     if (lines[i] == '')
    39       continue;
    41     // can't just .split() because the value might contain = characters
    42     let eq = lines[i].indexOf('=');
    43     if (eq != -1) {
    44       let [key, value] = [lines[i].substring(0, eq),
    45                           lines[i].substring(eq + 1)];
    46       if (key && value)
    47         data[key] = value.replace(/\\n/g, "\n").replace(/\\\\/g, "\\");
    48     }
    49   }
    50   return data;
    51 }
    53 function parseKeyValuePairsFromFile(file) {
    54   var fstream = Cc["@mozilla.org/network/file-input-stream;1"].
    55                 createInstance(Ci.nsIFileInputStream);
    56   fstream.init(file, -1, 0, 0);
    57   var is = Cc["@mozilla.org/intl/converter-input-stream;1"].
    58            createInstance(Ci.nsIConverterInputStream);
    59   is.init(fstream, "UTF-8", 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
    60   var str = {};
    61   var contents = '';
    62   while (is.readString(4096, str) != 0) {
    63     contents += str.value;
    64   }
    65   is.close();
    66   fstream.close();
    67   return parseKeyValuePairs(contents);
    68 }
    70 SpecialPowersObserverAPI.prototype = {
    72   _observe: function(aSubject, aTopic, aData) {
    73     function addDumpIDToMessage(propertyName) {
    74       try {
    75         var id = aSubject.getPropertyAsAString(propertyName);
    76       } catch(ex) {
    77         var id = null;
    78       }
    79       if (id) {
    80         message.dumpIDs.push({id: id, extension: "dmp"});
    81         message.dumpIDs.push({id: id, extension: "extra"});
    82       }
    83     }
    85     switch(aTopic) {
    86       case "plugin-crashed":
    87       case "ipc:content-shutdown":
    88         var message = { type: "crash-observed", dumpIDs: [] };
    89         aSubject = aSubject.QueryInterface(Ci.nsIPropertyBag2);
    90         if (aTopic == "plugin-crashed") {
    91           addDumpIDToMessage("pluginDumpID");
    92           addDumpIDToMessage("browserDumpID");
    94           let pluginID = aSubject.getPropertyAsAString("pluginDumpID");
    95           let extra = this._getExtraData(pluginID);
    96           if (extra && ("additional_minidumps" in extra)) {
    97             let dumpNames = extra.additional_minidumps.split(',');
    98             for (let name of dumpNames) {
    99               message.dumpIDs.push({id: pluginID + "-" + name, extension: "dmp"});
   100             }
   101           }
   102         } else { // ipc:content-shutdown
   103           addDumpIDToMessage("dumpID");
   104         }
   105         this._sendAsyncMessage("SPProcessCrashService", message);
   106         break;
   107     }
   108   },
   110   _getCrashDumpDir: function() {
   111     if (!this._crashDumpDir) {
   112       this._crashDumpDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
   113       this._crashDumpDir.append("minidumps");
   114     }
   115     return this._crashDumpDir;
   116   },
   118   _getExtraData: function(dumpId) {
   119     let extraFile = this._getCrashDumpDir().clone();
   120     extraFile.append(dumpId + ".extra");
   121     if (!extraFile.exists()) {
   122       return null;
   123     }
   124     return parseKeyValuePairsFromFile(extraFile);
   125   },
   127   _deleteCrashDumpFiles: function(aFilenames) {
   128     var crashDumpDir = this._getCrashDumpDir();
   129     if (!crashDumpDir.exists()) {
   130       return false;
   131     }
   133     var success = aFilenames.length != 0;
   134     aFilenames.forEach(function(crashFilename) {
   135       var file = crashDumpDir.clone();
   136       file.append(crashFilename);
   137       if (file.exists()) {
   138         file.remove(false);
   139       } else {
   140         success = false;
   141       }
   142     });
   143     return success;
   144   },
   146   _findCrashDumpFiles: function(aToIgnore) {
   147     var crashDumpDir = this._getCrashDumpDir();
   148     var entries = crashDumpDir.exists() && crashDumpDir.directoryEntries;
   149     if (!entries) {
   150       return [];
   151     }
   153     var crashDumpFiles = [];
   154     while (entries.hasMoreElements()) {
   155       var file = entries.getNext().QueryInterface(Ci.nsIFile);
   156       var path = String(file.path);
   157       if (path.match(/\.(dmp|extra)$/) && !aToIgnore[path]) {
   158         crashDumpFiles.push(path);
   159       }
   160     }
   161     return crashDumpFiles.concat();
   162   },
   164   _getURI: function (url) {
   165     return Services.io.newURI(url, null, null);
   166   },
   168   _readUrlAsString: function(aUrl) {
   169     // Fetch script content as we can't use scriptloader's loadSubScript
   170     // to evaluate http:// urls...
   171     var scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
   172                              .getService(Ci.nsIScriptableInputStream);
   173     var channel = Services.io.newChannel(aUrl, null, null);
   174     var input = channel.open();
   175     scriptableStream.init(input);
   177     var str;
   178     var buffer = [];
   180     while ((str = scriptableStream.read(4096))) {
   181       buffer.push(str);
   182     }
   184     var output = buffer.join("");
   186     scriptableStream.close();
   187     input.close();
   189     var status;
   190     try {
   191       channel.QueryInterface(Ci.nsIHttpChannel);
   192       status = channel.responseStatus;
   193     } catch(e) {
   194       /* The channel is not a nsIHttpCHannel, but that's fine */
   195       dump("-*- _readUrlAsString: Got an error while fetching " +
   196            "chrome script '" + aUrl + "': (" + e.name + ") " + e.message + ". " +
   197            "Ignoring.\n");
   198     }
   200     if (status == 404) {
   201       throw new SpecialPowersException(
   202         "Error while executing chrome script '" + aUrl + "':\n" +
   203         "The script doesn't exists. Ensure you have registered it in " +
   204         "'support-files' in your mochitest.ini.");
   205     }
   207     return output;
   208   },
   210   /**
   211    * messageManager callback function
   212    * This will get requests from our API in the window and process them in chrome for it
   213    **/
   214   _receiveMessageAPI: function(aMessage) {
   215     // We explicitly return values in the below code so that this function
   216     // doesn't trigger a flurry of warnings about "does not always return
   217     // a value".
   218     switch(aMessage.name) {
   219       case "SPPrefService":
   220         var prefs = Services.prefs;
   221         var prefType = aMessage.json.prefType.toUpperCase();
   222         var prefName = aMessage.json.prefName;
   223         var prefValue = "prefValue" in aMessage.json ? aMessage.json.prefValue : null;
   225         if (aMessage.json.op == "get") {
   226           if (!prefName || !prefType)
   227             throw new SpecialPowersException("Invalid parameters for get in SPPrefService");
   229           // return null if the pref doesn't exist
   230           if (prefs.getPrefType(prefName) == prefs.PREF_INVALID)
   231             return null;
   232         } else if (aMessage.json.op == "set") {
   233           if (!prefName || !prefType  || prefValue === null)
   234             throw new SpecialPowersException("Invalid parameters for set in SPPrefService");
   235         } else if (aMessage.json.op == "clear") {
   236           if (!prefName)
   237             throw new SpecialPowersException("Invalid parameters for clear in SPPrefService");
   238         } else {
   239           throw new SpecialPowersException("Invalid operation for SPPrefService");
   240         }
   242         // Now we make the call
   243         switch(prefType) {
   244           case "BOOL":
   245             if (aMessage.json.op == "get")
   246               return(prefs.getBoolPref(prefName));
   247             else 
   248               return(prefs.setBoolPref(prefName, prefValue));
   249           case "INT":
   250             if (aMessage.json.op == "get") 
   251               return(prefs.getIntPref(prefName));
   252             else
   253               return(prefs.setIntPref(prefName, prefValue));
   254           case "CHAR":
   255             if (aMessage.json.op == "get")
   256               return(prefs.getCharPref(prefName));
   257             else
   258               return(prefs.setCharPref(prefName, prefValue));
   259           case "COMPLEX":
   260             if (aMessage.json.op == "get")
   261               return(prefs.getComplexValue(prefName, prefValue[0]));
   262             else
   263               return(prefs.setComplexValue(prefName, prefValue[0], prefValue[1]));
   264           case "":
   265             if (aMessage.json.op == "clear") {
   266               prefs.clearUserPref(prefName);
   267               return undefined;
   268             }
   269         }
   270         return undefined;	// See comment at the beginning of this function.
   272       case "SPProcessCrashService":
   273         switch (aMessage.json.op) {
   274           case "register-observer":
   275             this._addProcessCrashObservers();
   276             break;
   277           case "unregister-observer":
   278             this._removeProcessCrashObservers();
   279             break;
   280           case "delete-crash-dump-files":
   281             return this._deleteCrashDumpFiles(aMessage.json.filenames);
   282           case "find-crash-dump-files":
   283             return this._findCrashDumpFiles(aMessage.json.crashDumpFilesToIgnore);
   284           default:
   285             throw new SpecialPowersException("Invalid operation for SPProcessCrashService");
   286         }
   287         return undefined;	// See comment at the beginning of this function.
   289       case "SPPermissionManager":
   290         let msg = aMessage.json;
   292         let secMan = Services.scriptSecurityManager;
   293         let principal = secMan.getAppCodebasePrincipal(this._getURI(msg.url), msg.appId, msg.isInBrowserElement);
   295         switch (msg.op) {
   296           case "add":
   297             Services.perms.addFromPrincipal(principal, msg.type, msg.permission);
   298             break;
   299           case "remove":
   300             Services.perms.removeFromPrincipal(principal, msg.type);
   301             break;
   302           case "has":
   303             let hasPerm = Services.perms.testPermissionFromPrincipal(principal, msg.type);
   304             if (hasPerm == Ci.nsIPermissionManager.ALLOW_ACTION) 
   305               return true;
   306             return false;
   307             break;
   308           case "test":
   309             let testPerm = Services.perms.testPermissionFromPrincipal(principal, msg.type, msg.value);
   310             if (testPerm == msg.value)  {
   311               return true;
   312             }
   313             return false;
   314             break;
   315           default:
   316             throw new SpecialPowersException("Invalid operation for " +
   317                                              "SPPermissionManager");
   318         }
   319         return undefined;	// See comment at the beginning of this function.
   321       case "SPWebAppService":
   322         let Webapps = {};
   323         Components.utils.import("resource://gre/modules/Webapps.jsm", Webapps);
   324         switch (aMessage.json.op) {
   325           case "set-launchable":
   326             let val = Webapps.DOMApplicationRegistry.allAppsLaunchable;
   327             Webapps.DOMApplicationRegistry.allAppsLaunchable = aMessage.json.launchable;
   328             return val;
   329           default:
   330             throw new SpecialPowersException("Invalid operation for SPWebAppsService");
   331         }
   332         return undefined;	// See comment at the beginning of this function.
   334       case "SPObserverService":
   335         switch (aMessage.json.op) {
   336           case "notify":
   337             let topic = aMessage.json.observerTopic;
   338             let data = aMessage.json.observerData
   339             Services.obs.notifyObservers(null, topic, data);
   340             break;
   341           default:
   342             throw new SpecialPowersException("Invalid operation for SPObserverervice");
   343         }
   344         return undefined;	// See comment at the beginning of this function.
   346       case "SPLoadChromeScript":
   347         var url = aMessage.json.url;
   348         var id = aMessage.json.id;
   350         var jsScript = this._readUrlAsString(url);
   352         // Setup a chrome sandbox that has access to sendAsyncMessage
   353         // and addMessageListener in order to communicate with
   354         // the mochitest.
   355         var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
   356         var sb = Components.utils.Sandbox(systemPrincipal);
   357         var mm = aMessage.target
   358                          .QueryInterface(Ci.nsIFrameLoaderOwner)
   359                          .frameLoader
   360                          .messageManager;
   361         sb.sendAsyncMessage = (name, message) => {
   362           mm.sendAsyncMessage("SPChromeScriptMessage",
   363                               { id: id, name: name, message: message });
   364         };
   365         sb.addMessageListener = (name, listener) => {
   366           this._chromeScriptListeners.push({ id: id, name: name, listener: listener });
   367         };
   369         // Also expose assertion functions
   370         let reporter = function (err, message, stack) {
   371           // Pipe assertions back to parent process
   372           mm.sendAsyncMessage("SPChromeScriptAssert",
   373                               { id: id, url: url, err: err, message: message,
   374                                 stack: stack });
   375         };
   376         Object.defineProperty(sb, "assert", {
   377           get: function () {
   378             let scope = Components.utils.createObjectIn(sb);
   379             Services.scriptloader.loadSubScript("resource://specialpowers/Assert.jsm",
   380                                                 scope);
   382             let assert = new scope.Assert(reporter);
   383             delete sb.assert;
   384             return sb.assert = assert;
   385           },
   386           configurable: true
   387         });
   389         // Evaluate the chrome script
   390         try {
   391           Components.utils.evalInSandbox(jsScript, sb, "1.8", url, 1);
   392         } catch(e) {
   393           throw new SpecialPowersException("Error while executing chrome " +
   394                                            "script '" + url + "':\n" + e + "\n" +
   395                                            e.fileName + ":" + e.lineNumber);
   396         }
   397         return undefined;	// See comment at the beginning of this function.
   399       case "SPChromeScriptMessage":
   400         var id = aMessage.json.id;
   401         var name = aMessage.json.name;
   402         var message = aMessage.json.message;
   403         this._chromeScriptListeners
   404             .filter(o => (o.name == name && o.id == id))
   405             .forEach(o => o.listener(message));
   406         return undefined;	// See comment at the beginning of this function.
   408       default:
   409         throw new SpecialPowersException("Unrecognized Special Powers API");
   410     }
   412     // We throw an exception before reaching this explicit return because
   413     // we should never be arriving here anyway.
   414     throw new SpecialPowersException("Unreached code");
   415     return undefined;
   416   }
   417 };

mercurial