summaryrefslogtreecommitdiffstats
path: root/devtools/client/webconsole/new-console-output/components/grip-message-body.js
blob: 29c2e6a4f2487958fa8f78459a6ea4b5ebc4191b (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
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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";

// If this is being run from Mocha, then the browser loader hasn't set up
// define. We need to do that before loading Rep.
if (typeof define === "undefined") {
  require("amd-loader");
}

// React
const {
  createFactory,
  PropTypes
} = require("devtools/client/shared/vendor/react");
const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
const { Rep } = createFactories(require("devtools/client/shared/components/reps/rep"));
const StringRep = createFactories(require("devtools/client/shared/components/reps/string").StringRep).rep;
const VariablesViewLink = createFactory(require("devtools/client/webconsole/new-console-output/components/variables-view-link"));
const { Grip } = require("devtools/client/shared/components/reps/grip");

GripMessageBody.displayName = "GripMessageBody";

GripMessageBody.propTypes = {
  grip: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.object,
  ]).isRequired,
  serviceContainer: PropTypes.shape({
    createElement: PropTypes.func.isRequired,
  }),
  userProvidedStyle: PropTypes.string,
};

function GripMessageBody(props) {
  const { grip, userProvidedStyle, serviceContainer } = props;

  let styleObject;
  if (userProvidedStyle && userProvidedStyle !== "") {
    styleObject = cleanupStyle(userProvidedStyle, serviceContainer.createElement);
  }

  return (
    // @TODO once there is a longString rep, also turn off quotes for those.
    typeof grip === "string"
      ? StringRep({
        object: grip,
        useQuotes: false,
        mode: props.mode,
        style: styleObject
      })
      : Rep({
        object: grip,
        objectLink: VariablesViewLink,
        defaultRep: Grip,
        mode: props.mode,
      })
  );
}

function cleanupStyle(userProvidedStyle, createElement) {
  // Regular expression that matches the allowed CSS property names.
  const allowedStylesRegex = new RegExp(
    "^(?:-moz-)?(?:background|border|box|clear|color|cursor|display|float|font|line|" +
    "margin|padding|text|transition|outline|white-space|word|writing|" +
    "(?:min-|max-)?width|(?:min-|max-)?height)"
  );

  // Regular expression that matches the forbidden CSS property values.
  const forbiddenValuesRegexs = [
    // url(), -moz-element()
    /\b(?:url|(?:-moz-)?element)[\s('"]+/gi,

    // various URL protocols
    /['"(]*(?:chrome|resource|about|app|data|https?|ftp|file):+\/*/gi,
  ];

  // Use a dummy element to parse the style string.
  let dummy = createElement("div");
  dummy.style = userProvidedStyle;

  // Return a style object as expected by React DOM components, e.g.
  // {color: "red"}
  // without forbidden properties and values.
  return [...dummy.style]
    .filter(name => {
      return allowedStylesRegex.test(name)
        && !forbiddenValuesRegexs.some(regex => regex.test(dummy.style[name]));
    })
    .reduce((object, name) => {
      return Object.assign({
        [name]: dummy.style[name]
      }, object);
    }, {});
}

module.exports = GripMessageBody;