summaryrefslogtreecommitdiffstats
path: root/devtools/client/fronts/descriptors/process.js
blob: 6efc1bd4d7742dde77e31887666aa34ffefb39f7 (plain)
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* 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 {
  processDescriptorSpec,
} = require("resource://devtools/shared/specs/descriptors/process.js");
const {
  WindowGlobalTargetFront,
} = require("resource://devtools/client/fronts/targets/window-global.js");
const {
  ContentProcessTargetFront,
} = require("resource://devtools/client/fronts/targets/content-process.js");
const {
  FrontClassWithSpec,
  registerFront,
} = require("resource://devtools/shared/protocol.js");
const {
  DescriptorMixin,
} = require("resource://devtools/client/fronts/descriptors/descriptor-mixin.js");
const DESCRIPTOR_TYPES = require("resource://devtools/client/fronts/descriptors/descriptor-types.js");

class ProcessDescriptorFront extends DescriptorMixin(
  FrontClassWithSpec(processDescriptorSpec)
) {
  constructor(client, targetFront, parentFront) {
    super(client, targetFront, parentFront);
    this._isParent = false;
    this._processTargetFront = null;
    this._targetFrontPromise = null;
  }

  descriptorType = DESCRIPTOR_TYPES.PROCESS;

  form(json) {
    this.id = json.id;
    this._isParent = json.isParent;
    this._isWindowlessParent = json.isWindowlessParent;
    this.traits = json.traits || {};
  }

  async _createProcessTargetFront(form) {
    let front = null;
    // the request to getTarget may return a ContentProcessTargetActor or a
    // ParentProcessTargetActor. In most cases getProcess(0) will return the
    // main process target actor, which is a ParentProcessTargetActor, but
    // not in xpcshell, which uses a ContentProcessTargetActor. So select
    // the right front based on the actor ID.
    if (form.actor.includes("parentProcessTarget")) {
      // ParentProcessTargetActor doesn't have a specific front, instead it uses
      // WindowGlobalTargetFront on the client side.
      front = new WindowGlobalTargetFront(this._client, null, this);
    } else {
      front = new ContentProcessTargetFront(this._client, null, this);
    }
    // As these fronts aren't instantiated by protocol.js, we have to set their actor ID
    // manually like that:
    front.actorID = form.actor;
    front.form(form);

    // @backward-compat { version 84 } Older server don't send the processID in the form
    if (!front.processID) {
      front.processID = this.id;
    }

    this.manage(front);
    return front;
  }

  /**
   * This flag should be true for parent process descriptors of a regular
   * browser instance, where you can expect the target to be associated with a
   * window global.
   *
   * This will typically be true for the descriptor used by the Browser Toolbox
   * or the Browser Console opened against a regular Firefox instance.
   *
   * On the contrary this will be false for parent process descriptors created
   * for xpcshell debugging or for background task debugging.
   */
  get isBrowserProcessDescriptor() {
    return this._isParent && !this._isWindowlessParent;
  }

  get isParentProcessDescriptor() {
    return this._isParent;
  }

  get isProcessDescriptor() {
    return true;
  }

  getCachedTarget() {
    return this._processTargetFront;
  }

  async getTarget() {
    // Only return the cached Target if it is still alive.
    if (this._processTargetFront && !this._processTargetFront.isDestroyed()) {
      return this._processTargetFront;
    }
    // Otherwise, ensure that we don't try to spawn more than one Target by
    // returning the pending promise
    if (this._targetFrontPromise) {
      return this._targetFrontPromise;
    }
    this._targetFrontPromise = (async () => {
      let targetFront = null;
      try {
        const targetForm = await super.getTarget();
        targetFront = await this._createProcessTargetFront(targetForm);
      } catch (e) {
        // This is likely to happen if we get a lot of events which drop previous
        // processes.
        console.log(
          `Request to connect to ProcessDescriptor "${this.id}" failed: ${e}`
        );
      }
      // Save the reference to the target only after the call to attach
      // so that getTarget always returns the attached target in case of concurrent calls
      this._processTargetFront = targetFront;
      // clear the promise if we are finished so that we can re-connect if
      // necessary
      this._targetFrontPromise = null;
      return targetFront;
    })();
    return this._targetFrontPromise;
  }

  destroy() {
    if (this._processTargetFront) {
      this._processTargetFront.destroy();
      this._processTargetFront = null;
    }
    this._targetFrontPromise = null;
    super.destroy();
  }
}

exports.ProcessDescriptorFront = ProcessDescriptorFront;
registerFront(ProcessDescriptorFront);