summaryrefslogtreecommitdiffstats
path: root/devtools/client/responsive/utils/ua.js
blob: 70c99c552d343a9f204a04a695fbd5b9dd0c3db2 (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
/* 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 BROWSERS = [
  {
    name: "Firefox",
    mustContain: new RegExp(`(?:Firefox|FxiOS)\/(${getVersionRegex(1, 1)})`),
  },
  {
    name: "Opera",
    mustContain: new RegExp(`(?:OPR|Opera)\/(${getVersionRegex(1, 1)})`),
  },
  {
    name: "Safari",
    mustContain: new RegExp(`Version\/(${getVersionRegex(1, 1)}).+Safari`),
    mustNotContain: new RegExp("Chrome|Chromium"),
  },
  {
    name: "Edge",
    mustContain: new RegExp(`Edge\/(${getVersionRegex(0, 1)})`),
  },
  {
    name: "Chrome",
    mustContain: new RegExp(`(?:Chrome|CriOS)\/(${getVersionRegex(1, 1)})`),
  },
  {
    name: "IE",
    mustContain: new RegExp(`MSIE (${getVersionRegex(1, 1)})`),
  },
];

const OSES = [
  {
    name: "iOS",
    minMinorVersionCount: 0,
    mustContain: new RegExp(`CPU iPhone OS (${getVersionRegex(0, 2)})`),
  },
  {
    name: "iPadOS",
    minMinorVersionCount: 0,
    mustContain: new RegExp(`CPU OS (${getVersionRegex(0, 2)})`),
  },
  {
    name: "Windows Phone",
    minMinorVersionCount: 1,
    mustContain: new RegExp(`Windows Phone (${getVersionRegex(1, 2)})`),
  },
  {
    name: "Chrome OS",
    minMinorVersionCount: 1,
    mustContain: new RegExp(`CrOS .+ (${getVersionRegex(1, 2)})`),
  },
  {
    name: "Android",
    minMinorVersionCount: 0,
    mustContain: new RegExp(`Android (${getVersionRegex(0, 2)})`),
  },
  {
    name: "Windows NT",
    minMinorVersionCount: 1,
    mustContain: new RegExp(`Windows NT (${getVersionRegex(1, 2)})`),
  },
  {
    name: "Mac OSX",
    minMinorVersionCount: 1,
    mustContain: new RegExp(`Intel Mac OS X (${getVersionRegex(1, 2)})`),
  },
  {
    name: "Linux",
    mustContain: new RegExp("Linux"),
  },
];

function getVersionRegex(minMinorVersionCount, maxMinorVersionCount) {
  return `\\d+(?:[._][0-9a-z]+){${minMinorVersionCount},${maxMinorVersionCount}}`;
}

function detect(ua, dataset) {
  for (const {
    name,
    mustContain,
    mustNotContain,
    minMinorVersionCount,
  } of dataset) {
    const result = mustContain.exec(ua);

    if (!result) {
      continue;
    }

    if (mustNotContain && mustNotContain.test(ua)) {
      continue;
    }

    let version = null;

    if (result && result.length === 2) {
      // Remove most minor version if that expresses 0.
      let parts = result[1].match(/([0-9a-z]+)/g);
      parts = parts.reverse();
      const validVersionIndex = parts.findIndex(
        part => parseInt(part, 10) !== 0
      );
      if (validVersionIndex !== -1) {
        parts = parts.splice(validVersionIndex);
        for (let i = 0; i < minMinorVersionCount + 1 - parts.length; i++) {
          parts.unshift(0);
        }
      }
      version = parts.reverse().join(".");
    }

    return { name, version };
  }

  return null;
}

function parseUserAgent(ua) {
  return {
    browser: detect(ua, BROWSERS),
    os: detect(ua, OSES),
  };
}

module.exports = { parseUserAgent };