tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

error-messages.js (6538B)


      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 
      5 "use strict";
      6 
      7 const nsIConsoleListenerWatcher = require("resource://devtools/server/actors/resources/utils/nsi-console-listener-watcher.js");
      8 const {
      9  createStringGrip,
     10  makeDebuggeeValue,
     11  createValueGripForTarget,
     12 } = require("resource://devtools/server/actors/object/utils.js");
     13 const {
     14  getActorIdForInternalSourceId,
     15 } = require("resource://devtools/server/actors/utils/dbg-source.js");
     16 const {
     17  WebConsoleUtils,
     18 } = require("resource://devtools/server/actors/webconsole/utils.js");
     19 
     20 const Targets = require("resource://devtools/server/actors/targets/index.js");
     21 
     22 const { MESSAGE_CATEGORY } = require("resource://devtools/shared/constants.js");
     23 
     24 loader.lazyRequireGetter(
     25  this,
     26  "ErrorDocs",
     27  "resource://devtools/server/actors/errordocs.js"
     28 );
     29 
     30 const PLATFORM_SPECIFIC_CATEGORIES = [
     31  "XPConnect JavaScript",
     32  "component javascript",
     33  "chrome javascript",
     34  "chrome registration",
     35 ];
     36 
     37 class ErrorMessageWatcher extends nsIConsoleListenerWatcher {
     38  shouldHandleMessage(targetActor, message, isCachedMessage = false) {
     39    // The listener we use can be called either with a nsIConsoleMessage or a nsIScriptError.
     40    // In this file, we only want to handle nsIScriptError.
     41    if (
     42      // We only care about nsIScriptError
     43      !(message instanceof Ci.nsIScriptError) ||
     44      !this.isCategoryAllowed(targetActor, message.category) ||
     45      // Block any error that was triggered by eager evaluation
     46      message.sourceName === "debugger eager eval code"
     47    ) {
     48      return false;
     49    }
     50 
     51    // Filter specific to CONTENT PROCESS targets
     52    if (this.isProcessTarget(targetActor)) {
     53      // Don't want to display cached messages from private windows.
     54      const isCachedFromPrivateWindow =
     55        isCachedMessage && message.isFromPrivateWindow;
     56      if (isCachedFromPrivateWindow) {
     57        return false;
     58      }
     59 
     60      // `ContentChild` forwards all errors to the parent process (via IPC) all errors up
     61      // the parent process and sets a `isForwardedFromContentProcess` property on them.
     62      // Ignore these forwarded messages as the original ones will be logged either in a
     63      // content process target (if window-less message) or frame target (if related to a window)
     64      if (message.isForwardedFromContentProcess) {
     65        return false;
     66      }
     67 
     68      // Ignore all messages related to a given window for content process targets
     69      // These messages will be handled by Watchers instantiated for the related frame targets
     70      if (
     71        targetActor.targetType == Targets.TYPES.PROCESS &&
     72        message.innerWindowID
     73      ) {
     74        return false;
     75      }
     76 
     77      return true;
     78    }
     79 
     80    if (!message.innerWindowID) {
     81      return false;
     82    }
     83 
     84    const ids = targetActor.windows.map(window =>
     85      WebConsoleUtils.getInnerWindowId(window)
     86    );
     87    return ids.includes(message.innerWindowID);
     88  }
     89 
     90  /**
     91   * Check if the given message category is allowed to be tracked or not.
     92   * We ignore chrome-originating errors as we only care about content.
     93   *
     94   * @param string category
     95   *        The message category you want to check.
     96   * @return boolean
     97   *         True if the category is allowed to be logged, false otherwise.
     98   */
     99  isCategoryAllowed(targetActor, category) {
    100    // CSS Parser errors will be handled by the CSSMessageWatcher.
    101    if (!category || category === MESSAGE_CATEGORY.CSS_PARSER) {
    102      return false;
    103    }
    104 
    105    // We listen for everything on Process targets
    106    if (this.isProcessTarget(targetActor)) {
    107      return true;
    108    }
    109 
    110    // Don't restrict any categories in the Browser Toolbox/Browser Console
    111    if (targetActor.sessionContext.type == "all") {
    112      return true;
    113    }
    114 
    115    // For non-process targets in other toolboxes, we filter-out platform-specific errors.
    116    return !PLATFORM_SPECIFIC_CATEGORIES.includes(category);
    117  }
    118 
    119  /**
    120   * Prepare an nsIScriptError to be sent to the client.
    121   *
    122   * @param nsIScriptError error
    123   *        The page error we need to send to the client.
    124   * @return object
    125   *         The object you can send to the remote client.
    126   */
    127  buildResource(targetActor, error) {
    128    const stack = this.prepareStackForRemote(targetActor, error.stack);
    129 
    130    const notesArray = this.prepareNotesForRemote(targetActor, error.notes);
    131 
    132    // If there is no location information in the error but we have a stack,
    133    // fill in the location with the first frame on the stack.
    134    let { sourceName, sourceId, lineNumber, columnNumber } = error;
    135    if (!sourceName && !sourceId && !lineNumber && !columnNumber && stack) {
    136      sourceName = stack[0].filename;
    137      sourceId = stack[0].sourceId;
    138      // The line is 1-based.
    139      lineNumber = stack[0].lineNumber;
    140      // The column is also 1-based as it ultimately derivates from SavedFrame's 1-based column
    141      columnNumber = stack[0].columnNumber;
    142    }
    143 
    144    const pageError = {
    145      errorMessage: createStringGrip(targetActor, error.errorMessage),
    146      errorMessageName: error.errorMessageName,
    147      exceptionDocURL: ErrorDocs.GetURL(error),
    148      sourceName,
    149      sourceId: getActorIdForInternalSourceId(targetActor, sourceId),
    150      lineNumber,
    151      columnNumber,
    152      category: error.category,
    153      innerWindowID: error.innerWindowID,
    154      timeStamp: error.microSecondTimeStamp / 1000,
    155      warning: !!(error.flags & error.warningFlag),
    156      error: !(error.flags & (error.warningFlag | error.infoFlag)),
    157      info: !!(error.flags & error.infoFlag),
    158      private: error.isFromPrivateWindow,
    159      stacktrace: stack,
    160      notes: notesArray,
    161      chromeContext: error.isFromChromeContext,
    162      isPromiseRejection: error.isPromiseRejection,
    163      isForwardedFromContentProcess: error.isForwardedFromContentProcess,
    164    };
    165 
    166    // If the pageError does have an exception object, we want to return the grip for it,
    167    // but only if we do manage to get the grip, as we're checking the property on the
    168    // client to render things differently.
    169    if (error.hasException) {
    170      try {
    171        const obj = makeDebuggeeValue(targetActor, error.exception);
    172        if (obj?.class !== "DeadObject") {
    173          pageError.exception = createValueGripForTarget(targetActor, obj);
    174          pageError.hasException = true;
    175        }
    176      } catch (e) {}
    177    }
    178 
    179    return {
    180      pageError,
    181    };
    182  }
    183 }
    184 module.exports = ErrorMessageWatcher;