text: // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //      http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS-IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  /**
   * @fileoverview Error handling utilities.
   *
   */
  
  goog.provide('goog.debug.ErrorHandler');
  goog.provide('goog.debug.ErrorHandler.ProtectedFunctionError');
  
  goog.require('goog.asserts');
  goog.require('goog.debug');
  goog.require('goog.debug.EntryPointMonitor');
  goog.require('goog.debug.Trace');
  
  
  
  /**
   * The ErrorHandler can be used to to wrap functions with a try/catch
   * statement. If an exception is thrown, the given error handler function will
   * be called.
   *
   * When this object is disposed, it will stop handling exceptions and tracing.
   * It will also try to restore window.setTimeout and window.setInterval
   * if it wrapped them. Notice that in the general case, it is not technically
   * possible to remove the wrapper, because functions have no knowledge of
   * what they have been assigned to. So the app is responsible for other
   * forms of unwrapping.
   *
   * @param {Function} handler Handler for exceptions.
   * @constructor
   * @extends {goog.Disposable}
   * @implements {goog.debug.EntryPointMonitor}
   */
  goog.debug.ErrorHandler = function(handler) {
    goog.base(this);
  
    /**
     * Handler for exceptions, which can do logging, reporting, etc.
     * @type {Function}
     * @private
     */
    this.errorHandlerFn_ = handler;
  
    /**
     * Whether errors should be wrapped in
     * goog.debug.ErrorHandler.ProtectedFunctionError before rethrowing.
     * @type {boolean}
     * @private
     */
    this.wrapErrors_ = true;  // TODO(user) Change default.
  
    /**
     * Whether to add a prefix to all error messages. The prefix is
     * goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX. This option
     * only has an effect if this.wrapErrors_  is set to false.
     * @type {boolean}
     * @private
     */
    this.prefixErrorMessages_ = false;
  };
  goog.inherits(goog.debug.ErrorHandler, goog.Disposable);
  
  
  /**
   * Whether to add tracers when instrumenting entry points.
   * @type {boolean}
   * @private
   */
  goog.debug.ErrorHandler.prototype.addTracersToProtectedFunctions_ = false;
  
  
  /**
   * Enable tracers when instrumenting entry points.
   * @param {boolean} newVal See above.
   */
  goog.debug.ErrorHandler.prototype.setAddTracersToProtectedFunctions =
      function(newVal) {
    this.addTracersToProtectedFunctions_ = newVal;
  };
  
  
  /** @override */
  goog.debug.ErrorHandler.prototype.wrap = function(fn) {
    return this.protectEntryPoint(goog.asserts.assertFunction(fn));
  };
  
  
  /** @override */
  goog.debug.ErrorHandler.prototype.unwrap = function(fn) {
    goog.asserts.assertFunction(fn);
    return fn[this.getFunctionIndex_(false)] || fn;
  };
  
  
  /**
   * Private helper function to return a span that can be clicked on to display
   * an alert with the current stack trace. Newlines are replaced with a
   * placeholder so that they will not be html-escaped.
   * @param {string} stackTrace The stack trace to create a span for.
   * @return {string} A span which can be clicked on to show the stack trace.
   * @private
   */
  goog.debug.ErrorHandler.prototype.getStackTraceHolder_ = function(stackTrace) {
    var buffer = [];
    buffer.push('##PE_STACK_START##');
    buffer.push(stackTrace.replace(/(\r\n|\r|\n)/g, '##STACK_BR##'));
    buffer.push('##PE_STACK_END##');
    return buffer.join('');
  };
  
  
  /**
   * Get the index for a function. Used for internal indexing.
   * @param {boolean} wrapper True for the wrapper; false for the wrapped.
   * @return {string} The index where we should store the function in its
   *     wrapper/wrapped function.
   * @private
   */
  goog.debug.ErrorHandler.prototype.getFunctionIndex_ = function(wrapper) {
    return (wrapper ? '__wrapper_' : '__protected_') + goog.getUid(this) + '__';
  };
  
  
  /**
   * Installs exception protection for an entry point function. When an exception
   * is thrown from a protected function, a handler will be invoked to handle it.
   *
   * @param {Function} fn An entry point function to be protected.
   * @return {!Function} A protected wrapper function that calls the entry point
   *     function.
   */
  goog.debug.ErrorHandler.prototype.protectEntryPoint = function(fn) {
    var protectedFnName = this.getFunctionIndex_(true);
    if (!fn[protectedFnName]) {
      var wrapper = fn[protectedFnName] = this.getProtectedFunction(fn);
      wrapper[this.getFunctionIndex_(false)] = fn;
    }
    return fn[protectedFnName];
  };
  
  
  /**
   * Helps {@link #protectEntryPoint} by actually creating the protected
   * wrapper function, after {@link #protectEntryPoint} determines that one does
   * not already exist for the given function.  Can be overriden by subclasses
   * that may want to implement different error handling, or add additional
   * entry point hooks.
   * @param {!Function} fn An entry point function to be protected.
   * @return {!Function} protected wrapper function.
   * @protected
   */
  goog.debug.ErrorHandler.prototype.getProtectedFunction = function(fn) {
    var that = this;
    var tracers = this.addTracersToProtectedFunctions_;
    if (tracers) {
      var stackTrace = goog.debug.getStacktraceSimple(15);
    }
    var googDebugErrorHandlerProtectedFunction = function() {
      if (that.isDisposed()) {
        return fn.apply(this, arguments);
      }
  
      if (tracers) {
        var tracer = goog.debug.Trace.startTracer('protectedEntryPoint: ' +
            that.getStackTraceHolder_(stackTrace));
      }
      try {
        return fn.apply(this, arguments);
      } catch (e) {
        that.errorHandlerFn_(e);
        if (!that.wrapErrors_) {
          // Add the prefix to the existing message.
          if (that.prefixErrorMessages_) {
            if (typeof e === 'object') {
              e.message =
                  goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX +
                  e.message;
            } else {
              e = goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX +
                  e;
            }
          }
          if (goog.DEBUG) {
            // Work around for https://code.google.com/p/v8/issues/detail?id=2625
            // and https://code.google.com/p/chromium/issues/detail?id=237059
            // Custom errors and errors with custom stack traces show the wrong
            // stack trace
            // If it has a stack and Error.captureStackTrace is supported (only
            // supported in V8 as of May 2013) log the stack to the console.
            if (e && e.stack && Error.captureStackTrace &&
                goog.global['console']) {
              goog.global['console']['error'](e.message, e.stack);
            }
          }
          // Re-throw original error. This is great for debugging as it makes
          // browser JS dev consoles show the correct error and stack trace.
          throw e;
        }
        // Re-throw it since this may be expected by the caller.
        throw new goog.debug.ErrorHandler.ProtectedFunctionError(e);
      } finally {
        if (tracers) {
          goog.debug.Trace.stopTracer(tracer);
        }
      }
    };
    googDebugErrorHandlerProtectedFunction[this.getFunctionIndex_(false)] = fn;
    return googDebugErrorHandlerProtectedFunction;
  };
  
  
  // TODO(user): Allow these functions to take in the window to protect.
  /**
   * Installs exception protection for window.setTimeout to handle exceptions.
   */
  goog.debug.ErrorHandler.prototype.protectWindowSetTimeout =
      function() {
    this.protectWindowFunctionsHelper_('setTimeout');
  };
  
  
  /**
   * Install exception protection for window.setInterval to handle exceptions.
   */
  goog.debug.ErrorHandler.prototype.protectWindowSetInterval =
      function() {
    this.protectWindowFunctionsHelper_('setInterval');
  };
  
  
  /**
   * Install exception protection for window.requestAnimationFrame to handle
   * exceptions.
   */
  goog.debug.ErrorHandler.prototype.protectWindowRequestAnimationFrame =
      function() {
    var win = goog.getObjectByName('window');
    var fnNames = [
      'requestAnimationFrame',
      'mozRequestAnimationFrame',
      'webkitAnimationFrame',
      'msRequestAnimationFrame'
    ];
    for (var i = 0; i < fnNames.length; i++) {
      var fnName = fnNames[i];
      if (fnNames[i] in win) {
        win[fnName] = this.protectEntryPoint(win[fnName]);
      }
    }
  };
  
  
  /**
   * Helper function for protecting setTimeout/setInterval.
   * @param {string} fnName The name of the function we're protecting. Must
   *     be setTimeout or setInterval.
   * @private
   */
  goog.debug.ErrorHandler.prototype.protectWindowFunctionsHelper_ =
      function(fnName) {
    var win = goog.getObjectByName('window');
    var originalFn = win[fnName];
    var that = this;
    win[fnName] = function(fn, time) {
      // Don't try to protect strings. In theory, we could try to globalEval
      // the string, but this seems to lead to permission errors on IE6.
      if (goog.isString(fn)) {
        fn = goog.partial(goog.globalEval, fn);
      }
      fn = that.protectEntryPoint(fn);
  
      // IE doesn't support .call for setInterval/setTimeout, but it
      // also doesn't care what "this" is, so we can just call the
      // original function directly
      if (originalFn.call) {
        return originalFn.call(this, fn, time);
      } else {
        return originalFn(fn, time);
      }
    };
    win[fnName][this.getFunctionIndex_(false)] = originalFn;
  };
  
  
  /**
   * Set whether to wrap errors that occur in protected functions in a
   * goog.debug.ErrorHandler.ProtectedFunctionError.
   * @param {boolean} wrapErrors Whether to wrap errors.
   */
  goog.debug.ErrorHandler.prototype.setWrapErrors = function(wrapErrors) {
    this.wrapErrors_ = wrapErrors;
  };
  
  
  /**
   * Set whether to add a prefix to all error messages that occur in protected
   * functions.
   * @param {boolean} prefixErrorMessages Whether to add a prefix to error
   *     messages.
   */
  goog.debug.ErrorHandler.prototype.setPrefixErrorMessages =
      function(prefixErrorMessages) {
    this.prefixErrorMessages_ = prefixErrorMessages;
  };
  
  
  /** @override */
  goog.debug.ErrorHandler.prototype.disposeInternal = function() {
    // Try to unwrap window.setTimeout and window.setInterval.
    var win = goog.getObjectByName('window');
    win.setTimeout = this.unwrap(win.setTimeout);
    win.setInterval = this.unwrap(win.setInterval);
  
    goog.base(this, 'disposeInternal');
  };
  
  
  
  /**
   * Error thrown to the caller of a protected entry point if the entry point
   * throws an error.
   * @param {*} cause The error thrown by the entry point.
   * @constructor
   * @extends {goog.debug.Error}
   */
  goog.debug.ErrorHandler.ProtectedFunctionError = function(cause) {
    var message = goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX +
        (cause && cause.message ? String(cause.message) : String(cause));
    goog.base(this, message);
  
    /**
     * The error thrown by the entry point.
     * @type {*}
     */
    this.cause = cause;
  
    var stack = cause && cause.stack;
    if (stack && goog.isString(stack)) {
      this.stack = /** @type {string} */ (stack);
    }
  };
  goog.inherits(goog.debug.ErrorHandler.ProtectedFunctionError, goog.debug.Error);
  
  
  /**
   * Text to prefix the message with.
   * @type {string}
   */
  goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX =
      'Error in protected function: ';
  