DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (e03e0c60462c)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

this.EXPORTED_SYMBOLS = [
  "ContentTask"
];

const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Services.jsm");

const FRAME_SCRIPT = "resource://testing-common/content-task.js";

/**
 * Keeps track of whether the frame script was already loaded.
 */
var gFrameScriptLoaded = false;

/**
 * Mapping from message id to associated promise.
 */
var gPromises = new Map();

/**
 * Incrementing integer to generate unique message id.
 */
var gMessageID = 1;

/**
 * This object provides the public module functions.
 */
this.ContentTask = {
  /**
   * _testScope saves the current testScope from
   * browser-test.js. This is used to implement SimpleTest functions
   * like ok() and is() in the content process. The scope is only
   * valid for tasks spawned in the current test, so we keep track of
   * the ID of the first task spawned in this test (_scopeValidId).
   */
  _testScope: null,
  _scopeValidId: 0,

  /**
   * Creates and starts a new task in a browser's content.
   *
   * @param browser A xul:browser
   * @param arg A single serializable argument that will be passed to the
   *             task when executed on the content process.
   * @param task
   *        - A generator or function which will be serialized and sent to
   *          the remote browser to be executed. Unlike Task.spawn, this
   *          argument may not be an iterator as it will be serialized and
   *          sent to the remote browser.
   * @return A promise object where you can register completion callbacks to be
   *         called when the task terminates.
   * @resolves With the final returned value of the task if it executes
   *           successfully.
   * @rejects An error message if execution fails.
   */
  spawn: function ContentTask_spawn(browser, arg, task) {
    // Load the frame script if needed.
    if (!gFrameScriptLoaded) {
      Services.mm.loadFrameScript(FRAME_SCRIPT, true);
      gFrameScriptLoaded = true;
    }

    let deferred = {};
    deferred.promise = new Promise((resolve, reject) => {
      deferred.resolve = resolve;
      deferred.reject = reject;
    });

    let id = gMessageID++;
    gPromises.set(id, deferred);

    browser.messageManager.sendAsyncMessage(
      "content-task:spawn",
      {
        id: id,
        runnable: task.toString(),
        arg: arg,
      });

    return deferred.promise;
  },

  setTestScope(scope) {
    this._testScope = scope;
    this._scopeValidId = gMessageID;
  },
};

var ContentMessageListener = {
  receiveMessage(aMessage) {
    let id = aMessage.data.id;

    if (id < ContentTask._scopeValidId) {
      throw new Error("test result returned after test finished");
    }

    if (aMessage.name == "content-task:complete") {
      let deferred = gPromises.get(id);
      gPromises.delete(id);

      if (aMessage.data.error) {
        deferred.reject(aMessage.data.error);
      } else {
        deferred.resolve(aMessage.data.result);
      }
    } else if (aMessage.name == "content-task:test-result") {
      let data = aMessage.data;
      ContentTask._testScope.ok(data.condition, data.name, null, data.stack);
    } else if (aMessage.name == "content-task:test-info") {
      ContentTask._testScope.info(aMessage.data.name);
    } else if (aMessage.name == "content-task:test-todo") {
      ContentTask._testScope.todo(aMessage.data.expr, aMessage.data.name);
    }
  },
};

Services.mm.addMessageListener("content-task:complete", ContentMessageListener);
Services.mm.addMessageListener("content-task:test-result", ContentMessageListener);
Services.mm.addMessageListener("content-task:test-info", ContentMessageListener);