Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,5 +361,6 @@ module.exports = {
btoa: 'readable',
atob: 'readable',
performance: 'readable',
navigator: 'readable',
},
};
55 changes: 55 additions & 0 deletions doc/api/globals.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,58 @@ The `MessagePort` class. See [`MessagePort`][] for more details.

This variable may appear to be global but is not. See [`module`][].

## `navigator`
<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

An implementation of the [Navigator API][]. Similar to [`window.navigator`][]
in browsers.

### `navigator.deviceMemory`
<!-- YAML
added: REPLACEME
-->

* {number}

The total amount of device memory in GiB, rounded to the nearest power of 2,
between 0.25 and 8 GiB. Part of the [Device Memory API][].

```js
console.log(`This device has ${navigator.deviceMemory} GiB of RAM`);
```

### `navigator.hardwareConcurrency`
<!-- YAML
added: REPLACEME
-->

* {integer}

The number of logical processors.

```js
console.log(`This device has ${navigator.hardwareConcurrency} logical CPUs`);
```

### `navigator.platform`

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The specification does not indicate any deprecation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm 😕
Confused... Seems like MDN and TypeScript have incorrectly marked it as deprecated?

https://stackoverflow.com/questions/38506517/deprecated-javascript-os-detection-techniques

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimmywarting MDN takes the term "deprecated" to mean "use of this API is discouraged" - not "this API is on the way to removal from the spec". Under MDN's meaning of the term this marking as deprecated is correct because most of the properties/methods return incorrect and/or inconsistent information. There are more reliable ways to get the information.

Of course it would be good if the reasoning was provided along with the tagging.

Copy link

@sideshowbarker sideshowbarker Aug 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that around https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator-oscpu, the spec adds this big red warning about the entire set of NavigatorID members (including platform):

Any information in this API that varies from user to user can be used to profile the user. In fact, if enough such information is available, a user can actually be uniquely identified. For this reason, user agent implementers are strongly urged to include as little information in this API as possible.

That — along with the fact that most of the members are useless because they’re required by the spec to either not ever return anything useful or to not reliably/deterministically return anything useful — is why we have it flagged as deprecated in MDN: As a “don’t use this” warning to developers, so we don’t end up causing developers to mistakenly try to use any of it.

For example, in the case of platform, the spec requirements are:

Must return either the empty string or a string representing the platform on which the browser is executing, e.g. "MacIntel", "Win32", "FreeBSD i386", "WebTV OS".

So, given that browsers can choose to always return the empty string for it, that means developers cannot and should not write code which assumes it returns "MacIntel", "Win32", "FreeBSD i386", "WebTV OS" or anything else actually useful. So it’s flagged as deprecated in MDN to help developers avoid making that mistake.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless the spec have marked it as deprecated or removed it then i think you should use the term "Legacy" instead of "Deprecated" like NodeJS docs dose or something like that.
Think "Deprecated" is a too strong word for what it actually means.

Say something in terms of: "Legacy discouraged fingerprinting, use feature detection instead"

<!-- YAML
added: REPLACEME
-->

* {string}

A string identifying the operating system platform on which the Node.js process
is running. For example, it returns 'Linux' on Linux, 'Darwin' on macOS, and
'Win32' on Windows.

```js
console.log(`This process is running on ${navigator.platform}`);
```

## `performance`

The [`perf_hooks.performance`][] object.
Expand Down Expand Up @@ -429,6 +481,8 @@ The object that acts as the namespace for all W3C
[WebAssembly][webassembly-org] related functionality. See the
[Mozilla Developer Network][webassembly-mdn] for usage and compatibility.

