-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathindex.src.html
957 lines (854 loc) · 36 KB
/
index.src.html
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
<pre class="metadata">
Title: Measure Memory API
Status: CG-DRAFT
Group: WICG
Repository: WICG/performance-measure-memory
Shortname: performance-measure-memory
Markup Shorthands: markdown yes, css no
Level: None
URL: https://wicg.github.io/performance-measure-memory/
Editor: Ulan Degenbaev, Google https://www.google.com/, ulan@google.com
Abstract:
This specification defines an API that allows web applications to measure their memory usage.
Indent: 2
Default Biblio Status: current
Boilerplate: omit conformance, omit feedback-header
Complain About: accidental-2119 yes, missing-example-ids yes
!Participate: <a href="https://github.com/WICG/performance-measure-memory">GitHub WICG/performance-measure-memory</a> (<a href="https://github.com/WICG/performance-measure-memory/issues/new">new issue</a>, <a href="https://github.com/WICG/performance-measure-memory/issues?state=open">open issues</a>)
</pre>
<pre class="anchors">
urlPrefix: https://tc39.es/ecma262/; spec: ECMAScript
type: dfn
text: Realm; url: sec-code-realms
text: JavaScript realm; url: sec-code-realms
text: JavaScript realms; url: sec-code-realms
text: agent cluster; url: sec-agent-clusters
text: agent cluster; for: agent; url: sec-agent-clusters
text: agent; for: Realm; url: sec-agents
text: the current Realm; url: sec-agent
urlPrefix: https://w3c.github.io/hr-time/; spec: HR-TIME-2
type: interface
text: Performance; url: #sec-performance
urlPrefix: https://html.spec.whatwg.org/
type: dfn
text: agent cluster map; for: browsing context group; url: multipage/browsers.html#agent-cluster-map
type: element-attr
text: id; for: element; url: multipage/dom.html#the-id-attribute
text: src; for: frame; url: multipage/obsolete.html#dom-frame-src
type: abstract-op
text: run a worker; url: multipage/workers.html#run-a-worker
text: set up a window environment settings object; url: multipage/browsing-the-web.html#set-up-a-window-environment-settings-object
text: create and initialize a Document object; url: multipage/browsing-the-web.html#initialise-the-document-object
text: create a new browsing context; url: multipage/browsers.html#creating-a-new-browsing-context
urlPrefix: https://github.com/heycam/webidl
type: extended-attribute
text: CrossOriginIsolated; url: pull/883
</pre>
<pre class="link-defaults">
spec:html; type:dfn; text: browsing context group
spec:infra; type:dfn; for:/; text:set
spec:infra; type:dfn; for:/; text:list
spec:infra; type:dfn; for:list; text:for each
spec:html; type:dfn; for:Realm; text:settings object
spec:webidl; type:dfn; text:resolve
spec:webidl; type:dfn; text:identifier
spec:dom; type:dfn; for:Element; text:local name
</pre>
<style>
/* domintro from https://resources.whatwg.org/standard.css */
.domintro {
position: relative;
color: green;
background: #DDFFDD;
margin: 2.5em 0 2em 0;
padding: 1.5em 1em 0.5em 2em;
}
.domintro dt, .domintro dt * {
color: black;
font-size: inherit;
}
.domintro dd {
margin: 0.5em 0 1em 2em; padding: 0;
}
.domintro dd p {
margin: 0.5em 0;
}
.domintro::before {
content: 'For web developers (non-normative)';
background: green;
color: white;
padding: 0.15em 0.25em;
font-style: normal;
position: absolute;
top: -0.8em;
left: -0.8em;
}
</style>
Introduction {#intro}
=====================
<div nonnormative>
The tradeoff between memory and performance is inherent in many algorithms and data-structures.
Web developers today have multiple ways to measure the timing information and no standard way to measure the memory usage.
This specification defines a {{Performance/measureUserAgentSpecificMemory()|performance.measureUserAgentSpecificMemory()}} API that estimates the memory usage of the web application including all its iframes and workers.
The new API is intended for aggregating memory usage data from production. The main use cases are:
- Regression detection during rollout of a new version of the web application to catch new memory leaks.
- A/B testing a new feature to evaluate its memory impact.
- Correlating memory usage with user metrics to understand the overall impact of memory usage. </div>
Examples {#examples}
--------------------
<div class="example" id="example-simple-page">
A {{Performance/measureUserAgentSpecificMemory()|performance.measureUserAgentSpecificMemory()}} call returns a {{Promise}} and starts
an asynchronous measurement of the memory allocated by the page.
```javascript
async function run() {
const result = await performance.measureUserAgentSpecificMemory();
console.log(result);
}
run();
```
For a simple page without iframes and workers the result might look as follows:
```javascript
{
bytes: 1000000,
breakdown: [
{
bytes: 1000000,
attribution: [
{
url: "https://example.com",
scope: "Window",
},
],
types: ["JS", "DOM"],
},
{
bytes: 0,
attribution: [],
types: [],
},
],
}
```
Here all memory is attributed to the main page.
The entry with `bytes: 0` is present in the breakdown list to encourage processing of the result in a generic way without hardcoding specific entries.
Such an entry is inserted at a random position if the list is not empty.
Other possible valid results:
```javascript
{
bytes: 1000000,
breakdown: [],
}
```
Here the implementation provides only the total memory usage.
```javascript
{
bytes: 1000000,
breakdown: [
{
bytes: 0,
attribution: [],
types: [],
},
{
bytes: 1000000,
attribution: [
{
url: "https://example.com",
scope: "Window",
},
],
types: [],
},
],
}
```
Here the implementation does not break memory down by memory types.
</div>
<div class="example" id="example-same-origin-iframe">
For a page that embeds a same-origin iframe the result might attribute some memory
to that iframe and provide diagnostic information for identifying the iframe:
```html
<html>
<body>
<iframe id="example-id" src="redirect.html?target=iframe.html"></iframe>
</body>
</html>
```
```javascript
{
bytes: 1500000,
breakdown: [
{
bytes: 1000000,
attribution: [
{
url: "https://example.com",
scope: "Window",
},
],
types: ["DOM", "JS"],
},
{
bytes: 0,
attribution: [],
types: [],
},
{
bytes: 500000,
attribution: [
{
url: "https://example.com/iframe.html"
container: {
id: "example-id",
src: "redirect.html?target=iframe.html",
},
scope: "Window",
}
],
types: ["JS", "DOM"],
},
],
}
```
Note how the `url` and `container.src` fields differ for the iframe.
The former reflects the current `location.href` of the iframe whereas the
latter is the value of the `src` attribute of the iframe element.
It is not always possible to separate iframe memory from page memory in a
meaningful way.
An implementation is allowed to lump together some or all of iframe and page memory:
```javascript
{
bytes: 1500000,
breakdown: [
{
bytes: 1500000,
attribution: [
{
url: "https://example.com",
scope: "Window",
},
{
url: "https://example.com/iframe.html",
container: {
id: "example-id",
src: "redirect.html?target=iframe.html",
},
scope: "Window",
},
],
types: ["JS", "DOM"],
},
{
bytes: 0,
attribution: [],
types: [],
},
],
};
```
</div>
<div class="example" id="example-web-worker">
For a page that spawns a web worker the result includes the URL of the worker.
```javascript
{
bytes: 1800000,
breakdown: [
{
bytes: 0,
attribution: [],
types: [],
},
{
bytes: 1000000,
attribution: [
{
url: "https://example.com",
scope: "Window",
},
],
types: ["JS", "DOM"],
},
{
bytes: 800000,
attribution: [
{
url: "https://example.com/worker.js",
scope: "DedicatedWorkerGlobalScope",
},
],
types: ["JS"],
},
],
};
```
An implementation might lump together worker and page memory. If a worker is
spawned by an iframe, then the worker's attribution entry has a `container` field
corresponding to the iframe element.
Memory of shared and service workers is not included in the result.
</div>
<div class="example" id="example-shared-worker">
To get the memory usage of a shared/service worker, the {{Performance/measureUserAgentSpecificMemory()|performance.measureUserAgentSpecificMemory()}} function needs to be invoked in the context of that worker.
The result could be something like:
```javascript
{
bytes: 1000000,
breakdown: [
{
bytes: 1000000,
attribution: [
{
url: "https://example.com/service-worker.js",
scope: "ServiceWorkerGlobalScope",
},
],
types: ["JS"],
},
{
bytes: 0,
attribution: [],
types: [],
},
],
}
```
</div>
<div class="example" id="example-cross-origin-iframe">
If a page embeds a cross-origin iframe, then the URL of that iframe is not
revealed to avoid information leaks. Only the container element
(which is already known to the page) appears in the result.
Additionally, if the cross-origin iframe embeds other cross-origin iframes
and/or spawns workers, then all their memory is aggregated and attributed to
the top-most cross-origin iframe.
Consider a page with the following structure:
```javascript
example.com (1000000 bytes)
|
*--foo.com/iframe1 (500000 bytes)
|
*--foo.com/iframe2 (200000 bytes)
|
*--bar.com/iframe2 (300000 bytes)
|
*--foo.com/worker.js (400000 bytes)
```
A cross-origin iframe embeds to other iframes and spawns a worker.
All memory of these resources is attributed to the first iframe.
```html
<html>
<body>
<iframe id="example-id" src="https://foo.com/iframe1"></iframe>
</body>
</html>
```
```javascript
{
bytes: 2400000,
breakdown: [
{
bytes: 1000000,
attribution: [
{
url: "https://example.com",
scope: "Window",
},
],
types: ["JS", "DOM"],
},
{
bytes: 0,
attribution: [],
types: [],
},
{
bytes: 1400000,
attribution: [
{
url: "cross-origin-url",
container: {
id: "example-id",
src: "https://foo.com/iframe1",
},
scope: "cross-origin-aggregated",
},
],
types: ["DOM", "JS"],
},
],
}
```
Note that the `url` and `scope` fields of the cross-origin iframe entry have
special values indicating that information is not available.
If the implementation loads cross-origin iframes in a different address space,
then their memory usage is not measured.
The breakdown entries of such iframes have `bytes: 0` indicating that these
iframes are not accounted for:
```javascript
{
bytes: 100000,
breakdown: [
{
bytes: 1000000,
attribution: [
{
url: "https://example.com",
scope: "Window",
},
],
types: ["JS", "DOM"],
},
{
bytes: 0,
attribution: [
{
url: "cross-origin-url",
container: {
id: "example-id",
src: "https://foo.com/iframe1",
},
scope: "cross-origin-aggregated",
},
],
types: ["JS", "DOM"],
},
{
bytes: 0,
attribution: [],
types: [],
},
],
}
```
</div>
<div class="example" id="example-same-and-cross-origin-iframes">
If a cross-origin iframe embeds an iframe of the same origin as the main page,
then the same-origin iframe is revealed in the result.
Note that there is no information leak because the main page can find and read
`location.href` of the same-origin iframe.
```javascript
example.com (1000000 bytes)
|
*--foo.com/iframe1 (500000 bytes)
|
*--example.com/iframe2 (200000 bytes)
```
```html
<html>
<body>
<iframe id="example-id" src="https://foo.com/iframe1"></iframe>
</body>
</html>
```
```javascript
{
bytes: 1700000,
breakdown: [
{
bytes: 1000000,
attribution: [
{
url: "https://example.com",
scope: "Window",
},
],
types: ["DOM", "JS"],
},
{
bytes: 0,
attribution: [],
types: [],
},
{
bytes: 500000,
attribution: [
{
url: "cross-origin-url",
container: {
id: "example-id",
src: "https://foo.com/iframe1",
},
scope: "cross-origin-aggregated",
},
],
types: ["DOM", "JS"],
},
{
bytes: 200000,
attribution: [
{
url: "https://example.com/iframe2",
container: {
id: "example-id",
src: "https://foo.com/iframe1",
},
scope: "Window",
},
],
types: ["JS", "DOM"],
},
],
}
```
If the implementation omits memory measurement of cross-origin iframes,
then the result could look like:
```javascript
{
bytes: 1200000,
breakdown: [
{
bytes: 1000000,
attribution: [
{
url: "https://example.com",
scope: "Window",
},
],
types: ["JS", "DOM"],
},
{
bytes: 0,
attribution: [
{
url: "cross-origin-url",
container: {
id: "example-id",
src: "https://foo.com/iframe1",
},
scope: "cross-origin-aggregated",
},
],
types: ["JS", "DOM"],
},
{
bytes: 200000,
attribution: [
{
url: "https://example.com/iframe2",
container: {
id: "example-id",
src: "https://foo.com/iframe1",
},
scope: "Window",
},
],
types: ["JS", "DOM"],
},
{
bytes: 0,
attribution: [],
types: [],
},
],
}
```
</div>
Data model {#data-model}
========================
Memory measurement result {#memory-measurement-result}
------------------------------------------------------
The {{Performance/measureUserAgentSpecificMemory()|performance.measureUserAgentSpecificMemory()}} function returns a {{Promise}} that resolves to an instance of {{MemoryMeasurement}} dictionary:
<pre class="idl">
dictionary MemoryMeasurement {
unsigned long long bytes;
sequence<MemoryBreakdownEntry> breakdown;
};
</pre>
<dl class="domintro">
: <code>measurement . {{MemoryMeasurement/bytes}}</code>
:: A number that represents the total memory usage.
: <code>measurement . {{MemoryMeasurement/breakdown}}</code>
:: An array that partitions the total {{MemoryMeasurement/bytes}} and provides attribution and type information. </dl>
<pre class="idl">
dictionary MemoryBreakdownEntry {
unsigned long long bytes;
sequence<MemoryAttribution> attribution;
sequence<DOMString> types;
};
</pre>
<dl class="domintro">
: <code>breakdown . {{MemoryBreakdownEntry/bytes}}</code>
:: The size of the memory that this entry describes.
: <code>breakdown . {{MemoryBreakdownEntry/attribution}}</code>
:: An array of URLs and/or container elements of the [=JavaScript realms=] that use the memory.
: <code>breakdown . {{MemoryBreakdownEntry/types}}</code>
:: An array of [=implementation-defined=] memory types associated with the memory. </dl>
<pre class="idl">
dictionary MemoryAttribution {
USVString url;
MemoryAttributionContainer container;
DOMString scope;
};
</pre>
<dl class="domintro">
: <code>attribution . {{MemoryAttribution/url}}</code>
:: If this attribution corresponds to a same-origin [=JavaScript realm=], then this field contains realm's URL.
Otherwise, the attribution is for one or more cross-origin [=JavaScript realms=] and this field contains
a sentinel value: `"cross-origin-url"`.
: <code>attribution . {{MemoryAttribution/container}}</code>
:: Describes the DOM element that (maybe indirectly) contains the [=JavaScript realms=].
This property might be absent if the attribution is for the same-origin top-level realm.
Note that cross-origin realms cannot be top-level due to [=environment settings object/cross-origin isolated capability|cross-origin isolation=].
: <code>attribution . {{MemoryAttribution/scope}}</code>
:: Describes the type of the same-origin [=JavaScript realm=]:
<code>"Window", "DedicatedWorkerGlobalScope", "SharedWorkerGlobalScope", "ServiceWorkerGlobalScope"</code>
or <code>"cross-origin-aggregated"</code> for the cross-origin case. </dl>
<pre class="idl">
dictionary MemoryAttributionContainer {
DOMString id;
USVString src;
};
</pre>
<dl class="domintro">
: <code>container . {{MemoryAttributionContainer/id}}</code>
:: The `id` attribute of the container element.
: <code>container . {{MemoryAttributionContainer/src}}</code>
:: The `src` attribute of the container element. If the container element is an <{object}> element,
then this field contains the value of the <{object/data}> attribute.</dl>
Intermediate memory measurement {#intermediate-memory-measurement-section}
--------------------------------------------------------------------------
This specification assumes the existence of an [=implementation-defined=] algorithm that measures
the memory usage of the given set of [=agent clusters=] in the address space of the given current [=agent cluster=].
The result of such an algorithm is an <dfn>intermediate memory measurement</dfn>,
which is a [=set=] of [=intermediate memory breakdown entries=].
To preserve security guarantees of address space isolation, any intermediate memory breakdown entries that represent memory outside the current address space must have their bytes set to 0.
To reduce fingerprinting risks the result must include only the memory related to the web platform objects allocated or used by the given set of agent clusters.
This, for example, excludes the memory of user-agent-specific extensions and the baseline memory of an empty page.
The memory must be accounted at the address space level to exclude any platform-specific memory optimizations such memory compression and lazy memory committing.
An <dfn>intermediate memory breakdown entry</dfn> is a [=struct=] containing the following [=struct/items=]:
: <dfn for="intermediate memory breakdown entry">bytes</dfn>
:: The size of the memory that this [=intermediate memory breakdown entry=] describes, or 0 if this entry represents memory outside the current address space.
: <dfn for="intermediate memory breakdown entry">realms</dfn>
:: A [=set=] of [=JavaScript realms=] to which the memory is attributed to.
: <dfn for="intermediate memory breakdown entry">types</dfn>
:: A [=set=] of [=/strings=] specifying [=implementation-defined=] memory types associated with the memory.
Algorithms defined in this specification show how to convert an [=intermediate memory measurement=]
to an instance of {{MemoryMeasurement}}.
Memory attribution token {#memory-attribution-token-section}
------------------------------------------------------------
The link between an embedded [=JavaScript realm=] and its container element is ephemeral and is not guaranteed to always exist.
For example, navigation to another document in the container element or removal of the container element from the DOM tree severs the link.
A <dfn>memory attribution token</dfn> provides a way to get from a [=JavaScript realm=] to its container element.
It is a [=struct=] containing the following [=struct/items=]:
: <dfn for="memory attribution token">container</dfn>
:: An instance of {{MemoryAttributionContainer}}.
: <dfn for="memory attribution token">cross-origin aggregated flag</dfn>
:: A [=boolean=] flag indicating whether the token was created for aggregating the memory usage of
cross-origin [=JavaScript realms=].
It is stored in a new internal field of {{WindowOrWorkerGlobalScope}} at construction time and is always available for memory reporting.
Processing model {#processing-model}
====================================
Extensions to the {{Performance}} interface {#extensions-to-performance}
------------------------------------------------------------------------
<pre class="idl">
partial interface Performance {
[Exposed=(Window,ServiceWorker,SharedWorker), CrossOriginIsolated] Promise<MemoryMeasurement> measureUserAgentSpecificMemory();
};
</pre>
<dl class="domintro">
: <code>performance . {{Performance/measureUserAgentSpecificMemory()}}</code>
:: A method that performs an asynchronous memory measurement. Details about the result of the method are in [[#memory-measurement-result]]. </dl>
Top-level algorithms {#top-level-algorithms}
--------------------------------------------
<div algorithm>
The <dfn method for="Performance">measureUserAgentSpecificMemory()</dfn> method steps are:
1. [=Assert=]: [=the current Realm=]'s [=Realm/settings objects=]'s [=environment settings object/cross-origin isolated capability=] is true.
1. If [=memory measurement allowed predicate=] given [=the current Realm=] is false, then:
1. Return [=a promise rejected with=] a "{{SecurityError}}" {{DOMException}}.
1. Let |the current agent cluster| be [=the current Realm=]'s [=Realm/agent=]'s [=agent/agent cluster=].
1. Let |agent clusters| be the result of [=getting all agent clusters=] given [=the current Realm=].
1. Let |promise| be a new {{Promise}}.
1. Start asynchronous [=implementation-defined memory measurement=] given |the current agent cluster|, |agent clusters|, and |promise|.
1. Return |promise|.
</div>
<div algorithm>
To evaluate <dfn>memory measurement allowed predicate</dfn>
given a [=JavaScript realm=] |realm|:
1. Let |global object| be |realm|'s [=Realm/global object=].
1. If |global object| is a {{SharedWorkerGlobalScope}}, then return true.
1. If |global object| is a {{ServiceWorkerGlobalScope}}, then return true.
1. If |global object| is a {{Window}} then
1. Let |settings object| be |realm|'s [=Realm/settings object=].
1. If |settings object|'s [=environment settings object/origin=] is the same as |settings object|'s [=environment/top-level origin=], then return true.
1. Return false.
</div>
<div algorithm>
To <dfn lt="getting all agent clusters|get all agent clusters">get all agent clusters</dfn>
given an [=JavaScript realm=] |realm|:
1. If |realm|'s [=Realm/global object=] is a {{Window}}, then:
1. Let |group| be the [=browsing context group=] that contains |realm|'s [=Realm/global object=]'s [=Window/browsing context=].
1. Return the result of [=map/getting the values=] of |group|'s [=browsing context group/agent cluster map=].
1. Return « |realm|'s [=Realm/agent=]'s [=agent/agent cluster=] ».
</div>
<div algorithm>
To perform <dfn>implementation-defined memory measurement</dfn> given given an [=agent cluster=] |the current agent cluster|, a [=set=] of [=agent clusters=] |agent clusters|, and
a {{Promise}} |promise| run these steps [=in parallel=]:
1. Let |intermediate memory measurement| be [=implementation-defined=] [=intermediate memory measurement=] performed for |the current agent cluster| and |agent clusters|.
1. [=Queue a global task=] on the TODO task source given |promise|'s [=relevant global object=] to [=resolve=] |promise| with the result of [=creating a new memory measurement=] given |intermediate memory measurement|.
</div>
Converting an intermediate memory measurement to the result {#converting-an-intermediate-memory-measurement-to-the-result}
--------------------------------------------------------------------------------------------------------------------------
<div algorithm>
To <dfn lt="creating a new memory measurement|create a new memory measurement">create a new memory measurement</dfn>
given an [=intermediate memory measurement=] |intermediate measurement|:
1. Let |bytes| be 0.
1. [=For each=] [=intermediate memory breakdown entry=] |intermediate entry| in |intermediate measurement|:
1. Set |bytes| to |bytes| plus |intermediate entry|'s [=intermediate memory breakdown entry/bytes=].
1. Let |breakdown| be a new [=list=].
1. [=list/Append=] to |breakdown| a new {{MemoryBreakdownEntry}} whose:
- {{MemoryBreakdownEntry/bytes}} is 0,
- {{MemoryBreakdownEntry/attribution}} is « »,
- {{MemoryBreakdownEntry/types}} is « ».
1. [=set/For each=] [=intermediate memory breakdown entry=] |intermediate entry| in |intermediate measurement|:
1. Let |breakdown entry| be the result of [=creating a new memory breakdown entry=] given |intermediate entry|.
1. [=list/Append=] |breakdown entry| to |breakdown|.
1. Randomize the order of the items in |breakdown|.
1. Return a new {{MemoryMeasurement}} whose:
- {{MemoryMeasurement/bytes}} is |bytes|,
- {{MemoryMeasurement/breakdown}} is |breakdown|.
</div>
<div algorithm>
To <dfn lt="creating a new memory breakdown entry|create a new memory breakdown entry">create a new memory breakdown entry</dfn>
given an [=intermediate memory breakdown entry=] |intermediate entry|:
1. Let |attribution| a new [=list=].
1. [=set/For each=] [=JavaScript realm=] |realm| in |intermediate entry|'s [=intermediate memory breakdown entry/realms=]:
1. Let |attribution entry| be the result of [=creating a new memory attribution=] given |realm|.
1. [=list/Append=] |attribution entry| to |attribution|.
1. Let |types| be |intermediate entry|'s [=intermediate memory breakdown entry/types=].
1. Randomize the order of the items in |types|.
1. Return a new {{MemoryBreakdownEntry}} whose:
- {{MemoryBreakdownEntry/bytes}} is |intermediate entry|'s [=intermediate memory breakdown entry/bytes=],
- {{MemoryBreakdownEntry/attribution}} is |attribution|,
- {{MemoryBreakdownEntry/types}} is |types|.
</div>
<div algorithm>
To <dfn lt="creating a new memory attribution|create a new memory attribution">create a new memory attribution</dfn>
given a [=JavaScript realm=] |realm|:
1. Let |token| be |realm|'s [=Realm/global object=]'s [=WindowOrWorkerGlobalScope/memory attribution token=].
1. If |token|'s [=memory attribution token/cross-origin aggregated flag=] is true, then
1. Return a new {{MemoryAttribution}} whose:
- {{MemoryAttribution/url}} is "`cross-origin-url`",
- {{MemoryAttribution/container}} is |token|'s [=memory attribution token/container=] if it is not null; otherwise, the {{MemoryAttribution/container}} entry is omitted,
- {{MemoryAttribution/scope}} is "`cross-origin-aggregated`".
1. Let |scope name| be [=identifier=] of |realm|'s [=Realm/global object=]'s [=interface=].
1. Return a new {{MemoryAttribution}} whose:
- {{MemoryAttribution/url}} is |realm|'s [=Realm/settings object=]'s [=environment/creation URL=],
- {{MemoryAttribution/container}} is |token|'s [=memory attribution token/container=] if it is not null, otherwise, the {{MemoryAttribution/container}} entry is omitted,
- {{MemoryAttribution/scope}} is |scope name|.
</div>
Creating or obtaining a memory attribution token {#creating-or-obtaining-a-memory-attribution-token}
----------------------------------------------------------------------------------------------------
<div algorithm>
To <dfn lt="obtain a window memory attribution token|obtaining a window memory attribution token">obtain a window memory attribution token</dfn>
given an [=/origin=] |origin|, an [=/origin=] |parent origin|, an [=/origin=] |top-level origin|,
an {{HTMLElement}} |container element|, and a [=/memory attribution token=] |parent token|:
1. If |container element| is null, then:
1. [=Assert=]: |parent origin| is null.
1. [=Assert=]: |parent token| is null.
1. [=Assert=]: |origin| is equal to |parent origin|
1. Return a new [=/memory attribution token=] whose:
- [=memory attribution token/container=] is null,
- [=memory attribution token/cross-origin aggregated flag=] is false.
1. If |parent origin| is not equal to |top-level origin|, then:
1. Return |parent token|.
1. Let |container| be the result of [=extracting container element attributes=] given |container element|.
1. If |origin| is equal to |top-level origin|, then:
1. Return a new [=/memory attribution token=] whose:
- [=memory attribution token/container=] is |container|,
- [=memory attribution token/cross-origin aggregated flag=] is false.
1. Return a new [=/memory attribution token=] whose:
- [=memory attribution token/container=] is |container|,
- [=memory attribution token/cross-origin aggregated flag=] is true.
</div>
<div algorithm>
To <dfn lt="obtain a worker memory attribution token|obtaining a worker memory attribution token">obtain a worker memory attribution token</dfn>
given {{WorkerGlobalScope}} |worker global scope|, an [=environment settings object=] |outside settings|:
1. If |worker global scope| is a {{DedicatedWorkerGlobalScope}}, then
return |outside settings|'s [=environment settings object/global object=]'s [=WindowOrWorkerGlobalScope/memory attribution token=].
1. Assert: |worker global scope| is a {{SharedWorkerGlobalScope}} or a {{ServiceWorkerGlobalScope}}.
1. Return a new [=/memory attribution token=] whose:
- [=memory attribution token/container=] is null,
- [=memory attribution token/cross-origin aggregated flag=] is false.
</div>
<div algorithm>
To <dfn lt="extract container element attributes|extracting container element attributes">extract container element attributes</dfn>
given an {{HTMLElement}} |container element|:
1. Switch on |container element|'s [=local name=]:
<dl class="switch">
: "iframe"
:: Return a new {{MemoryAttributionContainer}} whose:
- {{MemoryAttributionContainer/id}} is |container element|'s <{element/id}> attribute,
- {{MemoryAttributionContainer/src}} is |container element|'s <{iframe/src}> attribute,
: "frame"
:: Return a new {{MemoryAttributionContainer}} whose:
- {{MemoryAttributionContainer/id}} is |container element|'s <{element/id}> attribute,
- {{MemoryAttributionContainer/src}} is |container element|'s <{frame/src}> attribute,
: "object"
:: Return a new {{MemoryAttributionContainer}} whose:
- {{MemoryAttributionContainer/id}} is |container element|'s <{element/id}> attribute,
- {{MemoryAttributionContainer/src}} is |container element|'s <{object/data}> attribute,
</dl>
</div>
Integration with the existing specification {#integration-with-the-existing-specification}
==========================================================================================
Extension to {{WindowOrWorkerGlobalScope}} {#extension-to-window-or-worker-global-scope}
----------------------------------------------------------------------------------------
A new internal field is added to {{WindowOrWorkerGlobalScope}}:
: A <dfn for="WindowOrWorkerGlobalScope">memory attribution token</dfn>
:: An [=/memory attribution token=] that is used for reporting the memory usage of this environment.
Extensions to the existing algorithms {#extensions-to-the-existing-algorithms}
------------------------------------------------------------------------------
The [$run a worker$] algorithm sets
the [=WindowOrWorkerGlobalScope/memory attribution token=] field of the newly created global object in step 6:
> 6. Let realm execution context be the result of creating a new JavaScript realm given agent
> and the following customizations:
> - ...
> - Set global object's [=WindowOrWorkerGlobalScope/memory attribution token=]
> to the result of [=obtaining a worker memory attribution token=] given
> the global object and <var ignore>outside settings</var>.
The [$create and initialize a Document object$] algorithm sets the [=WindowOrWorkerGlobalScope/memory attribution token=]
field of the newly created global object:
> 7. Otherwise:
> 5. Let |token| be an empty [=/memory attribution token=].
> 6. If |browsingContext| is not a top-level browsing context, then:
> 4. Let |parentToken| be |parentEnvironment|'s [=environment settings object/global object=]'s [=WindowOrWorkerGlobalScope/memory attribution token=].
> 5. Set |token| to the result of [=obtaining a window memory attribution token=] with
> |origin|, |parentEnvironment|'s origin, |topLevelOrigin|, |browsingContext|'s container, |parentToken|.
> 7. Else, set |token| to the result of [=obtaining a window memory attribution token=] with
> |origin|, null |topLevelOrigin|, null, null.
> 8. Let |window global scope| be the [=Realm/global object=] of |realm execution context|'s Realm component.
> 9. Set |window global scope|'s [=WindowOrWorkerGlobalScope/memory attribution token=] to |token|.
The [$create a new browsing context$] algorithm sets the [=WindowOrWorkerGlobalScope/memory attribution token=] field of the newly created global object:
> 11. Let |token| be an empty token.
> 12. If |embedder| is null, then set |token| to the result of [=obtaining a window memory attribution token=] with |origin|,
> null, |topLevelOrigin|, null, null.
> 13. Else, set |token| to the result of [=obtaining a window memory attribution token=]
> with |origin|, |embedder|'s [=relevant settings object=]'s origin, |topLevelOrigin|, |embedder|,
> |embedder|'s [=relevant global object=]'s [=WindowOrWorkerGlobalScope/memory attribution token=].
> 14. Let |window global scope| be the [=Realm/global object=] of |realm execution context|'s Realm component.
> 15. Set |window global scope|'s [=WindowOrWorkerGlobalScope/memory attribution token=] to |token|.
Privacy and Security {#privacy-and-security}
============================================
<div nonnormative>
Cross-origin information leak {#information-leak}
-------------------------------------------------
The URLs and other string values that appear in the result are guaranteed to be known to the origin that invokes the API.
The only information that is exposed cross-origin is the size information provided in {{MemoryMeasurement/bytes|memoryMeasurement.bytes}} and {{MemoryBreakdownEntry/bytes|memoryBreakdownEntry.bytes}}.
The API relies on the [=environment settings object/cross-origin isolated capability|cross-origin isolation=] mechanism to mitigate cross-origin size information leaks.
Specifically, the API relies on the invariant that all resources in the current address space have opted in to be embeddable and legible by their embedding origin.
The API does not expose the sizes of cross-origin resources loaded in a different address space.
Fingerprinting {#fingerprinting}
--------------------------------
The result of the API depends only on the objects allocated by the web page itself and does not include unrelated memory such as the baseline memory usage of an empty web page.
This means the same user agent binary running on two different devices should produce the same results for a fixed web page.
A web page can infer the following information about the user agent:
- the bitness of the user agent (32-bit vs 64-bit).
- the version of the user agent to some extent.
Similar information can be obtained from the existing APIs ({{NavigatorID/userAgent|navigator.userAgent}}, {{NavigatorID/platform|navigator.platform}}).
The bitness of the user agent can also be inferred by measuring the runtime of 32-bit and 64-bit operations.
Currently the API is available only to the top-level origin.
In the future the top-level origin will be able to delegate the API to other origins using Permissions Policy.
In both cases, cross-origin iframes do not get access to the API by default.
</div>
Acknowledgements {#acknowledgements}
====================================
Thanks to Domenic Denicola and Shu-yu Guo for contributing to the API design and for reviewing this specification.
Also thanks to Adam Giacobbe, Anne van Kesteren, Artur Janc, Boris Zbarsky, Chris Hamilton, Chris Palmer, Daniel Vogelheim, Dominik Inführ, Hannes Payer, Joe Mason, Kentaro Hara, L. David Baron, Mathias Bynens, Matthew Bolohan, Michael Lippautz, Mike West, Neil Mckay, Olga Belomestnykh, Per Parker, Philipp Weis, and Yoav Weiss for their feedback and contributions.