Commit 5775ff8c authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

Fix handling of remote frames and URLs in performance.measureMemory

The existing implementation measures memory usage of the main JS agent
and reports URLs of the JS realms in the agent. The algorithm for
mapping a JS realm to its reported URL walks the frame tree upwards to
find the top-most cross-origin frame. The algorithm incorrectly assumed
that all frames the path are local frames since the realms are local.

This does not hold in the ABA case, where the main origin A embeds
an iframe from origin B that in turn embeds an iframe from origin A.
In such a case, the main JS realm and the grandchild realm are in the
same JS agent and their frames are local. However, the child frame B
is a remote frame.

This CL fixes the algorithm to work both with local and remote frames.
The URL of a remote frame can no longer be fetched from its document.
Instead, it is fetched from the src attribute of the owner iframe
element. This aligns with the upcoming spec and fixes the leak of
post-server-redirect URLs.

The CL also removes LocalFrame::FirstUrlCrossOriginToParent that is
no longer needed.

Bug: 1093880,1084999

Change-Id: I7a57a17701448d0fe210a66c7bdb8c0229fa5149
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2246175
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#780319}
parent be4f97ce
...@@ -639,13 +639,6 @@ void LocalFrame::DidAttachDocument() { ...@@ -639,13 +639,6 @@ void LocalFrame::DidAttachDocument() {
// even after the frame reattaches. // even after the frame reattaches.
GetEventHandler().Clear(); GetEventHandler().Clear();
Selection().DidAttachDocument(document); Selection().DidAttachDocument(document);
if (IsCrossOriginToParentFrame() && !first_url_cross_origin_to_parent_) {
first_url_cross_origin_to_parent_ = GetDocument()->Url().GetString();
}
}
base::Optional<String> LocalFrame::FirstUrlCrossOriginToParent() const {
return first_url_cross_origin_to_parent_;
} }
bool LocalFrame::CanAccessEvent( bool LocalFrame::CanAccessEvent(
......
...@@ -609,10 +609,6 @@ class CORE_EXPORT LocalFrame final : public Frame, ...@@ -609,10 +609,6 @@ class CORE_EXPORT LocalFrame final : public Frame,
// Indicate that this frame was attached as a MainFrame. // Indicate that this frame was attached as a MainFrame.
void WasAttachedAsLocalMainFrame(); void WasAttachedAsLocalMainFrame();
// Returns the first URL loaded in this frame that is cross-origin to the
// parent frame.
base::Optional<String> FirstUrlCrossOriginToParent() const;
// Return true if the frame is able to access an event with the given // Return true if the frame is able to access an event with the given
// attribution (i.e. the event is targeted for an origin that the frame may // attribution (i.e. the event is targeted for an origin that the frame may
// access). // access).
...@@ -814,14 +810,6 @@ class CORE_EXPORT LocalFrame final : public Frame, ...@@ -814,14 +810,6 @@ class CORE_EXPORT LocalFrame final : public Frame,
Member<SystemClipboard> system_clipboard_; Member<SystemClipboard> system_clipboard_;
// Access to the global raw/unsanitized system clipboard // Access to the global raw/unsanitized system clipboard
Member<RawSystemClipboard> raw_system_clipboard_; Member<RawSystemClipboard> raw_system_clipboard_;
// Stores the first URL that was loaded in this frame that is cross-origin
// to the parent frame. For this URL we know that the parent origin provided
// it by either setting the |src| attribute or by navigating the iframe.
// Thus we can safely reveal it to the parent origin in Web APIs.
// TODO(ulan): Move this to the browser process once performance.measureMemory
// starts using Performance Manager to support cross-site iframes.
base::Optional<String> first_url_cross_origin_to_parent_;
}; };
inline FrameLoader& LocalFrame::Loader() const { inline FrameLoader& LocalFrame::Loader() const {
......
...@@ -3002,10 +3002,17 @@ crbug.com/1050754 external/wpt/mathml/relations/html5-tree/integration-point-4.h ...@@ -3002,10 +3002,17 @@ crbug.com/1050754 external/wpt/mathml/relations/html5-tree/integration-point-4.h
crbug.com/1050754 external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html [ Failure ] crbug.com/1050754 external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html [ Failure ]
crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-001.html [ Failure ] crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-001.html [ Failure ]
crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-002.html [ Failure ] crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-002.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory-cross-origin-iframe.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/detached.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory-cross-origin-redirecting-iframe.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/iframe.cross-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory-same-origin-iframe.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/iframe.cross-site.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/iframe.same-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/main-frame.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/redirect.client.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/redirect.server.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.cross-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.cross-site.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.mix.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.same-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.html [ Failure ] crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.html [ Failure ]
crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.worker.html [ Failure ] crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.worker.html [ Failure ]
crbug.com/1050754 external/wpt/media-capabilities/encodingInfo.html [ Failure ] crbug.com/1050754 external/wpt/media-capabilities/encodingInfo.html [ Failure ]
......
...@@ -2863,10 +2863,17 @@ crbug.com/1050754 external/wpt/mathml/relations/html5-tree/integration-point-4.h ...@@ -2863,10 +2863,17 @@ crbug.com/1050754 external/wpt/mathml/relations/html5-tree/integration-point-4.h
crbug.com/1050754 external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html [ Failure ] crbug.com/1050754 external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html [ Failure ]
crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-001.html [ Failure ] crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-001.html [ Failure ]
crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-002.html [ Failure Timeout ] crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-002.html [ Failure Timeout ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory-cross-origin-iframe.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/detached.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory-cross-origin-redirecting-iframe.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/iframe.cross-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory-same-origin-iframe.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/iframe.cross-site.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/iframe.same-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/main-frame.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/redirect.client.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/redirect.server.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.cross-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.cross-site.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.mix.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.same-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.html [ Failure ] crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.html [ Failure ]
crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.worker.html [ Failure ] crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.worker.html [ Failure ]
crbug.com/1050754 external/wpt/media-capabilities/encodingInfo.html [ Failure ] crbug.com/1050754 external/wpt/media-capabilities/encodingInfo.html [ Failure ]
......
...@@ -3111,10 +3111,17 @@ crbug.com/1050754 external/wpt/mathml/relations/html5-tree/integration-point-4.h ...@@ -3111,10 +3111,17 @@ crbug.com/1050754 external/wpt/mathml/relations/html5-tree/integration-point-4.h
crbug.com/1050754 external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html [ Failure ] crbug.com/1050754 external/wpt/mathml/relations/html5-tree/math-global-event-handlers.tentative.html [ Failure ]
crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-001.html [ Failure ] crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-001.html [ Failure ]
crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-002.html [ Failure ] crbug.com/1050754 external/wpt/mathml/relations/html5-tree/tabindex-002.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory-cross-origin-iframe.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/detached.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory-cross-origin-redirecting-iframe.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/iframe.cross-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory-same-origin-iframe.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/iframe.cross-site.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/measure-memory.tentative.window.html [ Failure ] crbug.com/1050754 external/wpt/measure-memory/iframe.same-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/main-frame.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/redirect.client.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/redirect.server.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.cross-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.cross-site.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.mix.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/measure-memory/window-open.same-origin.tentative.window.html [ Failure ]
crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.html [ Failure ] crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.html [ Failure ]
crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.worker.html [ Failure ] crbug.com/1050754 external/wpt/media-capabilities/decodingInfo.any.worker.html [ Failure ]
crbug.com/1050754 external/wpt/media-capabilities/encodingInfo.html [ Failure ] crbug.com/1050754 external/wpt/media-capabilities/encodingInfo.html [ Failure ]
......
...@@ -172,6 +172,7 @@ SET TIMEOUT: IndexedDB/* ...@@ -172,6 +172,7 @@ SET TIMEOUT: IndexedDB/*
SET TIMEOUT: infrastructure/* SET TIMEOUT: infrastructure/*
SET TIMEOUT: intersection-observer/resources/* SET TIMEOUT: intersection-observer/resources/*
SET TIMEOUT: intersection-observer/target-in-different-window.html SET TIMEOUT: intersection-observer/target-in-different-window.html
SET TIMEOUT: measure-memory/*
SET TIMEOUT: media-source/mediasource-util.js SET TIMEOUT: media-source/mediasource-util.js
SET TIMEOUT: media-source/URL-createObjectURL-revoke.html SET TIMEOUT: media-source/URL-createObjectURL-revoke.html
SET TIMEOUT: mixed-content/generic/sanity-checker.js SET TIMEOUT: mixed-content/generic/sanity-checker.js
......
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
// META: timeout=long
'use strict';
promise_test(async testCase => {
const {iframes, windows} = await build([
{
id: 'cross-site-1',
children: [
{
id: 'same-origin-2',
},
{
id: 'same-origin-11',
window_open: true,
},
],
},
{
id: 'same-origin-3',
children: [
{
id: 'same-origin-4',
},
{
id: 'same-origin-12',
window_open: true,
},
],
},
{
id: 'cross-origin-5',
children: [
{
id: 'same-origin-6',
},
{
id: 'same-origin-13',
window_open: true,
},
],
},
{
id: 'same-origin-7',
window_open: true,
children: [
{
id: 'same-origin-8',
}
],
},
{
id: 'cross-origin-9',
window_open: true,
children: [
{
id: 'same-origin-10',
}
],
},
]);
const allowed = [
window.location.href,
iframes['cross-site-1'].src,
iframes['same-origin-3'].src,
iframes['same-origin-4'].src,
iframes['cross-origin-5'].src,
windows['same-origin-7'].location.href,
windows['same-origin-12'].location.href,
];
const keep = sameOriginContexts(frames).concat(sameOriginContexts(windows));
// Detach iframes:
// 1) By setting src attribute:
iframes['cross-site-1'].src =
iframes['cross-site-1'].src.replace('iframe.sub', 'iframe.secret.sub');
// 2) By setting location attribute:
let url = iframes['same-origin-3'].contentWindow.location.href;
url = url.replace('iframe.sub', 'iframe.secret.sub');
iframes['same-origin-3'].contentWindow.location.href = url;
// 3) By removing from the DOM tree:
iframes['cross-origin-5'].parentNode.removeChild(iframes['cross-origin-5']);
// Detach windows:
// 1) By setting document.location attribute:
url = windows['same-origin-7'].location.href;
url = url.replace('window.sub', 'window.secret.sub');
windows['same-origin-7'].location.href = url;
// 2) By closing the window:
windows['cross-origin-9'].close();
try {
const result = await performance.measureMemory();
checkMeasureMemory(result, {
allowed: allowed.concat([
iframes['cross-site-1'].src,
]),
required: [
window.location.href,
],
});
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
assert_equals(error.name, 'SecurityError');
}
}, 'performance.measureMemory URLs within a cross-site iframe.');
...@@ -4,22 +4,32 @@ ...@@ -4,22 +4,32 @@
'use strict'; 'use strict';
promise_test(async testCase => { promise_test(async testCase => {
const grandchildLoaded = new Promise(resolve => { const {iframes} = await build([
window.onmessage = function(message) { {
if (message.data === 'grandchild-loaded') { id: 'cross-origin-1',
resolve(message); children: [
} {
} id: 'same-origin-2',
}); },
const frame = document.createElement('iframe'); {
const redirecting_child = getUrl(CROSS_ORIGIN, 'resources/redirecting-child.sub.html'); id: 'cross-origin-3',
frame.src = redirecting_child; },
document.body.append(frame); {
await grandchildLoaded; id: 'cross-site-4',
}
],
},
]);
try { try {
let result = await performance.measureMemory(); const result = await performance.measureMemory();
checkMeasureMemory(result, { checkMeasureMemory(result, {
allowed: [window.location.href, redirecting_child] allowed: [
window.location.href,
iframes['cross-origin-1'].src,
],
required: [
window.location.href,
],
}); });
} catch (error) { } catch (error) {
if (!(error instanceof DOMException)) { if (!(error instanceof DOMException)) {
...@@ -27,4 +37,4 @@ promise_test(async testCase => { ...@@ -27,4 +37,4 @@ promise_test(async testCase => {
} }
assert_equals(error.name, 'SecurityError'); assert_equals(error.name, 'SecurityError');
} }
}, 'Well-formed result of performance.measureMemory with cross-origin iframe.'); }, 'performance.measureMemory URLs within a cross-origin iframe.');
...@@ -4,22 +4,32 @@ ...@@ -4,22 +4,32 @@
'use strict'; 'use strict';
promise_test(async testCase => { promise_test(async testCase => {
const grandchildLoaded = new Promise(resolve => { const {iframes} = await build([
window.onmessage = function(message) { {
if (message.data === 'grandchild-loaded') { id: 'cross-site-1',
resolve(message); children: [
} {
} id: 'same-origin-2',
}); },
const frame = document.createElement('iframe'); {
const child = getUrl(CROSS_ORIGIN, 'resources/child.sub.html'); id: 'cross-origin-3',
frame.src = child; },
document.body.append(frame); {
await grandchildLoaded; id: 'cross-site-4',
}
],
},
]);
try { try {
let result = await performance.measureMemory(); const result = await performance.measureMemory();
checkMeasureMemory(result, { checkMeasureMemory(result, {
allowed: [window.location.href, child] allowed: [
window.location.href,
iframes['cross-site-1'].src,
],
required: [
window.location.href,
],
}); });
} catch (error) { } catch (error) {
if (!(error instanceof DOMException)) { if (!(error instanceof DOMException)) {
...@@ -27,4 +37,4 @@ promise_test(async testCase => { ...@@ -27,4 +37,4 @@ promise_test(async testCase => {
} }
assert_equals(error.name, 'SecurityError'); assert_equals(error.name, 'SecurityError');
} }
}, 'Well-formed result of performance.measureMemory with cross-origin iframe.'); }, 'performance.measureMemory URLs within a cross-site iframe.');
...@@ -4,23 +4,29 @@ ...@@ -4,23 +4,29 @@
'use strict'; 'use strict';
promise_test(async testCase => { promise_test(async testCase => {
const grandchildLoaded = new Promise(resolve => { const {iframes} = await build([
window.onmessage = function(message) { {
if (message.data === 'grandchild-loaded') { id: 'same-origin-1',
resolve(message); children: [
} {
} id: 'same-origin-2',
}); }
const frame = document.createElement('iframe'); ],
const child = getUrl(SAME_ORIGIN, 'resources/child.sub.html'); },
const grandchild = getUrl(SAME_ORIGIN, 'resources/grandchild.sub.html'); ]);
frame.src = child;
document.body.append(frame);
await grandchildLoaded;
try { try {
let result = await performance.measureMemory(); const result = await performance.measureMemory();
checkMeasureMemory(result, { checkMeasureMemory(result, {
allowed: [window.location.href, child, grandchild], allowed: [
window.location.href,
iframes['same-origin-1'].src,
iframes['same-origin-2'].src,
],
required: [
window.location.href,
iframes['same-origin-1'].src,
iframes['same-origin-2'].src,
]
}); });
} catch (error) { } catch (error) {
if (!(error instanceof DOMException)) { if (!(error instanceof DOMException)) {
...@@ -28,4 +34,4 @@ promise_test(async testCase => { ...@@ -28,4 +34,4 @@ promise_test(async testCase => {
} }
assert_equals(error.name, 'SecurityError'); assert_equals(error.name, 'SecurityError');
} }
}, 'Well-formed result of performance.measureMemory with same-origin iframe.'); }, 'Well-formed result of performance.measureMemory with same-origin iframes.');
...@@ -4,9 +4,12 @@ ...@@ -4,9 +4,12 @@
'use strict'; 'use strict';
promise_test(async testCase => { promise_test(async testCase => {
try { try {
let result = await performance.measureMemory(); const result = await performance.measureMemory();
checkMeasureMemory(result, {allowed: [window.location.href]}); checkMeasureMemory(result, {
allowed: [window.location.href],
required: [window.location.href],
});
} catch (error) { } catch (error) {
if (!(error instanceof DOMException)) { if (!(error instanceof DOMException)) {
throw error; throw error;
......
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
// META: timeout=long
'use strict';
promise_test(async testCase => {
const {iframes, windows} = await build([
{
id: 'cross-origin-1',
redirect: 'client',
children: [
{
id: 'same-origin-2',
},
{
id: 'cross-origin-3',
},
{
id: 'cross-site-4',
}
],
},
{
id: 'cross-origin-5',
redirect: 'client',
window_open: true,
children: [
{
id: 'same-origin-6',
},
{
id: 'cross-origin-7',
},
{
id: 'cross-site-8',
}
],
},
]);
const keep = sameOriginContexts(frames).concat(sameOriginContexts(windows));
try {
const result = await performance.measureMemory();
checkMeasureMemory(result, {
allowed: [
window.location.href,
iframes['cross-origin-1'].src,
],
required: [
window.location.href,
],
});
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
assert_equals(error.name, 'SecurityError');
}
}, 'performance.measureMemory does not leak client redirected URL.');
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
// META: timeout=long
'use strict';
promise_test(async testCase => {
const {iframes, windows} = await build([
{
id: 'cross-origin-1',
redirect: 'server',
children: [
{
id: 'same-origin-2',
},
{
id: 'cross-origin-3',
},
{
id: 'cross-site-4',
}
],
},
{
id: 'cross-origin-5',
redirect: 'server',
window_open: true,
children: [
{
id: 'same-origin-6',
},
{
id: 'cross-origin-7',
},
{
id: 'cross-site-8',
}
],
},
]);
const keep = sameOriginContexts(frames).concat(sameOriginContexts(windows));
try {
const result = await performance.measureMemory();
checkMeasureMemory(result, {
allowed: [
window.location.href,
iframes['cross-origin-1'].src,
],
required: [
window.location.href,
],
});
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
assert_equals(error.name, 'SecurityError');
}
}, 'performance.measureMemory does not leak server redirected URL.');
<!doctype html>
<meta charset=utf-8>
<html>
<script>
window.onmessage = function (message) {
// Forward the message to the parent.
window.parent.postMessage(message.data, '*');
}
window.onload = function () {
window.parent.postMessage('grandchild-loaded', '*');
}
</script>
<body>
Hello from child iframe.
<iframe src="grandchild.sub.html"></iframe>
</body>
</html>
const SAME_ORIGIN = {origin: get_host_info().HTTP_ORIGIN, name: "SAME_ORIGIN"}; const ORIGINS = {
const CROSS_ORIGIN = {origin: get_host_info().HTTP_REMOTE_ORIGIN, name: "CROSS_ORIGIN"} 'same-origin': get_host_info().HTTP_ORIGIN,
'cross-origin': get_host_info().HTTP_REMOTE_ORIGIN,
'cross-site': get_host_info().HTTP_NOTSAMESITE_ORIGIN,
}
function checkMeasureMemoryBreakdown(breakdown, options) { function checkMeasureMemoryBreakdown(breakdown, options, required) {
let allowed = new Set(options.allowed); const allowed = new Set(options.allowed);
assert_own_property(breakdown, 'bytes'); assert_own_property(breakdown, 'bytes');
assert_greater_than_equal(breakdown.bytes, 0); assert_greater_than_equal(breakdown.bytes, 0);
assert_own_property(breakdown, 'userAgentSpecificTypes'); assert_own_property(breakdown, 'userAgentSpecificTypes');
for (let userAgentSpecificType of breakdown.userAgentSpecificTypes) { for (const userAgentSpecificType of breakdown.userAgentSpecificTypes) {
assert_equals(typeof userAgentSpecificType, 'string'); assert_equals(typeof userAgentSpecificType, 'string');
} }
assert_own_property(breakdown, 'attribution'); assert_own_property(breakdown, 'attribution');
for (let attribution of breakdown.attribution) { for (const attribution of breakdown.attribution) {
assert_equals(typeof attribution, 'string'); assert_equals(typeof attribution, 'string');
assert_true( assert_true(
allowed.has(attribution), allowed.has(attribution),
`${attribution} must be in ${JSON.stringify(options.allowed)}`); `${attribution} must be in ${JSON.stringify(options.allowed)}`);
if (required.has(attribution)) {
required.delete(attribution);
}
} }
} }
function checkMeasureMemory(result, options) { function checkMeasureMemory(result, options) {
assert_own_property(result, 'bytes'); assert_own_property(result, 'bytes');
assert_own_property(result, 'breakdown'); assert_own_property(result, 'breakdown');
const required = new Set(options.required);
let bytes = 0; let bytes = 0;
for (let breakdown of result.breakdown) { for (let breakdown of result.breakdown) {
checkMeasureMemoryBreakdown(breakdown, options); checkMeasureMemoryBreakdown(breakdown, options, required);
bytes += breakdown.bytes; bytes += breakdown.bytes;
} }
assert_equals(bytes, result.bytes); assert_equals(bytes, result.bytes);
assert_equals(required.size, 0, JSON.stringify(result.breakdown) +
' does not include ' + JSON.stringify(required.values()));
}
function url(params) {
let origin = null;
for (const key of Object.keys(ORIGINS)) {
if (params.id.startsWith(key)) {
origin = ORIGINS[key];
}
}
const child = params.window_open ? 'window' : 'iframe';
let file = `measure-memory/resources/${child}.sub.html`;
if (params.redirect) {
file = `measure-memory/resources/${child}.redirect.sub.html`;
}
let url = `${origin}/${file}?id=${params.id}`;
if (params.redirect === 'server') {
url = `${origin}/common/redirect.py?location=${encodeURIComponent(url)}`;
}
return url;
}
// A simple multiplexor of messages based on iframe ids.
let waitForMessage = (function () {
class Inbox {
constructor() {
this.queue = [];
this.resolve = null;
}
push(value) {
if (this.resolve) {
this.resolve(value);
this.resolve = null;
} else {
this.queue.push(value);
}
}
pop() {
let promise = new Promise(resolve => this.resolve = resolve);
if (this.queue.length > 0) {
this.resolve(this.queue.shift());
this.resolve = null;
}
return promise;
}
}
const inbox = {};
window.onmessage = function (message) {
const id = message.data.id;
const payload = message.data.payload;
inbox[id] = inbox[id] || new Inbox();
inbox[id].push(payload);
}
return function (id) {
inbox[id] = inbox[id] || new Inbox();
return inbox[id].pop();
}
})();
// Constructs iframes based on their descriptoin.
async function build(children) {
window.accessible_children = {iframes: {}, windows: {}};
await Promise.all(children.map(buildChild));
const result = window.accessible_children;
delete window.accessible_children;
return result;
} }
function getUrl(host, relativePath) { async function buildChild(params) {
const path = new URL(relativePath, window.location).pathname; let child = null;
return `${host.origin}${path}`; function target() {
return params.window_open ? child : child.contentWindow;
}
if (params.window_open) {
child = window.open(url(params));
} else {
child = document.createElement('iframe');
child.src = url(params);
child.id = params.id;
document.body.appendChild(child);
}
const ready = await waitForMessage(params.id);
target().postMessage({id: 'parent', payload: params.children}, '*');
const done = await waitForMessage(params.id);
let main = window;
while (true) {
if (main === main.parent) {
if (!main.opener) {
break;
} else {
main = main.opener;
}
} else {
main = main.parent;
}
}
try {
main.accessible_children;
} catch (e) {
// Cross-origin iframe that cannot access the main frame.
return;
}
if (params.window_open) {
main.accessible_children.windows[params.id] = child;
} else {
main.accessible_children.iframes[params.id] = child;
}
}
function getId() {
const params = new URLSearchParams(document.location.search);
return params.get('id');
}
function getParent() {
if (window.parent == window && window.opener) {
return window.opener;
}
return window.parent;
}
// This function runs within an iframe.
// It gets the children descriptions from the parent and constructs them.
async function setupChild() {
const id = getId();
document.getElementById('title').textContent = id;
getParent().postMessage({id : id, payload: 'ready'}, '*');
const children = await waitForMessage('parent');
if (children) {
await build(children);
}
getParent().postMessage({id: id, payload: 'done'}, '*');
}
function sameOriginContexts(children) {
const result = [];
for (const [id, child] of Object.entries(children)) {
if (id.includes('same-origin')) {
result.push(child.contentWindow
? child.contentWindow.performance : child.performance);
}
}
return result;
} }
\ No newline at end of file
<!doctype html> <!doctype html>
<meta charset=utf-8> <meta charset="utf-8">
<html> <html>
<script> <script>
window.onload = function () { window.onload = function () {
document.location.href = document.location.href.replace('redirecting-child', 'child'); document.location.href = document.location.href.replace('redirect', 'secret');
} }
</script> </script>
<body> <body>
Hello from child iframe. Hello from the redirecting iframe: <span id="title"></span>
</body> </body>
</html> </html>
<!doctype html>
<meta charset="utf-8">
<html>
<script src="/common/get-host-info.sub.js"></script>
<script src="./common.js"></script>
<script>
window.onload = function () {
setTimeout(setupChild, 0);
}
</script>
<body>
Hello from the secrect iframe: <span id="title"></span>
</body>
</html>
<!doctype html>
<meta charset="utf-8">
<html>
<script src="/common/get-host-info.sub.js"></script>
<script src="./common.js"></script>
<script>
window.onload = function() {
setTimeout(setupChild, 0);
}
</script>
<body>
Hello from the iframe: <span id="title"></span>
</body>
</html>
<!doctype html> <!doctype html>
<meta charset=utf-8> <meta charset="utf-8">
<html> <html>
<script> <script>
window.onload = function () { window.onload = function () {
window.parent.postMessage('grandchild-loaded', '*'); document.location.href = document.location.href.replace('redirect', 'secret');
} }
</script> </script>
<body> <body>
Hello from grandchild iframe. Hello from the redirecting widnow: <span id="title"></span>
</body> </body>
</html> </html>
<!doctype html>
<meta charset="utf-8">
<html>
<script src="/common/get-host-info.sub.js"></script>
<script src="./common.js"></script>
<script>
window.onload = function () {
setTimeout(setupChild, 0);
}
</script>
<body>
Hello from the secrect window: <span id="title"></span>
</body>
</html>
<!doctype html>
<meta charset="utf-8">
<html>
<script src="/common/get-host-info.sub.js"></script>
<script src="./common.js"></script>
<script>
window.onload = function() {
setTimeout(setupChild, 0);
}
</script>
<body>
Hello from the window: <span id="title"></span>
</body>
</html>
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
// META: timeout=long
'use strict';
promise_test(async testCase => {
const {windows, iframes} = await build([
{
id: 'cross-origin-1',
window_open: true,
children: [
{
id: 'same-origin-2',
window_open: true,
},
{
id: 'same-origin-3',
},
{
id: 'cross-origin-4',
},
]
},
]);
try {
const result = await performance.measureMemory();
checkMeasureMemory(result, {
allowed: [
window.location.href,
],
required: [
window.location.href,
],
});
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
assert_equals(error.name, 'SecurityError');
}
}, 'performance.measureMemory does not leak URL of cross-origin window.open.');
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
// META: timeout=long
'use strict';
promise_test(async testCase => {
const {windows, iframes} = await build([
{
id: 'cross-site-1',
window_open: true,
children: [
{
id: 'same-origin-2',
window_open: true,
},
{
id: 'same-origin-3',
},
{
id: 'cross-origin-4',
},
]
},
]);
try {
const result = await performance.measureMemory();
checkMeasureMemory(result, {
allowed: [
window.location.href,
],
required: [
window.location.href,
],
});
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
assert_equals(error.name, 'SecurityError');
}
}, 'performance.measureMemory does not leak URL of cross-site window.open.');
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
// META: timeout=long
'use strict';
promise_test(async testCase => {
const {windows, iframes} = await build([
{
id: 'same-origin-1',
children: [
{
id: 'same-origin-2',
window_open: true,
children: [
{
id: 'same-origin-3',
window_open: true,
},
],
},
{
id: 'cross-origin-4',
children: [
{
id: 'same-origin-5',
window_open: true,
},
],
},
{
id: 'cross-site-6',
children: [
{
id: 'same-origin-7',
window_open: true,
},
],
},
{
id: 'same-origin-8',
children: [
{
id: 'cross-origin-9',
window_open: true,
children: [
{
id: 'same-origin-10',
},
{
id: 'same-origin-11',
window_open: true,
},
],
},
{
id: 'cross-site-12',
window_open: true,
children: [
{
id: 'same-origin-13',
},
{
id: 'same-origin-14',
window_open: true,
},
],
},
],
},
]
},
]);
try {
const result = await performance.measureMemory();
checkMeasureMemory(result, {
allowed: [
window.location.href,
iframes['same-origin-1'].src,
windows['same-origin-2'].location.href,
windows['same-origin-3'].location.href,
iframes['cross-origin-4'].src,
iframes['cross-site-6'].src,
iframes['same-origin-8'].src,
],
required: [
window.location.href,
iframes['same-origin-1'].src,
windows['same-origin-2'].location.href,
windows['same-origin-3'].location.href,
iframes['same-origin-8'].src,
],
});
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
assert_equals(error.name, 'SecurityError');
}
}, 'performance.measureMemory does not leak URLs in cross-origin iframes and windows.');
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
// META: timeout=long
'use strict';
promise_test(async testCase => {
const {windows, iframes} = await build([
{
id: 'same-origin-1',
window_open: true,
children: [
{
id: 'same-origin-2',
window_open: true,
},
{
id: 'same-origin-3',
},
]
},
]);
try {
const result = await performance.measureMemory();
checkMeasureMemory(result, {
allowed: [
window.location.href,
windows['same-origin-1'].location.href,
windows['same-origin-2'].location.href,
iframes['same-origin-3'].src,
],
required: [
window.location.href,
windows['same-origin-1'].location.href,
windows['same-origin-2'].location.href,
iframes['same-origin-3'].src,
],
});
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
assert_equals(error.name, 'SecurityError');
}
}, 'Well-formed result of performance.measureMemory with same-origin window.open.');
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment