summaryrefslogtreecommitdiffstats
path: root/devtools/shared/commands/inspected-window/inspected-window-command.js
blob: 0d15016ebdfc5183bbc50240528f934a30b2ae04 (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
143
144
145
/* 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 {
  getAdHocFrontOrPrimitiveGrip,
  // eslint-disable-next-line mozilla/reject-some-requires
} = require("resource://devtools/client/fronts/object.js");

/**
 * For now, this class is mostly a wrapper around webExtInspectedWindow actor.
 */
class InspectedWindowCommand {
  constructor({ commands }) {
    this.commands = commands;
  }

  /**
   * Return a promise that resolves to the related target actor's front.
   * The Web Extension inspected window actor.
   *
   * @return {Promise<WebExtensionInspectedWindowFront>}
   */
  getFront() {
    return this.commands.targetCommand.targetFront.getFront(
      "webExtensionInspectedWindow"
    );
  }

  /**
   * Evaluate the provided javascript code in a target window.
   *
   * @param {Object} webExtensionCallerInfo - The addonId and the url (the addon base url
   *        or the url of the actual caller filename and lineNumber) used to log useful
   *        debugging information in the produced error logs and eval stack trace.
   * @param {String} expression - The expression to evaluate.
   * @param {Object} options - An option object. Check the actor method definition to see
   *        what properties it can hold (minus the `consoleFront` property which is defined
   *        below).
   * @param {WebConsoleFront} options.consoleFront - An optional webconsole front. When
   *         set, the result will be either a primitive, a LongStringFront or an
   *         ObjectFront, and the WebConsoleActor corresponding to the console front will
   *         be used to generate those, which is needed if we want to handle ObjectFronts
   *         on the client.
   */
  async eval(webExtensionCallerInfo, expression, options = {}) {
    const { consoleFront } = options;

    if (consoleFront) {
      options.evalResultAsGrip = true;
      options.toolboxConsoleActorID = consoleFront.actor;
      delete options.consoleFront;
    }

    const front = await this.getFront();
    const response = await front.eval(
      webExtensionCallerInfo,
      expression,
      options
    );

    // If no consoleFront was provided, we can directly return the response.
    if (!consoleFront) {
      return response;
    }

    if (
      !response.hasOwnProperty("exceptionInfo") &&
      !response.hasOwnProperty("valueGrip")
    ) {
      throw new Error(
        "Response does not have `exceptionInfo` or `valueGrip` property"
      );
    }

    if (response.exceptionInfo) {
      console.error(
        response.exceptionInfo.description,
        ...(response.exceptionInfo.details || [])
      );
      return response;
    }

    // On the server, the valueGrip is created from the toolbox webconsole actor.
    // If we want since the ObjectFront connection is inherited from the parent front, we
    // need to set the console front as the parent front.
    return getAdHocFrontOrPrimitiveGrip(
      response.valueGrip,
      consoleFront || this
    );
  }

  /**
   * Reload the target tab, optionally bypass cache, customize the userAgent and/or
   * inject a script in targeted document or any of its sub-frame.
   *
   * @param {WebExtensionCallerInfo} callerInfo
   *   the addonId and the url (the addon base url or the url of the actual caller
   *   filename and lineNumber) used to log useful debugging information in the
   *   produced error logs and eval stack trace.
   * @param {Object} options
   * @param {boolean|undefined} options.ignoreCache
   *        Enable/disable the cache bypass headers.
   * @param {string|undefined} options.injectedScript
   *        Evaluate the provided javascript code in the top level and every sub-frame
   *        created during the page reload, before any other script in the page has been
   *        executed.
   * @param {string|undefined} options.userAgent
   *        Customize the userAgent during the page reload.
   * @returns {Promise} A promise that resolves once the page is done loading when userAgent
   *          or injectedScript option are passed. If those options are not provided, the
   *          Promise will resolve after the reload was initiated.
   */
  async reload(callerInfo, options = {}) {
    if (this._reloadPending) {
      return null;
    }

    this._reloadPending = true;

    try {
      // We always want to update the target configuration to set the user agent if one is
      // passed, or to reset a potential existing override if userAgent isn't defined.
      await this.commands.targetConfigurationCommand.updateConfiguration({
        customUserAgent: options.userAgent,
      });

      const front = await this.getFront();
      const result = await front.reload(callerInfo, options);
      this._reloadPending = false;

      return result;
    } catch (e) {
      this._reloadPending = false;
      console.error(e);
      return Promise.reject({
        message: "An unexpected error occurred",
      });
    }
  }
}

module.exports = InspectedWindowCommand;