Skip to content
Permalink
Browse files
RTCController should disable ICE candidate filtering in case of getUs…
…erMedia based on the RTCPerrConnection origin

https://bugs.webkit.org/show_bug.cgi?id=180851

Patch by Youenn Fablet <youenn@apple.com> on 2018-01-11
Reviewed by Eric Carlson.

Source/WebCore:

Test: http/wpt/webrtc/third-party-frame-ice-candidate-filtering.html

RTCController now stores all the client origins (top+frame origins) of frames that got access to camera/microphone access.
For any such client origin, PeerConnection objects ICE candidate filtering is disabled.
ICE candidate filtering is reset whenever navigating/reloading the page.

* Modules/mediastream/RTCController.cpp:
(WebCore::RTCController::reset):
(WebCore::matchDocumentOrigin):
(WebCore::RTCController::shouldDisableICECandidateFiltering):
(WebCore::RTCController::add):
(WebCore::RTCController::disableICECandidateFilteringForAllOrigins):
(WebCore::RTCController::disableICECandidateFiltering):
(WebCore::RTCController::enableICECandidateFiltering):
* Modules/mediastream/RTCController.h:
* Modules/mediastream/RTCPeerConnection.cpp:
(WebCore::RTCPeerConnection::create):
* Modules/mediastream/UserMediaRequest.cpp:
(WebCore::UserMediaRequest::allow):
* page/Page.cpp:
(WebCore::Page::disableICECandidateFiltering):
* testing/Internals.cpp:
(WebCore::Internals::setICECandidateFiltering):

LayoutTests:

* http/wpt/webrtc/resources/third-party-frame-ice-candidate-filtering-iframe.html: Added.
* http/wpt/webrtc/third-party-frame-ice-candidate-filtering-expected.txt: Added.
* http/wpt/webrtc/third-party-frame-ice-candidate-filtering.html: Added.

Canonical link: https://commits.webkit.org/197417@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@226804 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
youennf authored and webkit-commit-queue committed Jan 11, 2018
1 parent 9567b00 commit 95ce35ec039f7b0527891bdefcde69066c8b5731
@@ -1,3 +1,14 @@
2018-01-11 Youenn Fablet <youenn@apple.com>

RTCController should disable ICE candidate filtering in case of getUserMedia based on the RTCPerrConnection origin
https://bugs.webkit.org/show_bug.cgi?id=180851

Reviewed by Eric Carlson.

* http/wpt/webrtc/resources/third-party-frame-ice-candidate-filtering-iframe.html: Added.
* http/wpt/webrtc/third-party-frame-ice-candidate-filtering-expected.txt: Added.
* http/wpt/webrtc/third-party-frame-ice-candidate-filtering.html: Added.

2018-01-11 Ali Juma <ajuma@chromium.org>

window.visualViewport should behave as [SameObject]
@@ -0,0 +1,40 @@
<body>
<script>
function isFilteringEnabled()
{
var pc = new RTCPeerConnection();
pc.createDataChannel("");

var candidates = [];
return new Promise((resolve, reject) => {
pc.onicecandidate = (event) => {
if (event.candidate === null) {
resolve(!candidates.length);
return;
}
candidates.push(event.candidate.candidate);
};
pc.createOffer().then((offer) => {
pc.setLocalDescription(offer);
});
});
}

async function doGetUserMedia() {
var result = await navigator.mediaDevices.getUserMedia({video:true});
return true;
}

window.onmessage = async (event) => {
if (event.data === "checkFiltering") {
event.source.postMessage(await isFilteringEnabled(), event.origin);
return;
}
if (event.data === "capture") {
event.source.postMessage(await doGetUserMedia(), event.origin);
return;
}
event.source.postMessage("unknown message", event.origin);
}
</script>
</body>
@@ -0,0 +1,8 @@


PASS Setup test
PASS getUserMedia on third party iframe and check same frame filtering
PASS Check same origin filtering as top frame
PASS Check same origin filtering as capturing frame
PASS Check filtering of frame with different origin as top and capturing frame origins

@@ -0,0 +1,64 @@
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<title>ICE candidate filtering for third party iframes</title>
</head>
<body>
<script>
if (self.internals && internals.setICECandidateFiltering)
internals.setICECandidateFiltering(true);

async function withFrame(scopeURL)
{
return new Promise((resolve) => {
let frame = document.createElement('iframe');
frame.src = scopeURL;
frame.allow= "camera;microphone";
frame.onload = function() { resolve(frame); };
document.body.appendChild(frame);
});
}

function doIFrameTest(frame, name)
{
return new Promise((resolve, reject) => {
frame.contentWindow.postMessage(name, "*");
window.onmessage = (event) => {
window.onmessage = undefined;
resolve(event.data);
}
setTimeout(() => reject("no message from frame"), 5000);
});
}

