tor-browser

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

test_chromeutils_callFunctionAndLogException.js (8726B)


      1 let lastMessage;
      2 const consoleListener = {
      3  observe(message) {
      4    dump(" >> new message: " + message.errorMessage + "\n");
      5    lastMessage = message;
      6  },
      7 };
      8 Services.console.registerListener(consoleListener);
      9 
     10 // The Console Service notifies its listener after one event loop cycle.
     11 // So wait for one tick after each action dispatching a message/error to the service.
     12 function waitForATick() {
     13  return new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
     14 }
     15 
     16 add_task(async function customScriptError() {
     17  const scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(
     18    Ci.nsIScriptError
     19  );
     20  scriptError.init(
     21    "foo",
     22    "file.js",
     23    1,
     24    2,
     25    Ci.nsIScriptError.warningFlag,
     26    "some javascript"
     27  );
     28  Services.console.logMessage(scriptError);
     29 
     30  await waitForATick();
     31 
     32  Assert.equal(
     33    lastMessage,
     34    scriptError,
     35    "We receive the exact same nsIScriptError object"
     36  );
     37 
     38  Assert.equal(lastMessage.errorMessage, "foo");
     39  Assert.equal(lastMessage.sourceName, "file.js");
     40  Assert.equal(lastMessage.lineNumber, 1);
     41  Assert.equal(lastMessage.columnNumber, 2);
     42  Assert.equal(lastMessage.flags, Ci.nsIScriptError.warningFlag);
     43  Assert.equal(lastMessage.category, "some javascript");
     44 
     45  Assert.equal(
     46    lastMessage.stack,
     47    undefined,
     48    "Custom nsIScriptError object created from JS can't convey any stack"
     49  );
     50 });
     51 
     52 add_task(async function callFunctionAndLogExceptionWithChromeGlobal() {
     53  try {
     54    ChromeUtils.callFunctionAndLogException(globalThis, function () {
     55      throw new Error("custom exception");
     56    });
     57    Assert.ok(false, "callFunctionAndLogException should throw");
     58  } catch (e) {
     59    Assert.equal(
     60      e.name,
     61      "Error",
     62      "callFunctionAndLogException thrown with the expected exception name"
     63    );
     64    Assert.equal(
     65      e.message,
     66      "custom exception",
     67      "callFunctionAndLogException thrown with the expected message"
     68    );
     69  }
     70 
     71  await waitForATick();
     72 
     73  Assert.ok(!!lastMessage, "Got the message");
     74  Assert.ok(
     75    lastMessage instanceof Ci.nsIScriptError,
     76    "This is a nsIScriptError"
     77  );
     78 
     79  Assert.equal(lastMessage.errorMessage, "Error: custom exception");
     80  Assert.equal(lastMessage.sourceName, _TEST_FILE);
     81  Assert.equal(lastMessage.lineNumber, 55);
     82  Assert.equal(lastMessage.columnNumber, 13);
     83  Assert.equal(lastMessage.flags, Ci.nsIScriptError.errorFlag);
     84  Assert.equal(lastMessage.category, "chrome javascript");
     85  Assert.ok(lastMessage.stack, "It has a stack");
     86  Assert.equal(lastMessage.stack.source, _TEST_FILE);
     87  Assert.equal(lastMessage.stack.line, 55);
     88  Assert.equal(lastMessage.stack.column, 13);
     89  Assert.ok(!!lastMessage.stack.parent, "stack has a parent frame");
     90  Assert.equal(
     91    lastMessage.innerWindowID,
     92    0,
     93    "The message isn't bound to any WindowGlobal"
     94  );
     95 });
     96 
     97 add_task(async function callFunctionAndLogExceptionWithContentGlobal() {
     98  const window = createContentWindow();
     99  try {
    100    ChromeUtils.callFunctionAndLogException(window, function () {
    101      throw new Error("another custom exception");
    102    });
    103    Assert.ok(false, "callFunctionAndLogException should throw");
    104  } catch (e) {
    105    Assert.equal(
    106      e.name,
    107      "Error",
    108      "callFunctionAndLogException thrown with the expected exception name"
    109    );
    110    Assert.equal(
    111      e.message,
    112      "another custom exception",
    113      "callFunctionAndLogException thrown with the expected message"
    114    );
    115  }
    116 
    117  await waitForATick();
    118 
    119  Assert.ok(!!lastMessage, "Got the message");
    120  Assert.ok(
    121    lastMessage instanceof Ci.nsIScriptError,
    122    "This is a nsIScriptError"
    123  );
    124 
    125  Assert.equal(lastMessage.errorMessage, "Error: another custom exception");
    126  Assert.equal(lastMessage.sourceName, _TEST_FILE);
    127  Assert.equal(lastMessage.lineNumber, 101);
    128  Assert.equal(lastMessage.columnNumber, 13);
    129  Assert.equal(lastMessage.flags, Ci.nsIScriptError.errorFlag);
    130  Assert.equal(lastMessage.category, "content javascript");
    131  Assert.ok(lastMessage.stack, "It has a stack");
    132  Assert.equal(lastMessage.stack.source, _TEST_FILE);
    133  Assert.equal(lastMessage.stack.line, 101);
    134  Assert.equal(lastMessage.stack.column, 13);
    135  Assert.ok(!!lastMessage.stack.parent, "stack has a parent frame");
    136  Assert.ok(
    137    !!window.windowGlobalChild.innerWindowId,
    138    "The window has a innerWindowId"
    139  );
    140  Assert.equal(
    141    lastMessage.innerWindowID,
    142    window.windowGlobalChild.innerWindowId,
    143    "The message is bound to the content window"
    144  );
    145 });
    146 
    147 add_task(async function callFunctionAndLogExceptionForContentScriptSandboxes() {
    148  const { sandbox, window } = createContentScriptSandbox();
    149  Cu.evalInSandbox(
    150    `function foo() { throw new Error("sandbox exception"); }`,
    151    sandbox,
    152    null,
    153    "sandbox-file.js",
    154    1,
    155    0
    156  );
    157  try {
    158    ChromeUtils.callFunctionAndLogException(window, sandbox.foo);
    159    Assert.fail("callFunctionAndLogException should throw");
    160  } catch (e) {
    161    Assert.equal(
    162      e.name,
    163      "Error",
    164      "callFunctionAndLogException thrown with the expected exception name"
    165    );
    166    Assert.equal(
    167      e.message,
    168      "sandbox exception",
    169      "callFunctionAndLogException thrown with the expected message"
    170    );
    171  }
    172 
    173  await waitForATick();
    174 
    175  Assert.ok(!!lastMessage, "Got the message");
    176  // Note that it is important to "instanceof" in order to expose the nsIScriptError attributes.
    177  Assert.ok(
    178    lastMessage instanceof Ci.nsIScriptError,
    179    "This is a nsIScriptError"
    180  );
    181 
    182  Assert.equal(lastMessage.errorMessage, "Error: sandbox exception");
    183  Assert.equal(lastMessage.sourceName, "sandbox-file.js");
    184  Assert.equal(lastMessage.lineNumber, 1);
    185  Assert.equal(lastMessage.columnNumber, 24);
    186  Assert.equal(lastMessage.flags, Ci.nsIScriptError.errorFlag);
    187  Assert.equal(lastMessage.category, "content javascript");
    188  Assert.ok(lastMessage.stack, "It has a stack");
    189  Assert.equal(lastMessage.stack.source, "sandbox-file.js");
    190  Assert.equal(lastMessage.stack.line, 1);
    191  Assert.equal(lastMessage.stack.column, 24);
    192  Assert.ok(!!lastMessage.stack.parent, "stack has a parent frame");
    193  Assert.ok(
    194    !!window.windowGlobalChild.innerWindowId,
    195    "The sandbox's prototype is a window and has a innerWindowId"
    196  );
    197  Assert.equal(
    198    lastMessage.innerWindowID,
    199    window.windowGlobalChild.innerWindowId,
    200    "The message is bound to the sandbox's prototype WindowGlobal"
    201  );
    202 });
    203 
    204 add_task(
    205  async function callFunctionAndLogExceptionForContentScriptSandboxesWrappedInChrome() {
    206    const { sandbox, window } = createContentScriptSandbox();
    207    Cu.evalInSandbox(
    208      `function foo() { throw new Error("sandbox exception"); }`,
    209      sandbox,
    210      null,
    211      "sandbox-file.js",
    212      1,
    213      0
    214    );
    215    try {
    216      ChromeUtils.callFunctionAndLogException(window, function () {
    217        sandbox.foo();
    218      });
    219      Assert.fail("callFunctionAndLogException should throw");
    220    } catch (e) {
    221      Assert.equal(
    222        e.name,
    223        "Error",
    224        "callFunctionAndLogException thrown with the expected exception name"
    225      );
    226      Assert.equal(
    227        e.message,
    228        "sandbox exception",
    229        "callFunctionAndLogException thrown with the expected message"
    230      );
    231    }
    232 
    233    await waitForATick();
    234 
    235    Assert.ok(!!lastMessage, "Got the message");
    236    // Note that it is important to "instanceof" in order to expose the nsIScriptError attributes.
    237    Assert.ok(
    238      lastMessage instanceof Ci.nsIScriptError,
    239      "This is a nsIScriptError"
    240    );
    241 
    242    Assert.ok(
    243      !!window.windowGlobalChild.innerWindowId,
    244      "The sandbox's prototype is a window and has a innerWindowId"
    245    );
    246    Assert.equal(
    247      lastMessage.innerWindowID,
    248      window.windowGlobalChild.innerWindowId,
    249      "The message is bound to the sandbox's prototype WindowGlobal"
    250    );
    251  }
    252 );
    253 
    254 add_task(function teardown() {
    255  Services.console.unregisterListener(consoleListener);
    256 });
    257 
    258 // We are in xpcshell, so we can't have a real DOM Window as in Firefox
    259 // but let's try to have a fake one.
    260 function createContentWindow() {
    261  const principal =
    262    Services.scriptSecurityManager.createContentPrincipalFromOrigin(
    263      "http://example.com/"
    264    );
    265 
    266  const webnav = Services.appShell.createWindowlessBrowser(false);
    267 
    268  webnav.docShell.createAboutBlankDocumentViewer(principal, principal);
    269 
    270  return webnav.document.defaultView;
    271 }
    272 
    273 // Create a Sandbox as in WebExtension content scripts
    274 function createContentScriptSandbox() {
    275  const window = createContentWindow();
    276  // The sandboxPrototype is the key here in order to
    277  // make xpc::SandboxWindowOrNull ignore the sandbox
    278  // and instead retrieve its prototype and link the error message
    279  // to the window instead of the sandbox.
    280  return {
    281    sandbox: Cu.Sandbox(window, { sandboxPrototype: window }),
    282    window,
    283  };
    284 }