/* 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";

const { ObjectActor } = require("resource://devtools/server/actors/object.js");

class PauseScopedObjectActor extends ObjectActor {
  /**
   * Creates a pause-scoped actor for the specified object.
   *
   * @see ObjectActor
   */
  constructor(threadActor, obj, hooks) {
    super(threadActor, obj, hooks);

    const guardWithPaused = [
      "decompile",
      "displayString",
      "ownPropertyNames",
      "parameterNames",
      "property",
      "prototype",
      "prototypeAndProperties",
      "scope",
    ];

    for (const methodName of guardWithPaused) {
      this[methodName] = this.withPaused(this[methodName]);
    }

    // Cache this thread actor attribute as we may query it after the actor destruction.
    this.threadLifetimePool = this.threadActor.threadLifetimePool;
  }

  isThreadLifetimePool() {
    return this.getParent() === this.threadLifetimePool;
  }

  isPaused() {
    return this.threadActor ? this.threadActor.state === "paused" : true;
  }

  withPaused(method) {
    return function () {
      if (this.isPaused()) {
        return method.apply(this, arguments);
      }

      return {
        error: "wrongState",
        message:
          this.constructor.name +
          " actors can only be accessed while the thread is paused.",
      };
    };
  }

  /**
   * Handle a protocol request to promote a pause-lifetime grip to a
   * thread-lifetime grip.
   *
   * This method isn't used by DevTools frontend, but by VS Code Firefox adapter
   * in order to keep the object actor alive after resume and be able to remove
   * watchpoints.
   */
  threadGrip() {
    this.threadActor.promoteObjectToThreadLifetime(this);
    return {};
  }

  /**
   * Handle a protocol request to release a thread-lifetime grip.
   */
  destroy() {
    if (!this.isThreadLifetimePool()) {
      return {
        error: "notReleasable",
        message: "Only thread-lifetime actors can be released.",
      };
    }

    super.destroy();
    return null;
  }
}

exports.PauseScopedObjectActor = PauseScopedObjectActor;