var host = get_host_info();
var frame1, frame2, frame2b, frame3;
promise_test(async (test) => {
frame1 = await withFrame("resources/third-party-frame-ice-candidate-filtering-iframe.html");
frame2 = await withFrame(host.HTTP_REMOTE_ORIGIN + "/WebKit/webrtc/resources/third-party-frame-ice-candidate-filtering-iframe.html");
frame2b = await withFrame(host.HTTP_REMOTE_ORIGIN + "/WebKit/webrtc/resources/third-party-frame-ice-candidate-filtering-iframe.html");
frame3 = await withFrame(host.HTTP_REMOTE_ORIGIN_WITH_DIFFERENT_PORT + "/WebKit/webrtc/resources/third-party-frame-ice-candidate-filtering-iframe.html");
}, "Setup test");

promise_test(async (test) => {
assert_true(await doIFrameTest(frame2, "capture"), "iframe is capturing");
assert_false(await doIFrameTest(frame2, "checkFiltering"), "iframe is not filtering");
}, "getUserMedia on third party iframe and check same frame filtering");

promise_test(async (test) => {
assert_false(await doIFrameTest(frame1, "checkFiltering"));
}, "Check same origin filtering as top frame");

promise_test(async (test) => {
assert_false(await doIFrameTest(frame2b, "checkFiltering"));
}, "Check same origin filtering as capturing frame");

promise_test(async (test) => {
assert_true(await doIFrameTest(frame3, "checkFiltering"));
}, "Check filtering of frame with different origin as top and capturing frame origins");

</script>
</body>
</html>
@@ -130,6 +130,7 @@ imported/w3c/web-platform-tests/streams/readable-byte-streams/detached-buffers.s
imported/w3c/web-platform-tests/webrtc [ Skip ]
webrtc [ Skip ]
http/tests/webrtc [ Skip ]
http/wpt/webrtc [ Skip ]
webrtc/datachannel [ Pass ]
webrtc/datachannel/bufferedAmountLowThreshold.html [ Pass Failure ]
webrtc/datachannel/bufferedAmountLowThreshold-default.html [ Pass Failure ]
@@ -1,3 +1,34 @@
2018-01-11 Youenn Fablet <youenn@apple.com>

RTCController should disable ICE candidate filtering in case of getUserMedia based on the RTCPerrConnection origin
https://bugs.webkit.org/show_bug.cgi?id=180851

Reviewed by Eric Carlson.

Test: http/wpt/webrtc/third-party-frame-ice-candidate-filtering.html

RTCController now stores all the client origins (top+frame origins) of frames that got access to camera/microphone access.
For any such client origin, PeerConnection objects ICE candidate filtering is disabled.
ICE candidate filtering is reset whenever navigating/reloading the page.

* Modules/mediastream/RTCController.cpp:
(WebCore::RTCController::reset):
(WebCore::matchDocumentOrigin):
(WebCore::RTCController::shouldDisableICECandidateFiltering):
(WebCore::RTCController::add):
(WebCore::RTCController::disableICECandidateFilteringForAllOrigins):
(WebCore::RTCController::disableICECandidateFiltering):
(WebCore::RTCController::enableICECandidateFiltering):
* Modules/mediastream/RTCController.h:
* Modules/mediastream/RTCPeerConnection.cpp:
(WebCore::RTCPeerConnection::create):
* Modules/mediastream/UserMediaRequest.cpp:
(WebCore::UserMediaRequest::allow):
* page/Page.cpp:
(WebCore::Page::disableICECandidateFiltering):
* testing/Internals.cpp:
(WebCore::Internals::setICECandidateFiltering):

2018-01-11 Ali Juma <ajuma@chromium.org>

window.visualViewport should behave as [SameObject]
@@ -44,6 +44,7 @@ void RTCController::reset(bool shouldFilterICECandidates)
for (RTCPeerConnection& connection : m_peerConnections)
connection.clearController();
m_peerConnections.clear();
m_filteringDisabledOrigins.clear();
}

void RTCController::remove(RTCPeerConnection& connection)
@@ -53,14 +54,30 @@ void RTCController::remove(RTCPeerConnection& connection)
});
}

static inline bool matchDocumentOrigin(Document& document, SecurityOrigin& topOrigin, SecurityOrigin& clientOrigin)
{
if (originsMatch(topOrigin, document.securityOrigin()))
return true;
return originsMatch(topOrigin, document.topOrigin()) && originsMatch(clientOrigin, document.securityOrigin());
}

bool RTCController::shouldDisableICECandidateFiltering(Document& document)
{
if (!m_shouldFilterICECandidates)
return true;
return notFound != m_filteringDisabledOrigins.findMatching([&] (const auto& origin) {
return matchDocumentOrigin(document, origin.topOrigin, origin.clientOrigin);
});
}