[Device Memory API]: https://w3c.github.io/device-memory/
[Navigator API]: https://html.spec.whatwg.org/multipage/system-state.html#the-navigator-object
[`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
[`EventTarget` and `Event` API]: events.md#event-target-and-event-api
[`MessageChannel`]: worker_threads.md#worker_threads_class_messagechannel
Expand All @@ -455,6 +509,7 @@ The object that acts as the namespace for all W3C
[`setImmediate`]: timers.md#timers_setimmediate_callback_args
[`setInterval`]: timers.md#timers_setinterval_callback_delay_args
[`setTimeout`]: timers.md#timers_settimeout_callback_delay_args
[`window.navigator`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator
[buffer section]: buffer.md
[built-in objects]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
[module system documentation]: modules.md
Expand Down
8 changes: 8 additions & 0 deletions lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,14 @@ if (!config.noBrowserGlobals) {

defineOperation(globalThis, 'queueMicrotask', queueMicrotask);

// https://html.spec.whatwg.org/multipage/system-state.html#the-navigator-object
ObjectDefineProperty(globalThis, 'navigator', {
enumerable: true,
configurable: true,
writable: false,
value: require('internal/navigator'),
});

// https://www.w3.org/TR/hr-time-2/#the-performance-attribute
defineReplacableAttribute(globalThis, 'performance',
require('perf_hooks').performance);
Expand Down
67 changes: 67 additions & 0 deletions lib/internal/navigator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use strict';

const {
ObjectDefineProperties,
ObjectSetPrototypeOf,
MathClz32,
} = primordials;

const {
codes: {
ERR_ILLEGAL_CONSTRUCTOR,
}
} = require('internal/errors');

const {
getCPUs,
getOSInformation,
getTotalMem,
} = internalBinding('os');

class Navigator {
constructor() {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
}

class InternalNavigator {}
InternalNavigator.prototype.constructor = Navigator.prototype.constructor;
ObjectSetPrototypeOf(InternalNavigator.prototype, Navigator.prototype);

function getDeviceMemory() {
const mem = getTotalMem() / 1024 / 1024;
if (mem <= 0.25 * 1024) return 0.25;
if (mem >= 8 * 1024) return 8;
const lowerBound = 1 << 31 - MathClz32(mem - 1);
const upperBound = lowerBound * 2;
return mem - lowerBound <= upperBound - mem ?
lowerBound / 1024 :
upperBound / 1024;
}

function getPlatform() {
if (process.platform === 'win32') return 'Win32';
if (process.platform === 'android') return 'Android';
return getOSInformation()[0];
}

const cpuData = getCPUs();
ObjectDefineProperties(Navigator.prototype, {
deviceMemory: {
configurable: true,
enumerable: true,
value: getDeviceMemory(),
},
hardwareConcurrency: {
configurable: true,
enumerable: true,
value: cpuData ? cpuData.length / 7 : 1,
},
platform: {
configurable: true,
enumerable: true,
value: getPlatform(),
},
});

module.exports = new InternalNavigator();
4 changes: 3 additions & 1 deletion test/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,9 @@ if (global.AbortController)
if (global.gc) {
knownGlobals.push(global.gc);
}

if (global.navigator) {
knownGlobals.push(global.navigator);
}
if (global.performance) {
knownGlobals.push(global.performance);
}
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/wpt/LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The 3-Clause BSD License

Copyright 2019 web-platform-tests contributors
Copyright © web-platform-tests contributors

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/wpt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ Last update:

- common: https://github.com/web-platform-tests/wpt/tree/bb97a68974/common
- console: https://github.com/web-platform-tests/wpt/tree/3b1f72e99a/console
- device-memory: https://github.com/web-platform-tests/wpt/tree/c0cdd63f19/device-memory
- dom/abort: https://github.com/web-platform-tests/wpt/tree/1728d198c9/dom/abort
- encoding: https://github.com/web-platform-tests/wpt/tree/35f70910d3/encoding
- FileAPI: https://github.com/web-platform-tests/wpt/tree/3b279420d4/FileAPI
- hr-time: https://github.com/web-platform-tests/wpt/tree/9910784394/hr-time
- html/webappapis/atob: https://github.com/web-platform-tests/wpt/tree/f267e1dca6/html/webappapis/atob
- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing
- html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/5873f2d8f1/html/webappapis/timers
- interfaces: https://github.com/web-platform-tests/wpt/tree/80a4176623/interfaces
- interfaces: https://github.com/web-platform-tests/wpt/tree/fc086c82d5/interfaces
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/17ebc3aea0/performance-timeline
- resources: https://github.com/web-platform-tests/wpt/tree/972ca5b669/resources
- streams: https://github.com/web-platform-tests/wpt/tree/8f60d94439/streams
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/wpt/device-memory/META.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
spec: https://w3c.github.io/device-memory/
suggested_reviewers:
- npm1
8 changes: 8 additions & 0 deletions test/fixtures/wpt/device-memory/device-memory.https.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
test(function() {
assert_equals(typeof navigator.deviceMemory, "number",
"navigator.deviceMemory returns a number");
assert_true(navigator.deviceMemory >= 0,
"navigator.deviceMemory returns a positive value");
assert_true([0.25, 0.5, 1, 2, 4, 8].includes(navigator.deviceMemory),
"navigator.deviceMemory returns a power of 2 between 0.25 and 8");
}, "navigator.deviceMemory is a positive number, a power of 2, between 0.25 and 8");
19 changes: 19 additions & 0 deletions test/fixtures/wpt/device-memory/idlharness.https.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// META: script=/resources/WebIDLParser.js
// META: script=/resources/idlharness.js
// META: timeout=long

// https://w3c.github.io/device-memory/

"use strict";

idl_test(
['device-memory'],
['html'],
async idl_array => {
if (self.GLOBAL.isWorker()) {
idl_array.add_objects({ WorkerNavigator: ['navigator'] });
} else {
idl_array.add_objects({ Navigator: ['navigator'] });
}
}
);
14 changes: 14 additions & 0 deletions test/fixtures/wpt/interfaces/device-memory.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// GENERATED CONTENT - DO NOT EDIT
// Content was automatically extracted by Reffy into webref
// (https://github.com/w3c/webref)
// Source: Device Memory 1 (https://w3c.github.io/device-memory/)

[
SecureContext,
Exposed=(Window,Worker)
] interface mixin NavigatorDeviceMemory {
readonly attribute double deviceMemory;
};

Navigator includes NavigatorDeviceMemory;
WorkerNavigator includes NavigatorDeviceMemory;
6 changes: 5 additions & 1 deletion test/fixtures/wpt/versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
"commit": "3b1f72e99a91d31551edd2147dc7b564eaf25d72",
"path": "console"
},
"device-memory": {
"commit": "c0cdd63f19507a784531ff4ccc1c71456f79b867",
"path": "device-memory"
},
"dom/abort": {
"commit": "1728d198c92834d92f7f399ef35e7823d5bfa0e4",
"path": "dom/abort"
Expand Down Expand Up @@ -36,7 +40,7 @@
"path": "html/webappapis/timers"
},
"interfaces": {
"commit": "80a417662387b6eda904607d78ad246c5d8bf191",
"commit": "fc086c82d5a7e9b01a684a23336d6d1f9e79e303",
"path": "interfaces"
},
"performance-timeline": {
Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-global.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ builtinModules.forEach((moduleName) => {
'clearImmediate',
'clearInterval',
'clearTimeout',
'navigator',
'performance',
'setImmediate',
'setInterval',
Expand Down
7 changes: 7 additions & 0 deletions test/parallel/test-navigator-global.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';
/* eslint-disable no-global-assign */
require('../common');
const { notStrictEqual } = require('assert');

performance = undefined;
notStrictEqual(globalThis.performance, undefined);
21 changes: 21 additions & 0 deletions test/parallel/test-navigator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';
require('../common');
const assert = require('assert');
const { cpus } = require('os');
const { platform } = require('process');

assert.ok(navigator.deviceMemory >= 0.25 && navigator.deviceMemory <= 8);

assert.strictEqual(navigator.platform, {
aix: 'AIX',
android: 'Android',
darwin: 'Darwin',
freebsd: 'FreeBSD',
linux: 'Linux',
openbsd: 'OpenBSD',
sunos: 'SunOS',
win32: 'Win32',
}[platform]);

assert.ok(navigator.hardwareConcurrency >= 1);
assert.strictEqual(navigator.hardwareConcurrency, cpus().length);
5 changes: 5 additions & 0 deletions test/wpt/status/device-memory.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"idlharness.https.any.js": {
"skip": "idlharness cannot recognize Node.js environment"
}
}
7 changes: 7 additions & 0 deletions test/wpt/test-device-memory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';
require('../common');
const { WPTRunner } = require('../common/wpt');

const runner = new WPTRunner('device-memory');

runner.runJsTests();