mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	Because of this, we have to now pass the worker URL via WorkerDispatcher constructor. Doing this should help then extend WorkerDispatcher instead of manually instantiating it. About browser_jsterm_hide_when_devtools_chrome_enabled_false.js, as the SourceMapLoader is now started lazily, the worker is no longer started during this test and so we no longer have to wait for it. And browser_jsterm_await_paused.js was failing because we resumed too quickly. I added some code to better wait for awaitExpression full evaluation. Differential Revision: https://phabricator.services.mozilla.com/D164547
		
			
				
	
	
		
			157 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			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";
 | 
						|
 | 
						|
class WorkerDispatcher {
 | 
						|
  #msgId = 1;
 | 
						|
  #worker = null;
 | 
						|
  // Map of message ids -> promise resolution functions, for dispatching worker responses
 | 
						|
  #pendingCalls = new Map();
 | 
						|
  #url = "";
 | 
						|
 | 
						|
  constructor(url) {
 | 
						|
    this.#url = url;
 | 
						|
  }
 | 
						|
 | 
						|
  start() {
 | 
						|
    // When running in debugger jest test, we don't have access to ChromeWorker
 | 
						|
    if (typeof ChromeWorker == "function") {
 | 
						|
      this.#worker = new ChromeWorker(this.#url);
 | 
						|
    } else {
 | 
						|
      this.#worker = new Worker(this.#url);
 | 
						|
    }
 | 
						|
    this.#worker.onerror = err => {
 | 
						|
      console.error(`Error in worker ${this.#url}`, err.message);
 | 
						|
    };
 | 
						|
    this.#worker.addEventListener("message", this.#onMessage);
 | 
						|
  }
 | 
						|
 | 
						|
  stop() {
 | 
						|
    if (!this.#worker) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    this.#worker.removeEventListener("message", this.#onMessage);
 | 
						|
    this.#worker.terminate();
 | 
						|
    this.#worker = null;
 | 
						|
    this.#pendingCalls.clear();
 | 
						|
  }
 | 
						|
 | 
						|
  task(method, { queue = false } = {}) {
 | 
						|
    const calls = [];
 | 
						|
    const push = args => {
 | 
						|
      return new Promise((resolve, reject) => {
 | 
						|
        if (queue && calls.length === 0) {
 | 
						|
          Promise.resolve().then(flush);
 | 
						|
        }
 | 
						|
 | 
						|
        calls.push({ args, resolve, reject });
 | 
						|
 | 
						|
        if (!queue) {
 | 
						|
          flush();
 | 
						|
        }
 | 
						|
      });
 | 
						|
    };
 | 
						|
 | 
						|
    const flush = () => {
 | 
						|
      const items = calls.slice();
 | 
						|
      calls.length = 0;
 | 
						|
 | 
						|
      if (!this.#worker) {
 | 
						|
        this.start();
 | 
						|
      }
 | 
						|
 | 
						|
      const id = this.#msgId++;
 | 
						|
      this.#worker.postMessage({
 | 
						|
        id,
 | 
						|
        method,
 | 
						|
        calls: items.map(item => item.args),
 | 
						|
      });
 | 
						|
 | 
						|
      this.#pendingCalls.set(id, items);
 | 
						|
    };
 | 
						|
 | 
						|
    return (...args) => push(args);
 | 
						|
  }
 | 
						|
 | 
						|
  invoke(method, ...args) {
 | 
						|
    return this.task(method)(...args);
 | 
						|
  }
 | 
						|
 | 
						|
  #onMessage = ({ data: result }) => {
 | 
						|
    const items = this.#pendingCalls.get(result.id);
 | 
						|
    this.#pendingCalls.delete(result.id);
 | 
						|
    if (!items) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!this.#worker) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    result.results.forEach((resultData, i) => {
 | 
						|
      const { resolve, reject } = items[i];
 | 
						|
 | 
						|
      if (resultData.error) {
 | 
						|
        const err = new Error(resultData.message);
 | 
						|
        err.metadata = resultData.metadata;
 | 
						|
        reject(err);
 | 
						|
      } else {
 | 
						|
        resolve(resultData.response);
 | 
						|
      }
 | 
						|
    });
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
function workerHandler(publicInterface) {
 | 
						|
  return function(msg) {
 | 
						|
    const { id, method, calls } = msg.data;
 | 
						|
 | 
						|
    Promise.all(
 | 
						|
      calls.map(args => {
 | 
						|
        try {
 | 
						|
          const response = publicInterface[method].apply(undefined, args);
 | 
						|
          if (response instanceof Promise) {
 | 
						|
            return response.then(
 | 
						|
              val => ({ response: val }),
 | 
						|
              err => asErrorMessage(err)
 | 
						|
            );
 | 
						|
          }
 | 
						|
          return { response };
 | 
						|
        } catch (error) {
 | 
						|
          return asErrorMessage(error);
 | 
						|
        }
 | 
						|
      })
 | 
						|
    ).then(results => {
 | 
						|
      globalThis.postMessage({ id, results });
 | 
						|
    });
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
function asErrorMessage(error) {
 | 
						|
  if (typeof error === "object" && error && "message" in error) {
 | 
						|
    // Error can't be sent via postMessage, so be sure to convert to
 | 
						|
    // string.
 | 
						|
    return {
 | 
						|
      error: true,
 | 
						|
      message: error.message,
 | 
						|
      metadata: error.metadata,
 | 
						|
    };
 | 
						|
  }
 | 
						|
 | 
						|
  return {
 | 
						|
    error: true,
 | 
						|
    message: error == null ? error : error.toString(),
 | 
						|
    metadata: undefined,
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
// Might be loaded within a worker thread where `module` isn't available.
 | 
						|
if (typeof module !== "undefined") {
 | 
						|
  module.exports = {
 | 
						|
    WorkerDispatcher,
 | 
						|
    workerHandler,
 | 
						|
  };
 | 
						|
}
 |