summaryrefslogtreecommitdiffstats
path: root/devtools/server/actors/compatibility/compatibility.js
blob: 4ace06431d5148cf8e323f4d17ccbb06a9a5bf5d (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/* 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";

var protocol = require("devtools/shared/protocol");
const { compatibilitySpec } = require("devtools/shared/specs/compatibility");

loader.lazyGetter(this, "mdnCompatibility", () => {
  const MDNCompatibility = require("devtools/server/actors/compatibility/lib/MDNCompatibility");
  const cssPropertiesCompatData = require("devtools/shared/compatibility/dataset/css-properties.json");
  return new MDNCompatibility(cssPropertiesCompatData);
});

const CompatibilityActor = protocol.ActorClassWithSpec(compatibilitySpec, {
  /**
   * Create a CompatibilityActor.
   * CompatibilityActor is responsible for providing the compatibility information
   * for the web page using the data from the Inspector and the `MDNCompatibility`
   * and conveys them to the compatibility panel in the DevTool Inspector. Currently,
   * the `CompatibilityActor` only detects compatibility issues in the CSS declarations
   * but plans are in motion to extend it to evaluate compatibility information for
   * HTML and JavaScript.
   * The design below has the InspectorActor own the CompatibilityActor, but it's
   * possible we will want to move it into it's own panel in the future.
   *
   * @param inspector
   *    The InspectorActor that owns this CompatibilityActor.
   *
   * @constructor
   */
  initialize: function(inspector) {
    protocol.Actor.prototype.initialize.call(this, inspector.conn);
    this.inspector = inspector;
  },

  destroy: function() {
    protocol.Actor.prototype.destroy.call(this);
    this.inspector = null;
  },

  form() {
    return {
      actor: this.actorID,
    };
  },

  getTraits() {
    return {
      traits: {},
    };
  },

  /**
   * Responsible for computing the compatibility issues for a given CSS declaration block
   * @param Array
   *  Array of CSS declaration object of the form:
   *    {
   *      // Declaration name
   *      name: <string>,
   *      // Declaration value
   *      value: <string>,
   *    }
   * @param array targetBrowsers
   *  Array of target browsers to be used to check CSS compatibility against.
   *  It is an Array of the following form
   *     {
   *       // Browser id as specified in `devtools/shared/compatibility/datasets/browser.json`
   *       id: <string>,
   *       name: <string>,
   *       version: <string>,
   *       // Browser status - esr, current, beta, nightly
   *       status: <string>,
   *     }
   * @returns An Array of JSON objects with compatibility information in following form:
   *    {
   *      // Type of compatibility issue
   *      type: <string>,
   *      // The CSS declaration that has compatibility issues
   *      property: <string>,
   *      // Alias to the given CSS property
   *      alias: <Array>,
   *      // Link to MDN documentation for the particular CSS rule
   *      url: <string>,
   *      deprecated: <boolean>,
   *      experimental: <boolean>,
   *      // An array of all the browsers that don't support the given CSS rule
   *      unsupportedBrowsers: <Array>,
   *    }
   */
  getCSSDeclarationBlockIssues: function(declarationBlock, targetBrowsers) {
    return mdnCompatibility.getCSSDeclarationBlockIssues(
      declarationBlock,
      targetBrowsers
    );
  },

  /**
   * Responsible for computing the compatibility issues in the
   * CSS declaration of the given node.
   * @param NodeActor node
   * @param targetBrowsers Array
   *   An Array of JSON object of target browser to check compatibility against in following form:
   *   {
   *     // Browser id as specified in `devtools/server/actors/compatibility/lib/datasets/browser.json`
   *     id: <string>,
   *     name: <string>,
   *     version: <string>,
   *     // Browser status - esr, current, beta, nightly
   *     status: <string>,
   *   }
   * @returns An Array of JSON objects with compatibility information in following form:
   *    {
   *      // Type of compatibility issue
   *      type: <string>,
   *      // The CSS declaration that has compatibility issues
   *      property: <string>,
   *      // Alias to the given CSS property
   *      alias: <Array>,
   *      // Link to MDN documentation for the particular CSS rule
   *      url: <string>,
   *      deprecated: <boolean>,
   *      experimental: <boolean>,
   *      // An array of all the browsers that don't support the given CSS rule
   *      unsupportedBrowsers: <Array>,
   *    }
   */
  async getNodeCssIssues(node, targetBrowsers) {
    const pageStyle = await this.inspector.getPageStyle();
    const styles = await pageStyle.getApplied(node, {
      skipPseudo: false,
    });

    const declarationBlocks = styles.entries
      .map(({ rule }) => {
        // Replace form() with a function to get minimal subset
        // of declarations from StyleRuleActor when such a
        // function lands in the StyleRuleActor
        const declarations = rule.form().declarations;
        if (!declarations) {
          return null;
        }
        return declarations.filter(d => !d.commentOffsets);
      })
      .filter(declarations => declarations && declarations.length);

    return declarationBlocks
      .map(declarationBlock =>
        mdnCompatibility.getCSSDeclarationBlockIssues(
          declarationBlock,
          targetBrowsers
        )
      )
      .flat()
      .reduce((issues, issue) => {
        // Get rid of duplicate issue
        return issues.find(
          i => i.type === issue.type && i.property === issue.property
        )
          ? issues
          : [...issues, issue];
      }, []);
  },
});

module.exports = {
  CompatibilityActor,
};