toolkit/components/url-classifier/content/moz/debug.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/.
     5 #ifdef DEBUG
     7 // Generic logging/debugging functionality that:
     8 //
     9 // (*) when disabled compiles to no-ops at worst (for calls to the service) 
    10 //     and to nothing at best (calls to G_Debug() and similar are compiled
    11 //     away when you use a jscompiler that strips dead code)
    12 //
    13 // (*) has dynamically configurable/creatable debugging "zones" enabling 
    14 //     selective logging
    15 // 
    16 // (*) hides its plumbing so that all calls in different zones are uniform,
    17 //     so you can drop files using this library into other apps that use it
    18 //     without any configuration 
    19 //
    20 // (*) can be controlled programmatically or via preferences.  The
    21 //     preferences that control the service and its zones are under
    22 //     the preference branch "safebrowsing-debug-service."
    23 //
    24 // (*) outputs function call traces when the "loggifier" zone is enabled
    25 // 
    26 // (*) can write output to logfiles so that you can get a call trace
    27 //     from someone who is having a problem
    28 //
    29 // Example:
    30 //
    31 // var G_GDEBUG = true                           // Enable this module
    32 // var G_debugService = new G_DebugService();    // in global context
    33 //
    34 // // You can use it with arbitrary primitive first arguement
    35 // G_Debug("myzone", "Yo yo yo");   // outputs: [myzone] Yo yo yo\n
    36 //
    37 // // But it's nice to use it with an object; it will probe for the zone name
    38 // function Obj() {
    39 //   this.debugZone = "someobj";
    40 // }
    41 // Obj.prototype.foo = function() {
    42 //   G_Debug(this, "foo called");
    43 // }
    44 // (new Obj).foo();                        // outputs: [someobj] foo called\n
    45 //
    46 // G_debugService.loggifier.loggify(Obj.prototype);  // enable call tracing
    47 //
    48 // // En/disable specific zones programmatically (you can also use preferences)
    49 // G_debugService.enableZone("somezone");
    50 // G_debugService.disableZone("someotherzone");
    51 // G_debugService.enableAllZones();
    52 //
    53 // // We also have asserts and errors:
    54 // G_Error(this, "Some error occurred");                    // will throw
    55 // G_Assert(this, (x > 3), "x not greater than three!");    // will throw
    56 //
    57 // See classes below for more methods. 
    58 //
    59 // TODO add code to set prefs when not found to the default value of a tristate
    60 // TODO add error level support
    61 // TODO add ability to turn off console output
    62 //
    63 // -------> TO START DEBUGGING: set G_GDEBUG to true
    65 // These are the functions code will typically call. Everything is
    66 // wrapped in if's so we can compile it away when G_GDEBUG is false.
    69 if (typeof G_GDEBUG == "undefined") {
    70   throw new Error("G_GDEBUG constant must be set before loading debug.js");
    71 }
    74 /**
    75  * Write out a debugging message.
    76  *
    77  * @param who The thingy to convert into a zone name corresponding to the 
    78  *            zone to which this message belongs
    79  * @param msg Message to output
    80  */
    81 function G_Debug(who, msg) {
    82   if (G_GDEBUG) {
    83     G_GetDebugZone(who).debug(msg);
    84   }
    85 }
    87 /**
    88  * Debugs loudly
    89  */
    90 function G_DebugL(who, msg) {
    91   if (G_GDEBUG) {
    92     var zone = G_GetDebugZone(who);
    94     if (zone.zoneIsEnabled()) {
    95       G_debugService.dump(
    96         "\n************************************************************\n");
    98       G_Debug(who, msg);
   100       G_debugService.dump(
   101         "************************************************************\n\n");
   102     }
   103   }
   104 }
   106 /**
   107  * Write out a call tracing message
   108  *
   109  * @param who The thingy to convert into a zone name corresponding to the 
   110  *            zone to which this message belongs
   111  * @param msg Message to output
   112  */
   113 function G_TraceCall(who, msg) {
   114   if (G_GDEBUG) {
   115     if (G_debugService.callTracingEnabled()) {
   116       G_debugService.dump(msg + "\n");
   117     }
   118   }
   119 }
   121 /**
   122  * Write out an error (and throw)
   123  *
   124  * @param who The thingy to convert into a zone name corresponding to the 
   125  *            zone to which this message belongs
   126  * @param msg Message to output
   127  */
   128 function G_Error(who, msg) {
   129   if (G_GDEBUG) {
   130     G_GetDebugZone(who).error(msg);
   131   }
   132 }
   134 /**
   135  * Assert something as true and signal an error if it's not
   136  *
   137  * @param who The thingy to convert into a zone name corresponding to the 
   138  *            zone to which this message belongs
   139  * @param condition Boolean condition to test
   140  * @param msg Message to output
   141  */
   142 function G_Assert(who, condition, msg) {
   143   if (G_GDEBUG) {
   144     G_GetDebugZone(who).assert(condition, msg);
   145   }
   146 }
   148 /**
   149  * Helper function that takes input and returns the DebugZone
   150  * corresponding to it.
   151  *
   152  * @param who Arbitrary input that will be converted into a zone name. Most
   153  *            likely an object that has .debugZone property, or a string.
   154  * @returns The DebugZone object corresponding to the input
   155  */
   156 function G_GetDebugZone(who) {
   157   if (G_GDEBUG) {
   158     var zone = "?";
   160     if (who && who.debugZone) {
   161       zone = who.debugZone;
   162     } else if (typeof who == "string") {
   163       zone = who;
   164     }
   166     return G_debugService.getZone(zone);
   167   }
   168 }
   170 // Classes that implement the functionality.
   172 /**
   173  * A debug "zone" is a string derived from arbitrary types (but
   174  * typically derived from another string or an object). All debugging
   175  * messages using a particular zone can be enabled or disabled
   176  * independent of other zones. This enables you to turn on/off logging
   177  * of particular objects or modules. This object implements a single
   178  * zone and the methods required to use it.
   179  *
   180  * @constructor
   181  * @param service Reference to the DebugService object we use for 
   182  *                registration
   183  * @param prefix String indicating the unique prefix we should use
   184  *               when creating preferences to control this zone
   185  * @param zone String indicating the name of the zone
   186  */
   187 function G_DebugZone(service, prefix, zone) {
   188   if (G_GDEBUG) {
   189     this.debugService_ = service;
   190     this.prefix_ = prefix;
   191     this.zone_ = zone;
   192     this.zoneEnabledPrefName_ = prefix + ".zone." + this.zone_;
   193     this.settings_ = new G_DebugSettings();
   194   }
   195 }
   197 /**
   198  * @returns Boolean indicating if this zone is enabled
   199  */
   200 G_DebugZone.prototype.zoneIsEnabled = function() {
   201   if (G_GDEBUG) {
   202     var explicit = this.settings_.getSetting(this.zoneEnabledPrefName_, null);
   204     if (explicit !== null) {
   205       return explicit;
   206     } else {
   207       return this.debugService_.allZonesEnabled();
   208     }
   209   }
   210 }
   212 /**
   213  * Enable this logging zone
   214  */
   215 G_DebugZone.prototype.enableZone = function() {
   216   if (G_GDEBUG) {
   217     this.settings_.setDefault(this.zoneEnabledPrefName_, true);
   218   }
   219 }
   221 /**
   222  * Disable this logging zone
   223  */
   224 G_DebugZone.prototype.disableZone = function() {
   225   if (G_GDEBUG) {
   226     this.settings_.setDefault(this.zoneEnabledPrefName_, false);
   227   }
   228 }
   230 /**
   231  * Write a debugging message to this zone
   232  *
   233  * @param msg String of message to write
   234  */
   235 G_DebugZone.prototype.debug = function(msg) {
   236   if (G_GDEBUG) {
   237     if (this.zoneIsEnabled()) {
   238       this.debugService_.dump("[" + this.zone_ + "] " + msg + "\n");
   239     }
   240   }
   241 }
   243 /**
   244  * Write an error to this zone and throw
   245  *
   246  * @param msg String of error to write
   247  */
   248 G_DebugZone.prototype.error = function(msg) {
   249   if (G_GDEBUG) {
   250     this.debugService_.dump("[" + this.zone_ + "] " + msg + "\n");
   251     throw new Error(msg);
   252     debugger;
   253   }
   254 }
   256 /**
   257  * Assert something as true and error if it is not
   258  *
   259  * @param condition Boolean condition to test
   260  * @param msg String of message to write if is false
   261  */
   262 G_DebugZone.prototype.assert = function(condition, msg) {
   263   if (G_GDEBUG) {
   264     if (condition !== true) {
   265       G_Error(this.zone_, "ASSERT FAILED: " + msg);
   266     }
   267   }
   268 }
   271 /**
   272  * The debug service handles auto-registration of zones, namespacing
   273  * the zones preferences, and various global settings such as whether
   274  * all zones are enabled.
   275  *
   276  * @constructor
   277  * @param opt_prefix Optional string indicating the unique prefix we should 
   278  *                   use when creating preferences
   279  */
   280 function G_DebugService(opt_prefix) {
   281   if (G_GDEBUG) {
   282     this.prefix_ = opt_prefix ? opt_prefix : "safebrowsing-debug-service";
   283     this.consoleEnabledPrefName_ = this.prefix_ + ".alsologtoconsole";
   284     this.allZonesEnabledPrefName_ = this.prefix_ + ".enableallzones";
   285     this.callTracingEnabledPrefName_ = this.prefix_ + ".trace-function-calls";
   286     this.logFileEnabledPrefName_ = this.prefix_ + ".logfileenabled";
   287     this.logFileErrorLevelPrefName_ = this.prefix_ + ".logfile-errorlevel";
   288     this.zones_ = {};
   290     this.loggifier = new G_Loggifier();
   291     this.settings_ = new G_DebugSettings();
   292   }
   293 }
   295 // Error levels for reporting console messages to the log.
   296 G_DebugService.ERROR_LEVEL_INFO = "INFO";
   297 G_DebugService.ERROR_LEVEL_WARNING = "WARNING";
   298 G_DebugService.ERROR_LEVEL_EXCEPTION = "EXCEPTION";
   301 /**
   302  * @returns Boolean indicating if we should send messages to the jsconsole
   303  */
   304 G_DebugService.prototype.alsoDumpToConsole = function() {
   305   if (G_GDEBUG) {
   306     return this.settings_.getSetting(this.consoleEnabledPrefName_, false);
   307   }
   308 }
   310 /**
   311  * @returns whether to log output to a file as well as the console.
   312  */
   313 G_DebugService.prototype.logFileIsEnabled = function() {
   314   if (G_GDEBUG) {
   315     return this.settings_.getSetting(this.logFileEnabledPrefName_, false);
   316   }
   317 }
   319 /**
   320  * Turns on file logging. dump() output will also go to the file specified by
   321  * setLogFile()
   322  */
   323 G_DebugService.prototype.enableLogFile = function() {
   324   if (G_GDEBUG) {
   325     this.settings_.setDefault(this.logFileEnabledPrefName_, true);
   326   }
   327 }
   329 /**
   330  * Turns off file logging
   331  */
   332 G_DebugService.prototype.disableLogFile = function() {
   333   if (G_GDEBUG) {
   334     this.settings_.setDefault(this.logFileEnabledPrefName_, false);
   335   }
   336 }
   338 /**
   339  * @returns an nsIFile instance pointing to the current log file location
   340  */
   341 G_DebugService.prototype.getLogFile = function() {
   342   if (G_GDEBUG) {
   343     return this.logFile_;
   344   }
   345 }
   347 /**
   348  * Sets a new log file location
   349  */
   350 G_DebugService.prototype.setLogFile = function(file) {
   351   if (G_GDEBUG) {
   352     this.logFile_ = file;
   353   }
   354 }
   356 /**
   357  * Enables sending messages to the jsconsole
   358  */
   359 G_DebugService.prototype.enableDumpToConsole = function() {
   360   if (G_GDEBUG) {
   361     this.settings_.setDefault(this.consoleEnabledPrefName_, true);
   362   }
   363 }
   365 /**
   366  * Disables sending messages to the jsconsole
   367  */
   368 G_DebugService.prototype.disableDumpToConsole = function() {
   369   if (G_GDEBUG) {
   370     this.settings_.setDefault(this.consoleEnabledPrefName_, false);
   371   }
   372 }
   374 /**
   375  * @param zone Name of the zone to get
   376  * @returns The DebugZone object corresopnding to input. If not such
   377  *          zone exists, a new one is created and returned
   378  */
   379 G_DebugService.prototype.getZone = function(zone) {
   380   if (G_GDEBUG) {
   381     if (!this.zones_[zone]) 
   382       this.zones_[zone] = new G_DebugZone(this, this.prefix_, zone);
   384     return this.zones_[zone];
   385   }
   386 }
   388 /**
   389  * @param zone Zone to enable debugging for
   390  */
   391 G_DebugService.prototype.enableZone = function(zone) {
   392   if (G_GDEBUG) {
   393     var toEnable = this.getZone(zone);
   394     toEnable.enableZone();
   395   }
   396 }
   398 /**
   399  * @param zone Zone to disable debugging for
   400  */
   401 G_DebugService.prototype.disableZone = function(zone) {
   402   if (G_GDEBUG) {
   403     var toDisable = this.getZone(zone);
   404     toDisable.disableZone();
   405   }
   406 }
   408 /**
   409  * @returns Boolean indicating whether debugging is enabled for all zones
   410  */
   411 G_DebugService.prototype.allZonesEnabled = function() {
   412   if (G_GDEBUG) {
   413     return this.settings_.getSetting(this.allZonesEnabledPrefName_, false);
   414   }
   415 }
   417 /**
   418  * Enables all debugging zones
   419  */
   420 G_DebugService.prototype.enableAllZones = function() {
   421   if (G_GDEBUG) {
   422     this.settings_.setDefault(this.allZonesEnabledPrefName_, true);
   423   }
   424 }
   426 /**
   427  * Disables all debugging zones
   428  */
   429 G_DebugService.prototype.disableAllZones = function() {
   430   if (G_GDEBUG) {
   431     this.settings_.setDefault(this.allZonesEnabledPrefName_, false);
   432   }
   433 }
   435 /**
   436  * @returns Boolean indicating whether call tracing is enabled
   437  */
   438 G_DebugService.prototype.callTracingEnabled = function() {
   439   if (G_GDEBUG) {
   440     return this.settings_.getSetting(this.callTracingEnabledPrefName_, false);
   441   }
   442 }
   444 /**
   445  * Enables call tracing
   446  */
   447 G_DebugService.prototype.enableCallTracing = function() {
   448   if (G_GDEBUG) {
   449     this.settings_.setDefault(this.callTracingEnabledPrefName_, true);
   450   }
   451 }
   453 /**
   454  * Disables call tracing
   455  */
   456 G_DebugService.prototype.disableCallTracing = function() {
   457   if (G_GDEBUG) {
   458     this.settings_.setDefault(this.callTracingEnabledPrefName_, false);
   459   }
   460 }
   462 /**
   463  * Gets the minimum error that will be reported to the log.
   464  */
   465 G_DebugService.prototype.getLogFileErrorLevel = function() {
   466   if (G_GDEBUG) {
   467     var level = this.settings_.getSetting(this.logFileErrorLevelPrefName_, 
   468                                           G_DebugService.ERROR_LEVEL_EXCEPTION);
   470     return level.toUpperCase();
   471   }
   472 }
   474 /**
   475  * Sets the minimum error level that will be reported to the log.
   476  */
   477 G_DebugService.prototype.setLogFileErrorLevel = function(level) {
   478   if (G_GDEBUG) {
   479     // normalize case just to make it slightly easier to not screw up.
   480     level = level.toUpperCase();
   482     if (level != G_DebugService.ERROR_LEVEL_INFO &&
   483         level != G_DebugService.ERROR_LEVEL_WARNING &&
   484         level != G_DebugService.ERROR_LEVEL_EXCEPTION) {
   485       throw new Error("Invalid error level specified: {" + level + "}");
   486     }
   488     this.settings_.setDefault(this.logFileErrorLevelPrefName_, level);
   489   }
   490 }
   492 /**
   493  * Internal dump() method
   494  *
   495  * @param msg String of message to dump
   496  */
   497 G_DebugService.prototype.dump = function(msg) {
   498   if (G_GDEBUG) {
   499     dump(msg);
   501     if (this.alsoDumpToConsole()) {
   502       try {
   503         var console = Components.classes['@mozilla.org/consoleservice;1']
   504                       .getService(Components.interfaces.nsIConsoleService);
   505         console.logStringMessage(msg);
   506       } catch(e) {
   507         dump("G_DebugZone ERROR: COULD NOT DUMP TO CONSOLE\n");
   508       }
   509     }
   511     this.maybeDumpToFile(msg);
   512   }
   513 }
   515 /**
   516  * Writes the specified message to the log file, if file logging is enabled.
   517  */
   518 G_DebugService.prototype.maybeDumpToFile = function(msg) {
   519   if (this.logFileIsEnabled() && this.logFile_) {
   521     /* try to get the correct line end character for this platform */
   522     if (!this._LINE_END_CHAR)
   523       this._LINE_END_CHAR =
   524         Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
   525                                          .OS == "WINNT" ? "\r\n" : "\n";
   526     if (this._LINE_END_CHAR != "\n")
   527       msg = msg.replace(/\n/g, this._LINE_END_CHAR);
   529     try {
   530       var stream = Cc["@mozilla.org/network/file-output-stream;1"]
   531                    .createInstance(Ci.nsIFileOutputStream);
   532       stream.init(this.logFile_,
   533                   0x02 | 0x08 | 0x10 /* PR_WRONLY | PR_CREATE_FILE | PR_APPEND */
   534                   -1 /* default perms */, 0 /* no special behavior */);
   535       stream.write(msg, msg.length);
   536     } finally {
   537       stream.close();
   538     }
   539   }
   540 }
   542 /**
   543  * Implements nsIConsoleListener.observe(). Gets called when an error message
   544  * gets reported to the console and sends it to the log file as well.
   545  */
   546 G_DebugService.prototype.observe = function(consoleMessage) {
   547   if (G_GDEBUG) {
   548     var errorLevel = this.getLogFileErrorLevel();
   550     // consoleMessage can be either nsIScriptError or nsIConsoleMessage. The
   551     // latter does not have things like line number, etc. So we special case 
   552     // it first. 
   553     if (!(consoleMessage instanceof Ci.nsIScriptError)) {
   554       // Only report these messages if the error level is INFO.
   555       if (errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
   556         this.maybeDumpToFile(G_DebugService.ERROR_LEVEL_INFO + ": " + 
   557                              consoleMessage.message + "\n");
   558       }
   560       return;
   561     }
   563     // We make a local copy of these fields because writing to it doesn't seem
   564     // to work.
   565     var flags = consoleMessage.flags;
   566     var sourceName = consoleMessage.sourceName;
   567     var lineNumber = consoleMessage.lineNumber;
   569     // Sometimes, a scripterror instance won't have any flags set. We 
   570     // default to exception.
   571     if (!flags) {
   572       flags = Ci.nsIScriptError.exceptionFlag;
   573     }
   575     // Default the filename and line number if they aren't set.
   576     if (!sourceName) {
   577       sourceName = "<unknown>";
   578     }
   580     if (!lineNumber) {
   581       lineNumber = "<unknown>";
   582     }
   584     // Report the error in the log file.
   585     if (flags & Ci.nsIScriptError.warningFlag) {
   586       // Only report warnings if the error level is warning or better. 
   587       if (errorLevel == G_DebugService.ERROR_LEVEL_WARNING ||
   588           errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
   589         this.reportScriptError_(consoleMessage.message,
   590                                 sourceName,
   591                                 lineNumber,
   592                                 G_DebugService.ERROR_LEVEL_WARNING);
   593       }
   594     } else if (flags & Ci.nsIScriptError.exceptionFlag) {
   595       // Always report exceptions.
   596       this.reportScriptError_(consoleMessage.message,
   597                               sourceName,
   598                               lineNumber,
   599                               G_DebugService.ERROR_LEVEL_EXCEPTION);
   600     }
   601   }
   602 }
   604 /**
   605  * Private helper to report an nsIScriptError instance to the log/console.
   606  */
   607 G_DebugService.prototype.reportScriptError_ = function(message, sourceName, 
   608                                                        lineNumber, label) {
   609   message = "\n------------------------------------------------------------\n" +
   610             label + ": " + message +
   611             "\nlocation: " + sourceName + ", " + "line: " + lineNumber +
   612             "\n------------------------------------------------------------\n\n";
   614   dump(message);
   615   this.maybeDumpToFile(message);
   616 }
   620 /**
   621  * A class that instruments methods so they output a call trace,
   622  * including the values of their actual parameters and return value.
   623  * This code is mostly stolen from Aaron Boodman's original
   624  * implementation in clobber utils.
   625  *
   626  * Note that this class uses the "loggifier" debug zone, so you'll see 
   627  * a complete call trace when that zone is enabled.
   628  *
   629  * @constructor
   630  */
   631 function G_Loggifier() {
   632   if (G_GDEBUG) {
   633     // Careful not to loggify ourselves!
   634     this.mark_(this);  
   635   }
   636 }
   638 /**
   639  * Marks an object as having been loggified. Loggification is not 
   640  * idempotent :)
   641  *
   642  * @param obj Object to be marked
   643  */
   644 G_Loggifier.prototype.mark_ = function(obj) {
   645   if (G_GDEBUG) {
   646     obj.__loggified_ = true;
   647   }
   648 }
   650 /**
   651  * @param obj Object to be examined
   652  * @returns Boolean indicating if the object has been loggified
   653  */
   654 G_Loggifier.prototype.isLoggified = function(obj) {
   655   if (G_GDEBUG) {
   656     return !!obj.__loggified_;
   657   }
   658 }
   660 /**
   661  * Attempt to extract the class name from the constructor definition.
   662  * Assumes the object was created using new.
   663  *
   664  * @param constructor String containing the definition of a constructor,
   665  *                    for example what you'd get by examining obj.constructor
   666  * @returns Name of the constructor/object if it could be found, else "???"
   667  */
   668 G_Loggifier.prototype.getFunctionName_ = function(constructor) {
   669   if (G_GDEBUG) {
   670     return constructor.name || "???";
   671   }
   672 }
   674 /**
   675  * Wraps all the methods in an object so that call traces are
   676  * automatically outputted.
   677  *
   678  * @param obj Object to loggify. SHOULD BE THE PROTOTYPE OF A USER-DEFINED
   679  *            object. You can get into trouble if you attempt to 
   680  *            loggify something that isn't, for example the Window.
   681  *
   682  * Any additional parameters are considered method names which should not be
   683  * loggified.
   684  *
   685  * Usage:
   686  * G_debugService.loggifier.loggify(MyClass.prototype,
   687  *                                  "firstMethodNotToLog",
   688  *                                  "secondMethodNotToLog",
   689  *                                  ... etc ...);
   690  */
   691 G_Loggifier.prototype.loggify = function(obj) {
   692   if (G_GDEBUG) {
   693     if (!G_debugService.callTracingEnabled()) {
   694       return;
   695     }
   697     if (typeof window != "undefined" && obj == window || 
   698         this.isLoggified(obj))   // Don't go berserk!
   699       return;
   701     var zone = G_GetDebugZone(obj);
   702     if (!zone || !zone.zoneIsEnabled()) {
   703       return;
   704     }
   706     this.mark_(obj);
   708     // Helper function returns an instrumented version of
   709     // objName.meth, with "this" bound properly. (BTW, because we're
   710     // in a conditional here, functions will only be defined as
   711     // they're encountered during execution, so declare this helper
   712     // before using it.)
   714     function wrap(meth, objName, methName) {
   715       return function() {
   717         // First output the call along with actual parameters
   718         var args = new Array(arguments.length);
   719         var argsString = "";
   720         for (var i = 0; i < args.length; i++) {
   721           args[i] = arguments[i];
   722           argsString += (i == 0 ? "" : ", ");
   724           if (typeof args[i] == "function") {
   725             argsString += "[function]";
   726           } else {
   727             argsString += args[i];
   728           }
   729         }
   731         G_TraceCall(this, "> " + objName + "." + methName + "(" + 
   732                     argsString + ")");
   734         // Then run the function, capturing the return value and throws
   735         try {
   736           var retVal = meth.apply(this, arguments);
   737           var reportedRetVal = retVal;
   739           if (typeof reportedRetVal == "undefined")
   740             reportedRetVal = "void";
   741           else if (reportedRetVal === "")
   742             reportedRetVal = "\"\" (empty string)";
   743         } catch (e) {
   744           if (e && !e.__logged) {
   745             G_TraceCall(this, "Error: " + e.message + ". " + 
   746                         e.fileName + ": " + e.lineNumber);
   747             try {
   748               e.__logged = true;
   749             } catch (e2) {
   750               // Sometimes we can't add the __logged flag because it's an
   751               // XPC wrapper
   752               throw e;
   753             }
   754           }
   756           throw e;      // Re-throw!
   757         }
   759         // And spit it out already
   760         G_TraceCall(
   761           this, 
   762           "< " + objName + "." + methName + ": " + reportedRetVal);
   764         return retVal;
   765       };
   766     };
   768     var ignoreLookup = {};
   770     if (arguments.length > 1) {
   771       for (var i = 1; i < arguments.length; i++) {
   772         ignoreLookup[arguments[i]] = true;
   773       }
   774     }
   776     // Wrap each method of obj
   777     for (var p in obj) {
   778       // Work around bug in Firefox. In ffox typeof RegExp is "function",
   779       // so make sure this really is a function. Bug as of FFox 1.5b2.
   780       if (typeof obj[p] == "function" && obj[p].call && !ignoreLookup[p]) {
   781         var objName = this.getFunctionName_(obj.constructor);
   782         obj[p] = wrap(obj[p], objName, p);
   783       }
   784     }
   785   }
   786 }
   789 /**
   790  * Simple abstraction around debug settings. The thing with debug settings is
   791  * that we want to be able to specify a default in the application's startup,
   792  * but have that default be overridable by the user via their prefs.
   793  *
   794  * To generalize this, we package up a dictionary of defaults with the 
   795  * preferences tree. If a setting isn't in the preferences tree, then we grab it
   796  * from the defaults.
   797  */
   798 function G_DebugSettings() {
   799   this.defaults_ = {};
   800   this.prefs_ = new G_Preferences();
   801 }
   803 /**
   804  * Returns the value of a settings, optionally defaulting to a given value if it
   805  * doesn't exist. If no default is specified, the default is |undefined|.
   806  */
   807 G_DebugSettings.prototype.getSetting = function(name, opt_default) {
   808   var override = this.prefs_.getPref(name, null);
   810   if (override !== null) {
   811     return override;
   812   } else if (typeof this.defaults_[name] != "undefined") {
   813     return this.defaults_[name];
   814   } else {
   815     return opt_default;
   816   }
   817 }
   819 /**
   820  * Sets the default value for a setting. If the user doesn't override it with a
   821  * preference, this is the value which will be returned by getSetting().
   822  */
   823 G_DebugSettings.prototype.setDefault = function(name, val) {
   824   this.defaults_[name] = val;
   825 }
   827 var G_debugService = new G_DebugService(); // Instantiate us!
   829 if (G_GDEBUG) {
   830   G_debugService.enableAllZones();
   831 }
   833 #else
   835 // Stubs for the debugging aids scattered through this component.
   836 // They will be expanded if you compile yourself a debug build.
   838 function G_Debug(who, msg) { }
   839 function G_Assert(who, condition, msg) { }
   840 function G_Error(who, msg) { }
   841 var G_debugService = { __noSuchMethod__: function() { } };
   843 #endif

mercurial