void RTCController::add(RTCPeerConnection& connection)
{
m_peerConnections.append(connection);
if (!m_shouldFilterICECandidates)
if (shouldDisableICECandidateFiltering(downcast<Document>(*connection.scriptExecutionContext())))
connection.disableICECandidateFiltering();
}

void RTCController::disableICECandidateFiltering()
void RTCController::disableICECandidateFilteringForAllOrigins()
{
if (!LibWebRTCProvider::webRTCAvailable())
return;
@@ -70,11 +87,26 @@ void RTCController::disableICECandidateFiltering()
connection.disableICECandidateFiltering();
}

void RTCController::disableICECandidateFilteringForDocument(Document& document)
{
if (!LibWebRTCProvider::webRTCAvailable())
return;

m_filteringDisabledOrigins.append(PeerConnectionOrigin { document.topOrigin(), document.securityOrigin() });
for (RTCPeerConnection& connection : m_peerConnections) {
if (auto* peerConnectionDocument = downcast<Document>(connection.scriptExecutionContext())) {
if (matchDocumentOrigin(*peerConnectionDocument, document.topOrigin(), document.securityOrigin()))
connection.disableICECandidateFiltering();
}
}
}

void RTCController::enableICECandidateFiltering()
{
if (!LibWebRTCProvider::webRTCAvailable())
return;

m_filteringDisabledOrigins.clear();
m_shouldFilterICECandidates = true;
for (RTCPeerConnection& connection : m_peerConnections)
connection.enableICECandidateFiltering();
@@ -24,10 +24,12 @@

#pragma once

#include "SecurityOrigin.h"
#include <wtf/Vector.h>

namespace WebCore {

class Document;
class RTCPeerConnection;

class RTCController {
@@ -42,10 +44,19 @@ class RTCController {
void add(RTCPeerConnection&);
void remove(RTCPeerConnection&);

WEBCORE_EXPORT void disableICECandidateFiltering();
WEBCORE_EXPORT void disableICECandidateFilteringForAllOrigins();
WEBCORE_EXPORT void disableICECandidateFilteringForDocument(Document&);
WEBCORE_EXPORT void enableICECandidateFiltering();

private:

bool shouldDisableICECandidateFiltering(Document&);

struct PeerConnectionOrigin {
Ref<SecurityOrigin> topOrigin;
Ref<SecurityOrigin> clientOrigin;
};
Vector<PeerConnectionOrigin> m_filteringDisabledOrigins;
Vector<std::reference_wrapper<RTCPeerConnection>> m_peerConnections;
bool m_shouldFilterICECandidates { true };
#endif
@@ -69,12 +69,7 @@ Ref<RTCPeerConnection> RTCPeerConnection::create(ScriptExecutionContext& context
// Let's make it uncollectable until the pc is closed by JS or the page stops it.
if (!peerConnection->isClosed()) {
peerConnection->setPendingActivity(peerConnection.ptr());

// ICE candidate filtering can only be disabled for connections from documents that have the same origin as the top document,
// or if the page was set to disable it.
auto& document = downcast<Document>(context);
auto* page = document.page();
if (page && (!page->shouldEnableICECandidateFilteringByDefault() || document.origin() == document.topDocument().origin()))
if (auto* page = downcast<Document>(context).page())
peerConnection->registerToController(page->rtcController());
}
return peerConnection;
@@ -251,9 +251,9 @@ void UserMediaRequest::allow(CaptureDevice&& audioDevice, CaptureDevice&& videoD
return;

#if ENABLE(WEB_RTC)
auto* page = downcast<Document>(*m_scriptExecutionContext).page();
if (page)
page->rtcController().disableICECandidateFiltering();
auto& document = downcast<Document>(*m_scriptExecutionContext);
if (auto* page = document.page())
page->rtcController().disableICECandidateFilteringForDocument(document);
#endif
}

@@ -2330,7 +2330,7 @@ void Page::disableICECandidateFiltering()
{
m_shouldEnableICECandidateFilteringByDefault = false;
#if ENABLE(WEB_RTC)
m_rtcController.disableICECandidateFiltering();
m_rtcController.disableICECandidateFilteringForAllOrigins();
#endif
}

@@ -1316,7 +1316,7 @@ void Internals::setICECandidateFiltering(bool enabled)
if (enabled)
rtcController.enableICECandidateFiltering();
else
rtcController.disableICECandidateFiltering();
rtcController.disableICECandidateFilteringForAllOrigins();
}

void Internals::setEnumeratingAllNetworkInterfacesEnabled(bool enabled)

0 comments on commit 95ce35e

Please sign in to comment.