mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
commit
1d557a2997
@ -21,6 +21,10 @@
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
#viewGroup > radio > .radio-label-box {
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
#topBar {
|
||||
border-bottom: 1px solid ThreeDShadow;
|
||||
padding-inline-start: 10px;
|
||||
|
@ -27,7 +27,7 @@
|
||||
font: menu;
|
||||
text-shadow: @loweredShadow@;
|
||||
margin: 0;
|
||||
padding: 0 1px;
|
||||
padding: 0 6px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
|
@ -100,6 +100,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-search-container {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
-moz-appearance: searchfield;
|
||||
padding: 1px;
|
||||
@ -111,7 +115,7 @@
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.search-box.compact > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
|
||||
.search-box > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
|
||||
background-image: url(chrome://global/skin/icons/searchfield-cancel.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 11px 11px;
|
||||
@ -119,12 +123,6 @@
|
||||
height: 11px;
|
||||
}
|
||||
|
||||
.search-box.compact {
|
||||
padding: 0px;
|
||||
/* font size is in px because the XUL it was copied from uses px */
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.textbox-search-clear {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
#viewGroup > radio {
|
||||
list-style-image: url("chrome://browser/skin/pageInfo.png");
|
||||
-moz-appearance: none;
|
||||
padding: 5px 3px 1px 3px;
|
||||
margin: 0 1px;
|
||||
min-width: 4.5em;
|
||||
margin: 0;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
#viewGroup > radio:hover {
|
||||
@ -27,6 +27,11 @@
|
||||
color: black;
|
||||
}
|
||||
|
||||
#viewGroup > radio > .radio-label-box {
|
||||
margin: 0;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
#topBar {
|
||||
border-bottom: 1px solid ThreeDLightShadow;
|
||||
padding-inline-start: 10px;
|
||||
|
@ -289,9 +289,5 @@ https://tls1.example.com:443 privileged,tls1
|
||||
# Hosts for youtube rewrite tests
|
||||
https://mochitest.youtube.com:443
|
||||
|
||||
# Hosts for stylo blocklist tests
|
||||
http://stylo-blocklist.com:80 privileged
|
||||
http://test.stylo-blocklist.com:80 privileged
|
||||
|
||||
# Host for U2F localhost tests
|
||||
https://localhost:443
|
||||
|
@ -303,6 +303,7 @@
|
||||
.boxmodel-editable {
|
||||
border: 1px dashed transparent;
|
||||
-moz-user-select: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.boxmodel-editable:hover {
|
||||
|
@ -4445,24 +4445,6 @@ nsDOMWindowUtils::GetIsStyledByServo(bool* aStyledByServo)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::AddToStyloBlocklist(const nsACString& aBlockedDomain)
|
||||
{
|
||||
#ifdef MOZ_STYLO
|
||||
nsLayoutUtils::AddToStyloBlocklist(aBlockedDomain);
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::RemoveFromStyloBlocklist(const nsACString& aBlockedDomain)
|
||||
{
|
||||
#ifdef MOZ_STYLO
|
||||
nsLayoutUtils::RemoveFromStyloBlocklist(aBlockedDomain);
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)
|
||||
|
@ -13238,7 +13238,7 @@ nsIDocument::UpdateStyleBackendType()
|
||||
|
||||
#ifdef MOZ_STYLO
|
||||
if (nsLayoutUtils::StyloEnabled() &&
|
||||
nsLayoutUtils::ShouldUseStylo(mDocumentURI, NodePrincipal())) {
|
||||
nsLayoutUtils::ShouldUseStylo(NodePrincipal())) {
|
||||
mStyleBackendType = StyleBackendType::Servo;
|
||||
}
|
||||
#endif
|
||||
@ -13902,30 +13902,11 @@ nsDocument::IsThirdParty()
|
||||
return mIsThirdParty.value();
|
||||
}
|
||||
|
||||
static bool
|
||||
IsAboutReader(nsIURI* aURI)
|
||||
{
|
||||
if (!aURI) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCString spec;
|
||||
aURI->GetSpec(spec);
|
||||
|
||||
// Reader mode URLs look like about:reader?[...].
|
||||
return StringBeginsWith(spec, NS_LITERAL_CSTRING("about:reader"));
|
||||
}
|
||||
|
||||
bool
|
||||
nsIDocument::IsScopedStyleEnabled()
|
||||
{
|
||||
if (mIsScopedStyleEnabled == eScopedStyle_Unknown) {
|
||||
// We allow <style scoped> in about:reader pages since on Android
|
||||
// we use it to inject some in-page UI. (We currently don't
|
||||
// support styling about:reader pages in stylo anyway, so for
|
||||
// now it's OK to enable it here.)
|
||||
mIsScopedStyleEnabled = nsContentUtils::IsChromeDoc(this) ||
|
||||
IsAboutReader(mDocumentURI) ||
|
||||
nsContentUtils::IsScopedStylePrefEnabled()
|
||||
? eScopedStyle_Enabled
|
||||
: eScopedStyle_Disabled;
|
||||
|
@ -1980,20 +1980,6 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*/
|
||||
readonly attribute boolean isStyledByServo;
|
||||
|
||||
/**
|
||||
* Add a domain to the existing stylo blocklist.
|
||||
*
|
||||
* This calls nsLayoutUtils::AddToStyloBlocklist().
|
||||
*/
|
||||
void addToStyloBlocklist(in ACString aBlockedDomain);
|
||||
|
||||
/**
|
||||
* Remove a domain from the existing stylo blocklist.
|
||||
*
|
||||
* This calls nsLayoutUtils::RemoveFromStyloBlocklist().
|
||||
*/
|
||||
void removeFromStyloBlocklist(in ACString aBlockedDomain);
|
||||
|
||||
/**
|
||||
* Capture the contents of the current WebRender frame and
|
||||
* save them to a folder relative to the current working directory.
|
||||
|
@ -178,10 +178,6 @@ const char* mozilla::dom::ContentPrefs::gEarlyPrefs[] = {
|
||||
"layout.css.servo.enabled",
|
||||
#endif
|
||||
"layout.css.shape-outside.enabled",
|
||||
#ifdef MOZ_STYLO
|
||||
"layout.css.stylo-blocklist.blocked_domains",
|
||||
"layout.css.stylo-blocklist.enabled",
|
||||
#endif
|
||||
"layout.css.text-align-unsafe-value.enabled",
|
||||
"layout.css.text-combine-upright-digits.enabled",
|
||||
"layout.css.text-combine-upright.enabled",
|
||||
|
@ -350,6 +350,7 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aChannelMap, uint32_t aRate)
|
||||
params.channels = mOutChannels;
|
||||
params.layout = CubebUtils::ConvertChannelMapToCubebLayout(aChannelMap);
|
||||
params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
mAudioClock.Init(aRate);
|
||||
|
||||
|
@ -627,6 +627,7 @@ AudioCallbackDriver::Init()
|
||||
|
||||
output.channels = mOutputChannels;
|
||||
output.layout = CubebUtils::GetPreferredChannelLayoutOrSMPTE(cubebContext, mOutputChannels);
|
||||
output.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
Maybe<uint32_t> latencyPref = CubebUtils::GetCubebMSGLatencyInFrames();
|
||||
if (latencyPref) {
|
||||
|
@ -2339,7 +2339,7 @@ nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue,
|
||||
|
||||
RefPtr<DeclarationBlock> declaration;
|
||||
if (nsLayoutUtils::StyloEnabled() &&
|
||||
nsLayoutUtils::ShouldUseStylo(aDocumentURI, principal)) {
|
||||
nsLayoutUtils::ShouldUseStylo(principal)) {
|
||||
RefPtr<URLExtraData> data =
|
||||
new URLExtraData(aDocumentURI, aDocumentURI, principal);
|
||||
declaration = ServoDeclarationBlock::FromCssText(
|
||||
|
@ -129,7 +129,6 @@
|
||||
#include "DisplayListChecker.h"
|
||||
#include "TextDrawTarget.h"
|
||||
#include "nsDeckFrame.h"
|
||||
#include "nsIEffectiveTLDService.h" // for IsInStyloBlocklist
|
||||
#include "mozilla/StylePrefs.h"
|
||||
#include "mozilla/dom/InspectorFontFace.h"
|
||||
|
||||
@ -196,8 +195,6 @@ typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
|
||||
/* static */ bool nsLayoutUtils::sTextCombineUprightDigitsEnabled;
|
||||
#ifdef MOZ_STYLO
|
||||
/* static */ bool nsLayoutUtils::sStyloEnabled;
|
||||
/* static */ bool nsLayoutUtils::sStyloBlocklistEnabled;
|
||||
/* static */ nsTArray<nsCString>* nsLayoutUtils::sStyloBlocklist = nullptr;
|
||||
#endif
|
||||
/* static */ uint32_t nsLayoutUtils::sIdlePeriodDeadlineLimit;
|
||||
/* static */ uint32_t nsLayoutUtils::sQuiescentFramesBeforeIdlePeriod;
|
||||
@ -8231,24 +8228,6 @@ nsLayoutUtils::Initialize()
|
||||
Preferences::AddBoolVarCache(&sStyloEnabled,
|
||||
"layout.css.servo.enabled");
|
||||
}
|
||||
// We should only create the blocklist ONCE, and ignore any blocklist
|
||||
// reloads happen. Because otherwise we could have a top level page that
|
||||
// uses Stylo (if its load happens before the blocklist reload) and a
|
||||
// child iframe that uses Gecko (if its load happens after the blocklist
|
||||
// reload). If some page contains both backends, and they try to move
|
||||
// element across backend boundary, it could crash (see bug 1404020).
|
||||
sStyloBlocklistEnabled =
|
||||
Preferences::GetBool("layout.css.stylo-blocklist.enabled");
|
||||
if (sStyloBlocklistEnabled && !sStyloBlocklist) {
|
||||
nsAutoCString blocklist;
|
||||
Preferences::GetCString("layout.css.stylo-blocklist.blocked_domains", blocklist);
|
||||
if (!blocklist.IsEmpty()) {
|
||||
sStyloBlocklist = new nsTArray<nsCString>;
|
||||
for (const nsACString& domainString : blocklist.Split(',')) {
|
||||
sStyloBlocklist->AppendElement(domainString);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Preferences::AddUintVarCache(&sIdlePeriodDeadlineLimit,
|
||||
"layout.idle_period.time_limit",
|
||||
@ -8271,13 +8250,7 @@ nsLayoutUtils::Shutdown()
|
||||
delete sContentMap;
|
||||
sContentMap = nullptr;
|
||||
}
|
||||
#ifdef MOZ_STYLO
|
||||
if (sStyloBlocklist) {
|
||||
sStyloBlocklist->Clear();
|
||||
delete sStyloBlocklist;
|
||||
sStyloBlocklist = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (auto& callback : kPrefCallbacks) {
|
||||
Preferences::UnregisterCallback(callback.func, callback.name);
|
||||
}
|
||||
@ -8290,7 +8263,7 @@ nsLayoutUtils::Shutdown()
|
||||
#ifdef MOZ_STYLO
|
||||
/* static */
|
||||
bool
|
||||
nsLayoutUtils::ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal)
|
||||
nsLayoutUtils::ShouldUseStylo(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
// Disable stylo for system principal because XUL hasn't been fully
|
||||
// supported. Other principal aren't able to use XUL by default, and
|
||||
@ -8300,84 +8273,10 @@ nsLayoutUtils::ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal)
|
||||
nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
||||
return false;
|
||||
}
|
||||
// Check any internal page which we need to explicitly blacklist.
|
||||
if (aDocumentURI) {
|
||||
bool isAbout = false;
|
||||
if (NS_SUCCEEDED(aDocumentURI->SchemeIs("about", &isAbout)) && isAbout) {
|
||||
nsAutoCString path;
|
||||
aDocumentURI->GetFilePath(path);
|
||||
// about:reader requires support of scoped style, so we have to
|
||||
// use Gecko backend for now. See bug 1402094.
|
||||
// This should be fixed by bug 1204818.
|
||||
if (path.EqualsLiteral("reader")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check the stylo block list.
|
||||
if (IsInStyloBlocklist(aPrincipal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
nsLayoutUtils::IsInStyloBlocklist(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
if (!sStyloBlocklist) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note that a non-codebase principal (eg the system principal) will return
|
||||
// a null URI.
|
||||
nsCOMPtr<nsIURI> codebaseURI;
|
||||
aPrincipal->GetURI(getter_AddRefs(codebaseURI));
|
||||
if (!codebaseURI) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEffectiveTLDService> tldService =
|
||||
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(tldService, false);
|
||||
|
||||
// Check if a document's eTLD+1 domain belongs to one of the stylo blocklist.
|
||||
nsAutoCString baseDomain;
|
||||
NS_SUCCEEDED(tldService->GetBaseDomain(codebaseURI, 0, baseDomain));
|
||||
for (const nsCString& domains : *sStyloBlocklist) {
|
||||
if (baseDomain.Equals(domains)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsLayoutUtils::AddToStyloBlocklist(const nsACString& aBlockedDomain)
|
||||
{
|
||||
if (!sStyloBlocklist) {
|
||||
sStyloBlocklist = new nsTArray<nsCString>;
|
||||
}
|
||||
sStyloBlocklist->AppendElement(aBlockedDomain);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsLayoutUtils::RemoveFromStyloBlocklist(const nsACString& aBlockedDomain)
|
||||
{
|
||||
if (!sStyloBlocklist) {
|
||||
return;
|
||||
}
|
||||
|
||||
sStyloBlocklist->RemoveElement(aBlockedDomain);
|
||||
|
||||
if (sStyloBlocklist->IsEmpty()) {
|
||||
delete sStyloBlocklist;
|
||||
sStyloBlocklist = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
nsLayoutUtils::StyloChromeEnabled()
|
||||
|
@ -2582,35 +2582,11 @@ public:
|
||||
|
||||
#ifdef MOZ_STYLO
|
||||
/**
|
||||
* Return whether stylo should be used for a given document URI and
|
||||
* principal.
|
||||
* Return whether stylo should be used for a given document principal.
|
||||
*/
|
||||
static bool ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal);
|
||||
|
||||
/**
|
||||
* Principal-based blocklist for stylo.
|
||||
* Check if aPrincipal is blocked by stylo's blocklist and should fallback to
|
||||
* use Gecko's style backend. Note that using a document's principal rather
|
||||
* than the document URI will let us piggy-back off the existing principal
|
||||
* relationships and symmetries.
|
||||
*/
|
||||
static bool IsInStyloBlocklist(nsIPrincipal* aPrincipal);
|
||||
|
||||
/**
|
||||
* Add aBlockedDomain to the existing stylo blocklist, i.e., sStyloBlocklist.
|
||||
* This function is exposed to nsDOMWindowUtils and only for testing purpose.
|
||||
* So, NEVER use this in any other cases.
|
||||
*/
|
||||
static void AddToStyloBlocklist(const nsACString& aBlockedDomain);
|
||||
|
||||
/**
|
||||
* Remove aBlockedDomain from the existing stylo blocklist, i.e., sStyloBlocklist.
|
||||
* This function is exposed to nsDOMWindowUtils and only for testing purpose.
|
||||
* So, NEVER use this in any other cases.
|
||||
*/
|
||||
static void RemoveFromStyloBlocklist(const nsACString& aBlockedDomain);
|
||||
static bool ShouldUseStylo(nsIPrincipal* aPrincipal);
|
||||
#else
|
||||
static bool ShouldUseStylo(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal) {
|
||||
static bool ShouldUseStylo(nsIPrincipal* aPrincipal) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@ -3134,8 +3110,6 @@ private:
|
||||
static bool sTextCombineUprightDigitsEnabled;
|
||||
#ifdef MOZ_STYLO
|
||||
static bool sStyloEnabled;
|
||||
static bool sStyloBlocklistEnabled;
|
||||
static nsTArray<nsCString>* sStyloBlocklist;
|
||||
#endif
|
||||
static uint32_t sIdlePeriodDeadlineLimit;
|
||||
static uint32_t sQuiescentFramesBeforeIdlePeriod;
|
||||
|
@ -304,15 +304,6 @@ skip-if = toolkit == 'android' #bug 775227
|
||||
[test_stylesheet_additions.html]
|
||||
skip-if = !stylo
|
||||
[test_stylesheet_clone_font_face.html]
|
||||
[test_stylo_blocklist-01.html]
|
||||
support-files = stylo_blocked_domain_page.html
|
||||
skip-if = !stylo # no need to test blocklist for non-stylo platforms
|
||||
[test_stylo_blocklist-02.html]
|
||||
support-files = stylo_blocked_domain_page.html
|
||||
skip-if = !stylo # no need to test blocklist for non-stylo platforms
|
||||
[test_stylo_blocklist-03.html]
|
||||
support-files = stylo_non_blocked_domain_page.html
|
||||
skip-if = !stylo # no need to test blocklist for non-stylo platforms
|
||||
[test_supports_rules.html]
|
||||
[test_system_font_serialization.html]
|
||||
[test_text_decoration_shorthands.html]
|
||||
|
@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Test page with a mock domain which is listed in the stylo blocklist</title>
|
||||
</head>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
if (SpecialPowers.getDOMWindowUtils(window).isStyledByServo == false) {
|
||||
opener.postMessage("TEST-PASS", "*");
|
||||
} else {
|
||||
opener.postMessage("TEST-FAIL", "*");
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Test page with a mock domain which is NOT listed in the stylo blocklist</title>
|
||||
</head>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
if (SpecialPowers.getDOMWindowUtils(window).isStyledByServo == true) {
|
||||
opener.postMessage("TEST-PASS", "*");
|
||||
} else {
|
||||
opener.postMessage("TEST-FAIL", "*");
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,24 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the mechanism of stylo blocklist</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.getDOMWindowUtils(window).addToStyloBlocklist("stylo-blocklist.com");
|
||||
let win =
|
||||
window.open("http://stylo-blocklist.com/tests/layout/style/test/stylo_blocked_domain_page.html", "");
|
||||
window.onmessage = function (evt) {
|
||||
is(evt.data, "TEST-PASS",
|
||||
"Document with blocked domain should not use servo backend");
|
||||
win.close();
|
||||
SpecialPowers.getDOMWindowUtils(window).removeFromStyloBlocklist("stylo-blocklist.com");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,24 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the mechanism of stylo blocklist</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.getDOMWindowUtils(window).addToStyloBlocklist("stylo-blocklist.com");
|
||||
let win =
|
||||
window.open("http://test.stylo-blocklist.com/tests/layout/style/test/stylo_blocked_domain_page.html", "");
|
||||
window.onmessage = function (evt) {
|
||||
is(evt.data, "TEST-PASS",
|
||||
"Document with blocked sub-domain should not use servo backend");
|
||||
win.close();
|
||||
SpecialPowers.getDOMWindowUtils(window).removeFromStyloBlocklist("stylo-blocklist.com");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,25 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the mechanism of stylo blocklist</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// "http://example.com" should not be in the blocklist by any chance. Adding this
|
||||
// line is just for safety, and it also makes this test more readable.
|
||||
SpecialPowers.getDOMWindowUtils(window).removeFromStyloBlocklist("example.com");
|
||||
let win =
|
||||
window.open("http://example.com/tests/layout/style/test/stylo_non_blocked_domain_page.html", "");
|
||||
window.onmessage = function (evt) {
|
||||
is(evt.data, "TEST-PASS",
|
||||
"Document without blocked domain should use servo backend");
|
||||
win.close();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
|
||||
|
||||
The audioipc-2 git repository is: https://github.com/djg/audioipc-2.git
|
||||
|
||||
The git commit ID used was 71cc67f44b803e0288997247c060375196f1cf9b (2018-01-19 17:16:33 +1000)
|
||||
The git commit ID used was d7798606aa590ef402344b7a519a0053725a9805 (2018-01-27 09:07:03 +1000)
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "audioipc"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
authors = [
|
||||
"Matthew Gregan <kinetik@flim.org>",
|
||||
"Dan Glastonbury <dan.glastonbury@gmail.com>"
|
||||
|
@ -122,7 +122,8 @@ pub struct StreamParams {
|
||||
pub format: u32,
|
||||
pub rate: u16,
|
||||
pub channels: u8,
|
||||
pub layout: i32
|
||||
pub layout: i32,
|
||||
pub prefs: i32
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ffi::cubeb_stream_params> for StreamParams {
|
||||
@ -133,7 +134,8 @@ impl<'a> From<&'a ffi::cubeb_stream_params> for StreamParams {
|
||||
format: params.format,
|
||||
rate: params.rate as u16,
|
||||
channels: params.channels as u8,
|
||||
layout: params.layout
|
||||
layout: params.layout,
|
||||
prefs: params.prefs
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,7 +146,8 @@ impl<'a> From<&'a StreamParams> for ffi::cubeb_stream_params {
|
||||
format: params.format,
|
||||
rate: u32::from(params.rate),
|
||||
channels: u32::from(params.channels),
|
||||
layout: params.layout
|
||||
layout: params.layout,
|
||||
prefs: params.prefs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ index 5b79b83..01463be 100644
|
||||
[dependencies]
|
||||
audioipc = { path = "../audioipc" }
|
||||
-cubeb-core = { git = "https://github.com/djg/cubeb-rs", version="^0.1" }
|
||||
-cubeb = { git = "https://github.com/djg/cubeb-rs", version="^0.3.1" }
|
||||
-cubeb = { git = "https://github.com/djg/cubeb-rs", version="^0.3.2" }
|
||||
+cubeb-core = { path = "../../cubeb-rs/cubeb-core" }
|
||||
+cubeb = { path = "../../cubeb-rs/cubeb-api" }
|
||||
bytes = "0.4"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "audioipc-server"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
authors = [
|
||||
"Matthew Gregan <kinetik@flim.org>",
|
||||
"Dan Glastonbury <dan.glastonbury@gmail.com>"
|
||||
|
@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
|
||||
|
||||
The cubeb-rs git repository is: https://github.com/djg/cubeb-rs.git
|
||||
|
||||
The git commit ID used was aaba6b34368db990d3ec165ebc85053f61097bac (2017-10-19 15:43:50 +1000)
|
||||
The git commit ID used was c7e5ef006dede18158582246bddafbb51f832078 (2018-01-25 11:35:14 +1000)
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
|
||||
name = "cubeb"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
authors = ["Dan Glastonbury <dglastonbury@mozilla.com>"]
|
||||
license = "ISC"
|
||||
readme = "README.md"
|
||||
|
@ -27,12 +27,13 @@ pub use context::Context;
|
||||
// Re-export cubeb_core types
|
||||
pub use cubeb_core::{ChannelLayout, Device, DeviceFormat, DeviceId, DeviceInfo,
|
||||
DeviceState, DeviceType, Error, ErrorCode, LogLevel, Result,
|
||||
SampleFormat, State, StreamParams};
|
||||
SampleFormat, State, StreamParams, StreamPrefs};
|
||||
pub use cubeb_core::{DEVICE_FMT_F32BE, DEVICE_FMT_F32LE, DEVICE_FMT_S16BE,
|
||||
DEVICE_FMT_S16LE};
|
||||
pub use cubeb_core::{DEVICE_PREF_ALL, DEVICE_PREF_MULTIMEDIA, DEVICE_PREF_NONE,
|
||||
DEVICE_PREF_NOTIFICATION, DEVICE_PREF_VOICE};
|
||||
pub use cubeb_core::{DEVICE_TYPE_INPUT, DEVICE_TYPE_OUTPUT, DEVICE_TYPE_UNKNOWN};
|
||||
pub use cubeb_core::{STREAM_PREF_LOOPBACK, STREAM_PREF_NONE};
|
||||
|
||||
use cubeb_core::binding::Binding;
|
||||
use cubeb_core::ffi;
|
||||
|
@ -40,6 +40,7 @@
|
||||
//! .rate(44100)
|
||||
//! .channels(1)
|
||||
//! .layout(cubeb::ChannelLayout::Mono)
|
||||
//! .prefs(cubeb::STREAM_PREF_NONE)
|
||||
//! .take();
|
||||
//!
|
||||
//! let stream_init_opts = cubeb::StreamInitOptionsBuilder::new()
|
||||
@ -68,9 +69,10 @@
|
||||
//! ```
|
||||
|
||||
use {Binding, ChannelLayout, Context, Device, DeviceId, Error, Result,
|
||||
SampleFormat, State, StreamParams};
|
||||
SampleFormat, State, StreamParams, StreamPrefs};
|
||||
use STREAM_PREF_NONE;
|
||||
use ffi;
|
||||
use std::{ptr, str};
|
||||
use std::ptr;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::{c_long, c_void};
|
||||
use sys;
|
||||
@ -91,7 +93,7 @@ impl SampleType for i16 {
|
||||
SampleFormat::S16NE
|
||||
}
|
||||
fn from_float(x: f32) -> i16 {
|
||||
(x * i16::max_value() as f32) as i16
|
||||
(x * f32::from(i16::max_value())) as i16
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +120,8 @@ pub struct StreamParamsBuilder {
|
||||
format: SampleFormat,
|
||||
rate: u32,
|
||||
channels: u32,
|
||||
layout: ChannelLayout
|
||||
layout: ChannelLayout,
|
||||
prefs: StreamPrefs
|
||||
}
|
||||
|
||||
impl Default for StreamParamsBuilder {
|
||||
@ -133,7 +136,8 @@ impl StreamParamsBuilder {
|
||||
format: SampleFormat::S16NE,
|
||||
rate: 0,
|
||||
channels: 0,
|
||||
layout: ChannelLayout::Undefined
|
||||
layout: ChannelLayout::Undefined,
|
||||
prefs: STREAM_PREF_NONE
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,6 +161,12 @@ impl StreamParamsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn prefs(&mut self, prefs: StreamPrefs) -> &mut Self {
|
||||
self.prefs = prefs;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
|
||||
pub fn take(&self) -> StreamParams {
|
||||
// Convert native endian types to matching format
|
||||
let raw_sample_format = match self.format {
|
||||
@ -172,7 +182,8 @@ impl StreamParamsBuilder {
|
||||
format: raw_sample_format,
|
||||
rate: self.rate,
|
||||
channels: self.channels,
|
||||
layout: self.layout as ffi::cubeb_channel_layout
|
||||
layout: self.layout as ffi::cubeb_channel_layout,
|
||||
prefs: self.prefs.bits()
|
||||
} as *const _)
|
||||
}
|
||||
}
|
||||
@ -291,7 +302,7 @@ where
|
||||
self.raw,
|
||||
&mut device_ptr
|
||||
));
|
||||
Binding::from_raw_opt(device_ptr).ok_or(Error::from_raw(ffi::CUBEB_ERROR))
|
||||
Binding::from_raw_opt(device_ptr).ok_or_else(|| Error::from_raw(ffi::CUBEB_ERROR))
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,7 +337,7 @@ where
|
||||
} else {
|
||||
from_raw_parts(input_buffer as *const _, nframes as usize)
|
||||
};
|
||||
let mut output: &mut [CB::Frame] = if output_buffer.is_null() {
|
||||
let output: &mut [CB::Frame] = if output_buffer.is_null() {
|
||||
&mut []
|
||||
} else {
|
||||
from_raw_parts_mut(output_buffer as *mut _, nframes as usize)
|
||||
@ -638,4 +649,21 @@ mod tests {
|
||||
let raw = unsafe { &*params.raw() };
|
||||
assert_eq!(raw.rate, 44100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_params_builder_prefs_default() {
|
||||
use cubeb_core::STREAM_PREF_NONE;
|
||||
let params = StreamParamsBuilder::new().take();
|
||||
assert_eq!(params.prefs(), STREAM_PREF_NONE);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_params_builder_prefs() {
|
||||
use cubeb_core::STREAM_PREF_LOOPBACK;
|
||||
let params = StreamParamsBuilder::new()
|
||||
.prefs(STREAM_PREF_LOOPBACK)
|
||||
.take();
|
||||
assert_eq!(params.prefs(), STREAM_PREF_LOOPBACK);
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ pub unsafe extern "C" fn capi_stream_start<STM: Stream>(
|
||||
) -> c_int {
|
||||
let stm = &*(s as *const STM);
|
||||
|
||||
let _ = _try!(stm.start());
|
||||
_try!(stm.start());
|
||||
ffi::CUBEB_OK
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ pub unsafe extern "C" fn capi_stream_stop<STM: Stream>(
|
||||
) -> c_int {
|
||||
let stm = &*(s as *const STM);
|
||||
|
||||
let _ = _try!(stm.stop());
|
||||
_try!(stm.stop());
|
||||
ffi::CUBEB_OK
|
||||
}
|
||||
|
||||
@ -192,7 +192,7 @@ pub unsafe extern "C" fn capi_stream_reset_default_device<STM: Stream>(
|
||||
) -> c_int {
|
||||
let stm = &mut *(s as *mut STM);
|
||||
|
||||
let _ = _try!(stm.reset_default_device());
|
||||
_try!(stm.reset_default_device());
|
||||
ffi::CUBEB_OK
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@ pub unsafe extern "C" fn capi_stream_set_volume<STM: Stream>(
|
||||
) -> c_int {
|
||||
let stm = &mut *(s as *mut STM);
|
||||
|
||||
let _ = _try!(stm.set_volume(volume));
|
||||
_try!(stm.set_volume(volume));
|
||||
ffi::CUBEB_OK
|
||||
}
|
||||
|
||||
@ -232,7 +232,7 @@ pub unsafe extern "C" fn capi_stream_set_panning<STM: Stream>(
|
||||
) -> c_int {
|
||||
let stm = &mut *(s as *mut STM);
|
||||
|
||||
let _ = _try!(stm.set_panning(panning));
|
||||
_try!(stm.set_panning(panning));
|
||||
ffi::CUBEB_OK
|
||||
}
|
||||
|
||||
@ -263,7 +263,7 @@ pub unsafe extern "C" fn capi_register_device_collection_changed<CTX: Context>(
|
||||
) -> i32 {
|
||||
let ctx = &*(c as *const CTX);
|
||||
let devtype = DeviceType::from_bits_truncate(devtype);
|
||||
let _ = _try!(ctx.register_device_collection_changed(
|
||||
_try!(ctx.register_device_collection_changed(
|
||||
devtype,
|
||||
collection_changed_callback,
|
||||
user_ptr
|
||||
@ -288,7 +288,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn opt_cstr<'a, T: 'a>(_anchor: &'a T, ptr: *const c_char) -> Option<&'a CStr> {
|
||||
fn opt_cstr<T>(_anchor: &T, ptr: *const c_char) -> Option<&CStr> {
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
|
@ -37,23 +37,77 @@ pub struct cubeb_layout_map {
|
||||
layout: ffi::cubeb_channel_layout
|
||||
}
|
||||
|
||||
pub type InitFn =
|
||||
unsafe extern fn(context: *mut *mut ffi::cubeb, context_name: *const c_char) -> c_int;
|
||||
pub type GetBackendIdFn =
|
||||
unsafe extern fn(context: *mut ffi::cubeb) -> *const c_char;
|
||||
pub type GetMaxChannelCountFn =
|
||||
unsafe extern fn(context: *mut ffi::cubeb, max_channels: *mut u32) -> c_int;
|
||||
pub type GetMinLatencyFn =
|
||||
unsafe extern fn(context: *mut ffi::cubeb,
|
||||
params: ffi::cubeb_stream_params,
|
||||
latency_ms: *mut u32) -> c_int;
|
||||
pub type GetPreferredSampleRateFn =
|
||||
unsafe extern fn(context: *mut ffi::cubeb, rate: *mut u32) -> c_int;
|
||||
pub type GetPreferredChannelLayoutFn =
|
||||
unsafe extern fn(context: *mut ffi::cubeb, layout: *mut ffi::cubeb_channel_layout) -> c_int;
|
||||
pub type EnumerateDevicesFn =
|
||||
unsafe extern fn(context: *mut ffi::cubeb, devtype: ffi::cubeb_device_type,
|
||||
collection: *mut ffi::cubeb_device_collection) -> c_int;
|
||||
pub type DeviceCollectionDestroyFn =
|
||||
unsafe extern fn(context: *mut ffi::cubeb,
|
||||
collection: *mut ffi::cubeb_device_collection) -> c_int;
|
||||
pub type DestroyFn =
|
||||
unsafe extern "C" fn(context: *mut ffi::cubeb);
|
||||
pub type StreamInitFn =
|
||||
unsafe extern fn(context: *mut ffi::cubeb,
|
||||
stream: *mut *mut ffi::cubeb_stream,
|
||||
stream_name: *const c_char,
|
||||
input_device: ffi::cubeb_devid,
|
||||
input_stream_params: *const ffi::cubeb_stream_params,
|
||||
output_device: ffi::cubeb_devid,
|
||||
output_stream_params: *const ffi::cubeb_stream_params,
|
||||
latency: u32,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
state_callback: ffi::cubeb_state_callback,
|
||||
user_ptr: *mut c_void) -> c_int;
|
||||
pub type StreamDestroyFn =
|
||||
unsafe extern "C" fn(stream: *mut ffi::cubeb_stream);
|
||||
pub type StreamStartFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream) -> c_int;
|
||||
pub type StreamStopFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream) -> c_int;
|
||||
pub type StreamResetDefaultDeviceFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream) -> c_int;
|
||||
pub type StreamGetPositionFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream, position: *mut u64) -> c_int;
|
||||
pub type StreamGetLatencyFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream, latency: *mut u32) -> c_int;
|
||||
pub type StreamSetVolumeFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream, volumes: f32) -> c_int;
|
||||
pub type StreamSetPanningFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream, panning: f32) -> c_int;
|
||||
pub type StreamGetCurrentDeviceFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream,
|
||||
device: *mut *const ffi::cubeb_device) -> c_int;
|
||||
pub type StreamDeviceDestroyFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream,
|
||||
device: *const ffi::cubeb_device) -> c_int;
|
||||
pub type StreamRegisterDeviceChangedCallbackFn =
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream,
|
||||
device_changed_callback: ffi::cubeb_device_changed_callback) -> c_int;
|
||||
pub type RegisterDeviceCollectionChangedFn =
|
||||
unsafe extern fn(context: *mut ffi::cubeb,
|
||||
devtype: ffi::cubeb_device_type,
|
||||
callback: ffi::cubeb_device_collection_changed_callback,
|
||||
user_ptr: *mut c_void) -> c_int;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Ops {
|
||||
pub init: Option<
|
||||
unsafe extern fn(context: *mut *mut ffi::cubeb,
|
||||
context_name: *const c_char)
|
||||
-> c_int>,
|
||||
pub get_backend_id: Option<
|
||||
unsafe extern fn(context: *mut ffi::cubeb) -> *const c_char>,
|
||||
pub get_max_channel_count: Option<
|
||||
unsafe extern fn(context: *mut ffi::cubeb,
|
||||
max_channels: *mut u32)
|
||||
-> c_int>,
|
||||
pub get_min_latency: Option<
|
||||
unsafe extern fn(context: *mut ffi::cubeb,
|
||||
params: ffi::cubeb_stream_params,
|
||||
latency_ms: *mut u32)
|
||||
-> c_int>,
|
||||
pub init: Option<InitFn>,
|
||||
pub get_backend_id: Option<GetBackendIdFn>,
|
||||
pub get_max_channel_count: Option<GetMaxChannelCountFn>,
|
||||
pub get_min_latency: Option<GetMinLatencyFn>,
|
||||
pub get_preferred_sample_rate: Option<
|
||||
unsafe extern fn(context: *mut ffi::cubeb, rate: *mut u32) -> c_int>,
|
||||
pub get_preferred_channel_layout: Option<
|
||||
@ -70,19 +124,7 @@ pub struct Ops {
|
||||
collection: *mut ffi::cubeb_device_collection)
|
||||
-> c_int>,
|
||||
pub destroy: Option<unsafe extern "C" fn(context: *mut ffi::cubeb)>,
|
||||
pub stream_init: Option<
|
||||
unsafe extern fn(context: *mut ffi::cubeb,
|
||||
stream: *mut *mut ffi::cubeb_stream,
|
||||
stream_name: *const c_char,
|
||||
input_device: ffi::cubeb_devid,
|
||||
input_stream_params: *const ffi::cubeb_stream_params,
|
||||
output_device: ffi::cubeb_devid,
|
||||
output_stream_params: *const ffi::cubeb_stream_params,
|
||||
latency: u32,
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
state_callback: ffi::cubeb_state_callback,
|
||||
user_ptr: *mut c_void)
|
||||
-> c_int>,
|
||||
pub stream_init: Option<StreamInitFn>,
|
||||
pub stream_destroy: Option<unsafe extern "C" fn(stream: *mut ffi::cubeb_stream)>,
|
||||
pub stream_start: Option<
|
||||
unsafe extern fn(stream: *mut ffi::cubeb_stream) -> c_int>,
|
||||
@ -118,11 +160,7 @@ pub struct Ops {
|
||||
ffi::cubeb_device_changed_callback)
|
||||
-> c_int>,
|
||||
pub register_device_collection_changed: Option<
|
||||
unsafe extern fn(context: *mut ffi::cubeb,
|
||||
devtype: ffi::cubeb_device_type,
|
||||
callback: ffi::cubeb_device_collection_changed_callback,
|
||||
user_ptr: *mut c_void)
|
||||
-> c_int>
|
||||
RegisterDeviceCollectionChangedFn>
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@ -154,20 +192,12 @@ cubeb_enum! {
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct cubeb_channel_map {
|
||||
pub channels: c_uint,
|
||||
pub map: [cubeb_channel; CHANNEL_MAX as usize]
|
||||
}
|
||||
|
||||
impl Clone for cubeb_channel_map {
|
||||
/// Returns a deep copy of the value.
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for cubeb_channel_map {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("cubeb_channel_map")
|
||||
|
@ -23,6 +23,7 @@ pub trait Context {
|
||||
&self,
|
||||
collection: *mut ffi::cubeb_device_collection,
|
||||
);
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
|
||||
fn stream_init(
|
||||
&self,
|
||||
stream_name: Option<&CStr>,
|
||||
|
@ -18,11 +18,11 @@ impl Error {
|
||||
|
||||
pub unsafe fn from_raw(code: ffi::cubeb_error_code) -> Error {
|
||||
let code = match code {
|
||||
ffi::CUBEB_ERROR => ErrorCode::Error,
|
||||
ffi::CUBEB_ERROR_INVALID_FORMAT => ErrorCode::InvalidFormat,
|
||||
ffi::CUBEB_ERROR_INVALID_PARAMETER => ErrorCode::InvalidParameter,
|
||||
ffi::CUBEB_ERROR_NOT_SUPPORTED => ErrorCode::NotSupported,
|
||||
ffi::CUBEB_ERROR_DEVICE_UNAVAILABLE => ErrorCode::DeviceUnavailable,
|
||||
// Everything else is just the generic error
|
||||
_ => ErrorCode::Error,
|
||||
};
|
||||
|
||||
|
@ -87,13 +87,21 @@ cubeb_enum! {
|
||||
}
|
||||
}
|
||||
|
||||
cubeb_enum! {
|
||||
pub enum cubeb_stream_prefs: c_int {
|
||||
CUBEB_STREAM_PREF_NONE = 0x00,
|
||||
CUBEB_STREAM_PREF_LOOPBACK = 0x01,
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct cubeb_stream_params {
|
||||
pub format: cubeb_sample_format,
|
||||
pub rate: c_uint,
|
||||
pub channels: c_uint,
|
||||
pub layout: cubeb_channel_layout
|
||||
pub layout: cubeb_channel_layout,
|
||||
pub prefs: cubeb_stream_prefs
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@ -222,7 +230,7 @@ mod test {
|
||||
use super::cubeb_stream_params;
|
||||
assert_eq!(
|
||||
size_of::<cubeb_stream_params>(),
|
||||
16usize,
|
||||
20usize,
|
||||
concat!("Size of: ", stringify!(cubeb_stream_params))
|
||||
);
|
||||
assert_eq!(
|
||||
@ -270,6 +278,16 @@ mod test {
|
||||
stringify!(layout)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { &(*(0 as *const cubeb_stream_params)).prefs as *const _ as usize },
|
||||
16usize,
|
||||
concat!(
|
||||
"Alignment of field: ",
|
||||
stringify!(cubeb_stream_params),
|
||||
"::",
|
||||
stringify!(prefs)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -145,7 +145,6 @@ pub enum ChannelLayout {
|
||||
impl From<ffi::cubeb_channel_layout> for ChannelLayout {
|
||||
fn from(x: ffi::cubeb_channel_layout) -> ChannelLayout {
|
||||
match x {
|
||||
ffi::CUBEB_LAYOUT_UNDEFINED => ChannelLayout::Undefined,
|
||||
ffi::CUBEB_LAYOUT_DUAL_MONO => ChannelLayout::DualMono,
|
||||
ffi::CUBEB_LAYOUT_DUAL_MONO_LFE => ChannelLayout::DualMonoLfe,
|
||||
ffi::CUBEB_LAYOUT_MONO => ChannelLayout::Mono,
|
||||
@ -165,11 +164,20 @@ impl From<ffi::cubeb_channel_layout> for ChannelLayout {
|
||||
ffi::CUBEB_LAYOUT_3F3R_LFE => ChannelLayout::Layout3F3RLfe,
|
||||
ffi::CUBEB_LAYOUT_3F4_LFE => ChannelLayout::Layout3F4Lfe,
|
||||
// TODO: Implement TryFrom
|
||||
// Everything else is just undefined.
|
||||
_ => ChannelLayout::Undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Miscellaneous stream preferences.
|
||||
bitflags! {
|
||||
pub struct StreamPrefs: ffi::cubeb_stream_prefs {
|
||||
const STREAM_PREF_NONE = ffi::CUBEB_STREAM_PREF_NONE;
|
||||
const STREAM_PREF_LOOPBACK = ffi::CUBEB_STREAM_PREF_LOOPBACK;
|
||||
}
|
||||
}
|
||||
|
||||
/// Stream format initialization parameters.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct StreamParams {
|
||||
@ -233,6 +241,10 @@ impl StreamParams {
|
||||
CUBEB_LAYOUT_3F3R_LFE => Layout3F3RLfe,
|
||||
CUBEB_LAYOUT_3F4_LFE => Layout3F4Lfe)
|
||||
}
|
||||
|
||||
pub fn prefs(&self) -> StreamPrefs {
|
||||
StreamPrefs::from_bits(self.raw.prefs).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Binding for StreamParams {
|
||||
@ -292,7 +304,7 @@ impl<'a> Binding for Device<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stream states signaled via state_callback.
|
||||
/// Stream states signaled via `state_callback`.
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
|
||||
pub enum State {
|
||||
/// Stream started.
|
||||
@ -594,4 +606,12 @@ mod tests {
|
||||
assert_eq!(params.rate(), 44100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_params_raw_prefs() {
|
||||
use STREAM_PREF_LOOPBACK;
|
||||
let mut raw: super::ffi::cubeb_stream_params = unsafe { mem::zeroed() };
|
||||
raw.prefs = STREAM_PREF_LOOPBACK.bits();
|
||||
let params = unsafe { super::StreamParams::from_raw(&raw as *const _) };
|
||||
assert_eq!(params.prefs(), STREAM_PREF_LOOPBACK);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,8 @@
|
||||
The source from this directory was copied from the cubeb
|
||||
git repository using the update.sh script. The only changes
|
||||
made were those applied by update.sh, the addition of
|
||||
Makefile.in build files for the Mozilla build system,
|
||||
and the following patches, which may be overwritten when
|
||||
included upstream.
|
||||
https://github.com/kinetiknz/cubeb/pull/398/commits/c8e66dee61a35e6a6d54e3630e1668bdbd6984b4
|
||||
https://github.com/kinetiknz/cubeb/pull/398/commits/2ed979bc891cf1a7822799947a357d4d3b625964
|
||||
made were those applied by update.sh and the addition of
|
||||
Makefile.in build files for the Mozilla build system.
|
||||
|
||||
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
|
||||
|
||||
The git commit ID used was 2b98e3d3279322ce0764ae2b9f28a9ea9000418b (2018-01-19 10:17:28 +1300)
|
||||
The git commit ID used was 4c18a84a4573a23a1c39ccf6c70f1a6c328cb110 (2018-01-23 08:50:28 +1300)
|
||||
|
@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
|
||||
|
||||
The cubeb-pulse-rs git repository is: https://github.com/djg/cubeb-pulse-rs.git
|
||||
|
||||
The git commit ID used was cb7141e3aae5471b3a8ac1e4524f35d04edcdf4e (2018-01-17 13:10:58 +1000)
|
||||
The git commit ID used was deadde7d14e0010628471e33a538a0a1e59765ff (2018-01-25 10:00:16 +1000)
|
||||
|
@ -51,6 +51,16 @@ pub const LAYOUT_3F3R_LFE: ChannelLayout = 17;
|
||||
pub const LAYOUT_3F4_LFE: ChannelLayout = 18;
|
||||
pub const LAYOUT_MAX: ChannelLayout = 256;
|
||||
|
||||
|
||||
// These need to match cubeb_device_type
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
pub struct StreamPrefs : u32 {
|
||||
const STREAM_PREF_NONE = 0x00;
|
||||
const STREAM_PREF_LOOPBACK = 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct StreamParams {
|
||||
@ -58,6 +68,7 @@ pub struct StreamParams {
|
||||
pub rate: u32,
|
||||
pub channels: u32,
|
||||
pub layout: ChannelLayout,
|
||||
pub prefs: StreamPrefs,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@ -308,7 +319,7 @@ extern "C" {
|
||||
#[test]
|
||||
fn bindgen_test_layout_stream_params() {
|
||||
assert_eq!(::std::mem::size_of::<StreamParams>(),
|
||||
16usize,
|
||||
20usize,
|
||||
concat!("Size of: ", stringify!(StreamParams)));
|
||||
assert_eq!(::std::mem::align_of::<StreamParams>(),
|
||||
4usize,
|
||||
@ -337,6 +348,12 @@ fn bindgen_test_layout_stream_params() {
|
||||
stringify!(StreamParams),
|
||||
"::",
|
||||
stringify!(layout)));
|
||||
assert_eq!(unsafe { &(*(0 as *const StreamParams)).prefs as *const _ as usize },
|
||||
16usize,
|
||||
concat!("Alignment of field: ",
|
||||
stringify!(StreamParams),
|
||||
"::",
|
||||
stringify!(layout)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -370,7 +370,7 @@ impl SubscriptionEvent {
|
||||
}
|
||||
|
||||
pub fn event_type(self) -> SubscriptionEventType {
|
||||
unsafe { ::std::mem::transmute(((self.0 & ffi::PA_SUBSCRIPTION_EVENT_TYPE_MASK) >> 4)) }
|
||||
unsafe { ::std::mem::transmute((self.0 & ffi::PA_SUBSCRIPTION_EVENT_TYPE_MASK) >> 4) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ fn pa_channel_to_cubeb_channel(channel: pulse::ChannelPosition) -> cubeb::Channe
|
||||
fn channel_map_to_layout(cm: &pulse::ChannelMap) -> cubeb::ChannelLayout {
|
||||
use pulse::ChannelPosition;
|
||||
let mut cubeb_map: cubeb::ChannelMap = Default::default();
|
||||
cubeb_map.channels = cm.channels as u32;
|
||||
cubeb_map.channels = u32::from(cm.channels);
|
||||
for i in 0usize..cm.channels as usize {
|
||||
cubeb_map.map[i] = pa_channel_to_cubeb_channel(ChannelPosition::try_from(cm.map[i])
|
||||
.unwrap_or(ChannelPosition::Invalid));
|
||||
@ -176,6 +176,7 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
|
||||
pub fn new_stream(&mut self,
|
||||
stream_name: &CStr,
|
||||
input_device: cubeb::DeviceId,
|
||||
@ -205,7 +206,7 @@ impl Context {
|
||||
|
||||
pub fn max_channel_count(&self) -> Result<u32> {
|
||||
match self.default_sink_info {
|
||||
Some(ref info) => Ok(info.channel_map.channels as u32),
|
||||
Some(ref info) => Ok(u32::from(info.channel_map.channels)),
|
||||
None => Err(cubeb::ERROR),
|
||||
}
|
||||
}
|
||||
@ -276,7 +277,7 @@ impl Context {
|
||||
preferred: preferred,
|
||||
format: cubeb::DeviceFmt::all(),
|
||||
default_format: pulse_format_to_cubeb_format(info.sample_spec.format),
|
||||
max_channels: info.channel_map.channels as u32,
|
||||
max_channels: u32::from(info.channel_map.channels),
|
||||
min_rate: 1,
|
||||
max_rate: PA_RATE_MAX,
|
||||
default_rate: info.sample_spec.rate,
|
||||
@ -332,7 +333,7 @@ impl Context {
|
||||
preferred: preferred,
|
||||
format: cubeb::DeviceFmt::all(),
|
||||
default_format: pulse_format_to_cubeb_format(info.sample_spec.format),
|
||||
max_channels: info.channel_map.channels as u32,
|
||||
max_channels: u32::from(info.channel_map.channels),
|
||||
min_rate: 1,
|
||||
max_rate: PA_RATE_MAX,
|
||||
default_rate: info.sample_spec.rate,
|
||||
@ -341,7 +342,6 @@ impl Context {
|
||||
};
|
||||
|
||||
list_data.devinfo.push(devinfo);
|
||||
|
||||
}
|
||||
|
||||
fn default_device_names(_: &pulse::Context, info: &pulse::ServerInfo, user_data: *mut c_void) {
|
||||
@ -403,7 +403,7 @@ impl Context {
|
||||
let mut devices = Vec::from_raw_parts(coll.device as *mut cubeb::DeviceInfo,
|
||||
coll.count,
|
||||
coll.count);
|
||||
for dev in devices.iter_mut() {
|
||||
for dev in &mut devices {
|
||||
if !dev.group_id.is_null() {
|
||||
let _ = CString::from_raw(dev.group_id as *mut _);
|
||||
}
|
||||
@ -558,18 +558,15 @@ impl Context {
|
||||
}
|
||||
|
||||
let context_ptr: *mut c_void = self as *mut _ as *mut _;
|
||||
match self.context.take() {
|
||||
Some(ctx) => {
|
||||
self.mainloop.lock();
|
||||
if let Ok(o) = ctx.drain(drain_complete, context_ptr) {
|
||||
self.operation_wait(None, &o);
|
||||
}
|
||||
ctx.clear_state_callback();
|
||||
ctx.disconnect();
|
||||
ctx.unref();
|
||||
self.mainloop.unlock();
|
||||
},
|
||||
_ => {},
|
||||
if let Some(ctx) = self.context.take() {
|
||||
self.mainloop.lock();
|
||||
if let Ok(o) = ctx.drain(drain_complete, context_ptr) {
|
||||
self.operation_wait(None, &o);
|
||||
}
|
||||
ctx.clear_state_callback();
|
||||
ctx.disconnect();
|
||||
ctx.unref();
|
||||
self.mainloop.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ fn cubeb_channel_to_pa_channel(channel: cubeb::Channel) -> pa_channel_position_t
|
||||
PA_CHANNEL_POSITION_LFE // CHANNEL_LFE
|
||||
];
|
||||
|
||||
let idx: i32 = channel.into();
|
||||
let idx: i32 = channel;
|
||||
MAP[idx as usize]
|
||||
}
|
||||
|
||||
@ -89,6 +89,7 @@ impl<'ctx> Drop for Stream<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> Stream<'ctx> {
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
|
||||
pub fn new(context: &'ctx Context,
|
||||
stream_name: &CStr,
|
||||
input_device: cubeb::DeviceId,
|
||||
@ -114,10 +115,8 @@ impl<'ctx> Stream<'ctx> {
|
||||
let readable_size: i32 = s.readable_size()
|
||||
.and_then(|s| Ok(s as i32))
|
||||
.unwrap_or(-1);
|
||||
if readable_size > 0 {
|
||||
if unsafe { s.peek(buffer, size).is_err() } {
|
||||
return -1;
|
||||
}
|
||||
if readable_size > 0 && unsafe { s.peek(buffer, size).is_err() } {
|
||||
return -1;
|
||||
}
|
||||
readable_size
|
||||
}
|
||||
@ -230,7 +229,6 @@ impl<'ctx> Stream<'ctx> {
|
||||
return Err(e);
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set up input stream
|
||||
@ -258,7 +256,6 @@ impl<'ctx> Stream<'ctx> {
|
||||
return Err(e);
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let r = if stm.wait_until_ready() {
|
||||
@ -309,31 +306,25 @@ impl<'ctx> Stream<'ctx> {
|
||||
|
||||
self.context.mainloop.lock();
|
||||
{
|
||||
match self.output_stream.take() {
|
||||
Some(stm) => {
|
||||
if !self.drain_timer.is_null() {
|
||||
/* there's no pa_rttime_free, so use this instead. */
|
||||
self.context
|
||||
.mainloop
|
||||
.get_api()
|
||||
.time_free(self.drain_timer);
|
||||
}
|
||||
stm.clear_state_callback();
|
||||
stm.clear_write_callback();
|
||||
let _ = stm.disconnect();
|
||||
stm.unref();
|
||||
},
|
||||
_ => {},
|
||||
if let Some(stm) = self.output_stream.take() {
|
||||
if !self.drain_timer.is_null() {
|
||||
/* there's no pa_rttime_free, so use this instead. */
|
||||
self.context
|
||||
.mainloop
|
||||
.get_api()
|
||||
.time_free(self.drain_timer);
|
||||
}
|
||||
stm.clear_state_callback();
|
||||
stm.clear_write_callback();
|
||||
let _ = stm.disconnect();
|
||||
stm.unref();
|
||||
}
|
||||
|
||||
match self.input_stream.take() {
|
||||
Some(stm) => {
|
||||
stm.clear_state_callback();
|
||||
stm.clear_read_callback();
|
||||
let _ = stm.disconnect();
|
||||
stm.unref();
|
||||
},
|
||||
_ => {},
|
||||
if let Some(stm) = self.input_stream.take() {
|
||||
stm.clear_state_callback();
|
||||
stm.clear_read_callback();
|
||||
let _ = stm.disconnect();
|
||||
stm.unref();
|
||||
}
|
||||
}
|
||||
self.context.mainloop.unlock();
|
||||
@ -416,7 +407,7 @@ impl<'ctx> Stream<'ctx> {
|
||||
Some(ref stm) => {
|
||||
match stm.get_latency() {
|
||||
Ok(StreamLatency::Positive(r_usec)) => {
|
||||
let latency = (r_usec * self.output_sample_spec.rate as pa_usec_t / PA_USEC_PER_SEC) as u32;
|
||||
let latency = (r_usec * pa_usec_t::from(self.output_sample_spec.rate) / PA_USEC_PER_SEC) as u32;
|
||||
Ok(latency)
|
||||
},
|
||||
Ok(_) => {
|
||||
@ -451,8 +442,8 @@ impl<'ctx> Stream<'ctx> {
|
||||
self.volume = volume;
|
||||
} else {
|
||||
let channels = stm.get_sample_spec().channels;
|
||||
let vol = pulse::sw_volume_from_linear(volume as f64);
|
||||
cvol.set(channels as u32, vol);
|
||||
let vol = pulse::sw_volume_from_linear(f64::from(volume));
|
||||
cvol.set(u32::from(channels), vol);
|
||||
|
||||
let index = stm.get_index();
|
||||
|
||||
@ -564,6 +555,10 @@ impl<'ctx> Stream<'ctx> {
|
||||
stream_name: &CStr)
|
||||
-> Result<pulse::Stream> {
|
||||
|
||||
if stream_params.prefs == cubeb::StreamPrefs::STREAM_PREF_LOOPBACK {
|
||||
return Err(cubeb::ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
fn to_pulse_format(format: cubeb::SampleFormat) -> pulse::SampleFormat {
|
||||
match format {
|
||||
cubeb::SAMPLE_S16LE => pulse::SampleFormat::Signed16LE,
|
||||
@ -689,6 +684,7 @@ impl<'ctx> Stream<'ctx> {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
|
||||
fn trigger_user_callback(&mut self, input_data: *const c_void, nbytes: usize) {
|
||||
fn drained_cb(a: &pulse::MainloopApi, e: *mut pa_time_event, _tv: &pulse::TimeVal, u: *mut c_void) {
|
||||
let stm = unsafe { &mut *(u as *mut Stream) };
|
||||
|
@ -123,6 +123,7 @@ int run_test(int num_channels, layout_info layout, int sampling_rate, int is_flo
|
||||
params.rate = sampling_rate;
|
||||
params.channels = num_channels;
|
||||
params.layout = layout.layout;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
synth_state synth(params.channels, params.rate);
|
||||
|
||||
@ -171,6 +172,7 @@ int run_panning_volume_test(int is_float)
|
||||
params.rate = 44100;
|
||||
params.channels = 2;
|
||||
params.layout = CUBEB_LAYOUT_STEREO;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
synth_state synth(params.channels, params.rate);
|
||||
|
||||
|
@ -190,6 +190,7 @@ TEST(cubeb, enumerate_devices)
|
||||
input_params.rate = output_params.rate = 48000;
|
||||
input_params.channels = output_params.channels = 1;
|
||||
input_params.layout = output_params.layout = CUBEB_LAYOUT_MONO;
|
||||
input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
|
||||
NULL, &input_params, NULL, &output_params,
|
||||
|
@ -101,10 +101,12 @@ TEST(cubeb, duplex)
|
||||
input_params.rate = 48000;
|
||||
input_params.channels = 1;
|
||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
||||
input_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
output_params.format = STREAM_FORMAT;
|
||||
output_params.rate = 48000;
|
||||
output_params.channels = 2;
|
||||
output_params.layout = CUBEB_LAYOUT_STEREO;
|
||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
||||
|
@ -34,7 +34,8 @@ TEST(cubeb, latency)
|
||||
CUBEB_SAMPLE_FLOAT32NE,
|
||||
preferred_rate,
|
||||
max_channels,
|
||||
CUBEB_LAYOUT_UNDEFINED
|
||||
CUBEB_LAYOUT_UNDEFINED,
|
||||
CUBEB_STREAM_PREF_NONE
|
||||
};
|
||||
r = cubeb_get_min_latency(ctx, ¶ms, &latency_frames);
|
||||
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
|
||||
|
575
media/libcubeb/gtest/test_loopback.cpp
Normal file
575
media/libcubeb/gtest/test_loopback.cpp
Normal file
@ -0,0 +1,575 @@
|
||||
/*
|
||||
* Copyright © 2017 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
|
||||
/* libcubeb api/function test. Requests a loopback device and checks that
|
||||
output is being looped back to input. NOTE: Usage of output devices while
|
||||
performing this test will cause flakey results! */
|
||||
#include "gtest/gtest.h"
|
||||
#if !defined(_XOPEN_SOURCE)
|
||||
#define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "common.h"
|
||||
|
||||
const uint32_t SAMPLE_FREQUENCY = 48000;
|
||||
const uint32_t TONE_FREQUENCY = 440;
|
||||
const double OUTPUT_AMPLITUDE = 0.25;
|
||||
const uint32_t NUM_FRAMES_TO_OUTPUT = SAMPLE_FREQUENCY / 20; /* play ~50ms of samples */
|
||||
|
||||
template<typename T> T ConvertSampleToOutput(double input);
|
||||
template<> float ConvertSampleToOutput(double input) { return float(input); }
|
||||
template<> short ConvertSampleToOutput(double input) { return short(input * 32767.0f); }
|
||||
|
||||
template<typename T> double ConvertSampleFromOutput(T sample);
|
||||
template<> double ConvertSampleFromOutput(float sample) { return double(sample); }
|
||||
template<> double ConvertSampleFromOutput(short sample) { return double(sample / 32767.0); }
|
||||
|
||||
/* Simple cross correlation to help find phase shift. Not a performant impl */
|
||||
std::vector<double> cross_correlate(std::vector<double> & f,
|
||||
std::vector<double> & g,
|
||||
size_t signal_length)
|
||||
{
|
||||
/* the length we sweep our window through to find the cross correlation */
|
||||
size_t sweep_length = f.size() - signal_length + 1;
|
||||
std::vector<double> correlation;
|
||||
correlation.reserve(sweep_length);
|
||||
for (size_t i = 0; i < sweep_length; i++) {
|
||||
double accumulator = 0.0;
|
||||
for (size_t j = 0; j < signal_length; j++) {
|
||||
accumulator += f.at(j) * g.at(i + j);
|
||||
}
|
||||
correlation.push_back(accumulator);
|
||||
}
|
||||
return correlation;
|
||||
}
|
||||
|
||||
/* best effort discovery of phase shift between output and (looped) input*/
|
||||
size_t find_phase(std::vector<double> & output_frames,
|
||||
std::vector<double> & input_frames,
|
||||
size_t signal_length)
|
||||
{
|
||||
std::vector<double> correlation = cross_correlate(output_frames, input_frames, signal_length);
|
||||
size_t phase = 0;
|
||||
double max_correlation = correlation.at(0);
|
||||
for (size_t i = 1; i < correlation.size(); i++) {
|
||||
if (correlation.at(i) > max_correlation) {
|
||||
max_correlation = correlation.at(i);
|
||||
phase = i;
|
||||
}
|
||||
}
|
||||
return phase;
|
||||
}
|
||||
|
||||
std::vector<double> normalize_frames(std::vector<double> & frames) {
|
||||
double max = abs(*std::max_element(frames.begin(), frames.end(),
|
||||
[](double a, double b) { return abs(a) < abs(b); }));
|
||||
std::vector<double> normalized_frames;
|
||||
normalized_frames.reserve(frames.size());
|
||||
for (const double frame : frames) {
|
||||
normalized_frames.push_back(frame / max);
|
||||
}
|
||||
return normalized_frames;
|
||||
}
|
||||
|
||||
/* heuristic comparison of aligned output and input signals, gets flaky if TONE_FREQUENCY is too high */
|
||||
void compare_signals(std::vector<double> & output_frames,
|
||||
std::vector<double> & input_frames)
|
||||
{
|
||||
ASSERT_EQ(output_frames.size(), input_frames.size()) << "#Output frames != #input frames";
|
||||
size_t num_frames = output_frames.size();
|
||||
std::vector<double> normalized_output_frames = normalize_frames(output_frames);
|
||||
std::vector<double> normalized_input_frames = normalize_frames(input_frames);
|
||||
|
||||
/* calculate mean absolute errors */
|
||||
/* mean absolute errors between output and input */
|
||||
double io_mas = 0.0;
|
||||
/* mean absolute errors between output and silence */
|
||||
double output_silence_mas = 0.0;
|
||||
/* mean absolute errors between input and silence */
|
||||
double input_silence_mas = 0.0;
|
||||
for (size_t i = 0; i < num_frames; i++) {
|
||||
io_mas += abs(normalized_output_frames.at(i) - normalized_input_frames.at(i));
|
||||
output_silence_mas += abs(normalized_output_frames.at(i));
|
||||
input_silence_mas += abs(normalized_input_frames.at(i));
|
||||
}
|
||||
io_mas /= num_frames;
|
||||
output_silence_mas /= num_frames;
|
||||
input_silence_mas /= num_frames;
|
||||
|
||||
ASSERT_LT(io_mas, output_silence_mas)
|
||||
<< "Error between output and input should be less than output and silence!";
|
||||
ASSERT_LT(io_mas, input_silence_mas)
|
||||
<< "Error between output and input should be less than output and silence!";
|
||||
|
||||
/* make sure extrema are in (roughly) correct location */
|
||||
/* number of maxima + minama expected in the frames*/
|
||||
const long NUM_EXTREMA = 2 * TONE_FREQUENCY * NUM_FRAMES_TO_OUTPUT / SAMPLE_FREQUENCY;
|
||||
/* expected index of first maxima */
|
||||
const long FIRST_MAXIMUM_INDEX = SAMPLE_FREQUENCY / TONE_FREQUENCY / 4;
|
||||
/* Threshold we expect all maxima and minima to be above or below. Ideally
|
||||
the extrema would be 1 or -1, but particularly at the start of loopback
|
||||
the values seen can be significantly lower. */
|
||||
const double THRESHOLD = 0.5;
|
||||
|
||||
for (size_t i = 0; i < NUM_EXTREMA; i++) {
|
||||
bool is_maximum = i % 2 == 0;
|
||||
/* expected offset to current extreme: i * stide between extrema */
|
||||
size_t offset = i * SAMPLE_FREQUENCY / TONE_FREQUENCY / 2;
|
||||
if (is_maximum) {
|
||||
ASSERT_GT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset), THRESHOLD)
|
||||
<< "Output frames have unexpected missing maximum!";
|
||||
ASSERT_GT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset), THRESHOLD)
|
||||
<< "Input frames have unexpected missing maximum!";
|
||||
} else {
|
||||
ASSERT_LT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset), -THRESHOLD)
|
||||
<< "Output frames have unexpected missing minimum!";
|
||||
ASSERT_LT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset), -THRESHOLD)
|
||||
<< "Input frames have unexpected missing minimum!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct user_state_loopback {
|
||||
std::mutex user_state_mutex;
|
||||
long position = 0;
|
||||
/* track output */
|
||||
std::vector<double> output_frames;
|
||||
/* track input */
|
||||
std::vector<double> input_frames;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
long data_cb_loop_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
||||
{
|
||||
struct user_state_loopback * u = (struct user_state_loopback *) user;
|
||||
T * ib = (T *) inputbuffer;
|
||||
T * ob = (T *) outputbuffer;
|
||||
|
||||
if (stream == NULL || inputbuffer == NULL || outputbuffer == NULL) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(u->user_state_mutex);
|
||||
/* generate our test tone on the fly */
|
||||
for (int i = 0; i < nframes; i++) {
|
||||
double tone = 0.0;
|
||||
if (u->position + i < NUM_FRAMES_TO_OUTPUT) {
|
||||
/* generate sine wave */
|
||||
tone = sin(2 * M_PI*(i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
|
||||
tone *= OUTPUT_AMPLITUDE;
|
||||
}
|
||||
ob[i] = ConvertSampleToOutput<T>(tone);
|
||||
u->output_frames.push_back(tone);
|
||||
/* store any looped back output, may be silence */
|
||||
u->input_frames.push_back(ConvertSampleFromOutput(ib[i]));
|
||||
}
|
||||
|
||||
u->position += nframes;
|
||||
|
||||
return nframes;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
long data_cb_loop_input_only(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
||||
{
|
||||
struct user_state_loopback * u = (struct user_state_loopback *) user;
|
||||
T * ib = (T *) inputbuffer;
|
||||
|
||||
if (outputbuffer != NULL) {
|
||||
// Can't assert as it needs to return, so expect to fail instead
|
||||
EXPECT_EQ(outputbuffer, (void *) NULL) << "outputbuffer should be null in input only callback";
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
if (stream == NULL || inputbuffer == NULL) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(u->user_state_mutex);
|
||||
for (int i = 0; i < nframes; i++) {
|
||||
u->input_frames.push_back(ConvertSampleFromOutput(ib[i]));
|
||||
}
|
||||
|
||||
return nframes;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
long data_cb_playback(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
||||
{
|
||||
struct user_state_loopback * u = (struct user_state_loopback *) user;
|
||||
T * ob = (T *) outputbuffer;
|
||||
|
||||
if (stream == NULL || outputbuffer == NULL) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(u->user_state_mutex);
|
||||
/* generate our test tone on the fly */
|
||||
for (int i = 0; i < nframes; i++) {
|
||||
double tone = 0.0;
|
||||
if (u->position + i < NUM_FRAMES_TO_OUTPUT) {
|
||||
/* generate sine wave */
|
||||
tone = sin(2 * M_PI*(i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
|
||||
tone *= OUTPUT_AMPLITUDE;
|
||||
}
|
||||
ob[i] = ConvertSampleToOutput<T>(tone);
|
||||
u->output_frames.push_back(tone);
|
||||
}
|
||||
|
||||
u->position += nframes;
|
||||
|
||||
return nframes;
|
||||
}
|
||||
|
||||
void state_cb_loop(cubeb_stream * stream, void * /*user*/, cubeb_state state)
|
||||
{
|
||||
if (stream == NULL)
|
||||
return;
|
||||
|
||||
switch (state) {
|
||||
case CUBEB_STATE_STARTED:
|
||||
fprintf(stderr, "stream started\n"); break;
|
||||
case CUBEB_STATE_STOPPED:
|
||||
fprintf(stderr, "stream stopped\n"); break;
|
||||
case CUBEB_STATE_DRAINED:
|
||||
fprintf(stderr, "stream drained\n"); break;
|
||||
default:
|
||||
fprintf(stderr, "unknown stream state %d\n", state);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void run_loopback_duplex_test(bool is_float)
|
||||
{
|
||||
cubeb * ctx;
|
||||
cubeb_stream * stream;
|
||||
cubeb_stream_params input_params;
|
||||
cubeb_stream_params output_params;
|
||||
int r;
|
||||
uint32_t latency_frames = 0;
|
||||
|
||||
r = common_init(&ctx, "Cubeb loopback example: duplex stream");
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
||||
|
||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
||||
|
||||
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
||||
input_params.rate = SAMPLE_FREQUENCY;
|
||||
input_params.channels = 1;
|
||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
||||
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
|
||||
output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
||||
output_params.rate = SAMPLE_FREQUENCY;
|
||||
output_params.channels = 1;
|
||||
output_params.layout = CUBEB_LAYOUT_MONO;
|
||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
|
||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
||||
|
||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
||||
|
||||
/* setup a duplex stream with loopback */
|
||||
r = cubeb_stream_init(ctx, &stream, "Cubeb loopback",
|
||||
NULL, &input_params, NULL, &output_params, latency_frames,
|
||||
is_float ? data_cb_loop_duplex<float> : data_cb_loop_duplex<short>,
|
||||
state_cb_loop, user_data.get());
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
||||
|
||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
||||
|
||||
cubeb_stream_start(stream);
|
||||
delay(150);
|
||||
cubeb_stream_stop(stream);
|
||||
|
||||
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
|
||||
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
|
||||
std::vector<double> & output_frames = user_data->output_frames;
|
||||
std::vector<double> & input_frames = user_data->input_frames;
|
||||
ASSERT_EQ(output_frames.size(), input_frames.size())
|
||||
<< "#Output frames != #input frames";
|
||||
|
||||
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
|
||||
|
||||
/* extract vectors of just the relevant signal from output and input */
|
||||
auto output_frames_signal_start = output_frames.begin();
|
||||
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
|
||||
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
|
||||
auto input_frames_signal_start = input_frames.begin() + phase;
|
||||
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
|
||||
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
|
||||
|
||||
compare_signals(trimmed_output_frames, trimmed_input_frames);
|
||||
}
|
||||
|
||||
TEST(cubeb, loopback_duplex)
|
||||
{
|
||||
run_loopback_duplex_test(true);
|
||||
run_loopback_duplex_test(false);
|
||||
}
|
||||
|
||||
void run_loopback_separate_streams_test(bool is_float)
|
||||
{
|
||||
cubeb * ctx;
|
||||
cubeb_stream * input_stream;
|
||||
cubeb_stream * output_stream;
|
||||
cubeb_stream_params input_params;
|
||||
cubeb_stream_params output_params;
|
||||
int r;
|
||||
uint32_t latency_frames = 0;
|
||||
|
||||
r = common_init(&ctx, "Cubeb loopback example: separate streams");
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
||||
|
||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
||||
|
||||
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
||||
input_params.rate = SAMPLE_FREQUENCY;
|
||||
input_params.channels = 1;
|
||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
||||
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
|
||||
output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
||||
output_params.rate = SAMPLE_FREQUENCY;
|
||||
output_params.channels = 1;
|
||||
output_params.layout = CUBEB_LAYOUT_MONO;
|
||||
|
||||
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
|
||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
||||
|
||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
||||
|
||||
/* setup an input stream with loopback */
|
||||
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
|
||||
NULL, &input_params, NULL, NULL, latency_frames,
|
||||
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
|
||||
state_cb_loop, user_data.get());
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
||||
|
||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
||||
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
|
||||
|
||||
/* setup an output stream */
|
||||
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only",
|
||||
NULL, NULL, NULL, &output_params, latency_frames,
|
||||
is_float ? data_cb_playback<float> : data_cb_playback<short>,
|
||||
state_cb_loop, user_data.get());
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
||||
|
||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
||||
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
|
||||
|
||||
cubeb_stream_start(input_stream);
|
||||
cubeb_stream_start(output_stream);
|
||||
delay(150);
|
||||
cubeb_stream_stop(output_stream);
|
||||
cubeb_stream_stop(input_stream);
|
||||
|
||||
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
|
||||
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
|
||||
std::vector<double> & output_frames = user_data->output_frames;
|
||||
std::vector<double> & input_frames = user_data->input_frames;
|
||||
ASSERT_LE(output_frames.size(), input_frames.size())
|
||||
<< "#Output frames should be less or equal to #input frames";
|
||||
|
||||
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
|
||||
|
||||
/* extract vectors of just the relevant signal from output and input */
|
||||
auto output_frames_signal_start = output_frames.begin();
|
||||
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
|
||||
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
|
||||
auto input_frames_signal_start = input_frames.begin() + phase;
|
||||
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
|
||||
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
|
||||
|
||||
compare_signals(trimmed_output_frames, trimmed_input_frames);
|
||||
}
|
||||
|
||||
TEST(cubeb, loopback_separate_streams)
|
||||
{
|
||||
run_loopback_separate_streams_test(true);
|
||||
run_loopback_separate_streams_test(false);
|
||||
}
|
||||
|
||||
void run_loopback_silence_test(bool is_float)
|
||||
{
|
||||
cubeb * ctx;
|
||||
cubeb_stream * input_stream;
|
||||
cubeb_stream_params input_params;
|
||||
int r;
|
||||
uint32_t latency_frames = 0;
|
||||
|
||||
r = common_init(&ctx, "Cubeb loopback example: record silence");
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
||||
|
||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
||||
|
||||
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
||||
input_params.rate = SAMPLE_FREQUENCY;
|
||||
input_params.channels = 1;
|
||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
||||
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
|
||||
|
||||
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
|
||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
||||
|
||||
r = cubeb_get_min_latency(ctx, &input_params, &latency_frames);
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
||||
|
||||
/* setup an input stream with loopback */
|
||||
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
|
||||
NULL, &input_params, NULL, NULL, latency_frames,
|
||||
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
|
||||
state_cb_loop, user_data.get());
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
||||
|
||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
||||
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
|
||||
|
||||
cubeb_stream_start(input_stream);
|
||||
delay(50);
|
||||
cubeb_stream_stop(input_stream);
|
||||
|
||||
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
|
||||
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
|
||||
std::vector<double> & input_frames = user_data->input_frames;
|
||||
|
||||
/* expect to have at least ~50ms of frames */
|
||||
ASSERT_GE(input_frames.size(), SAMPLE_FREQUENCY / 20);
|
||||
double EPISILON = 0.000001;
|
||||
/* frames should be 0.0, but use epsilon to avoid possible issues with impls
|
||||
that may use ~0.0 silence values. */
|
||||
for (double frame : input_frames) {
|
||||
ASSERT_LT(abs(frame), EPISILON);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(cubeb, loopback_silence)
|
||||
{
|
||||
run_loopback_silence_test(true);
|
||||
run_loopback_silence_test(false);
|
||||
}
|
||||
|
||||
void run_loopback_device_selection_test(bool is_float)
|
||||
{
|
||||
cubeb * ctx;
|
||||
cubeb_device_collection collection;
|
||||
cubeb_stream * input_stream;
|
||||
cubeb_stream * output_stream;
|
||||
cubeb_stream_params input_params;
|
||||
cubeb_stream_params output_params;
|
||||
int r;
|
||||
uint32_t latency_frames = 0;
|
||||
|
||||
r = common_init(&ctx, "Cubeb loopback example: device selection, separate streams");
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
||||
|
||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
||||
|
||||
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
|
||||
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
|
||||
fprintf(stderr, "Device enumeration not supported"
|
||||
" for this backend, skipping this test.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
|
||||
/* get first preferred output device id */
|
||||
std::string device_id;
|
||||
for (size_t i = 0; i < collection.count; i++) {
|
||||
if (collection.device[i].preferred) {
|
||||
device_id = collection.device[i].device_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cubeb_device_collection_destroy(ctx, &collection);
|
||||
if (device_id.empty()) {
|
||||
fprintf(stderr, "Could not find preferred device, aborting test.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
||||
input_params.rate = SAMPLE_FREQUENCY;
|
||||
input_params.channels = 1;
|
||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
||||
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
|
||||
output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
||||
output_params.rate = SAMPLE_FREQUENCY;
|
||||
output_params.channels = 1;
|
||||
output_params.layout = CUBEB_LAYOUT_MONO;
|
||||
|
||||
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
|
||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
||||
|
||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
||||
|
||||
/* setup an input stream with loopback */
|
||||
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
|
||||
device_id.c_str(), &input_params, NULL, NULL, latency_frames,
|
||||
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
|
||||
state_cb_loop, user_data.get());
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
||||
|
||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
||||
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
|
||||
|
||||
/* setup an output stream */
|
||||
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only",
|
||||
NULL, NULL, device_id.c_str(), &output_params, latency_frames,
|
||||
is_float ? data_cb_playback<float> : data_cb_playback<short>,
|
||||
state_cb_loop, user_data.get());
|
||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
||||
|
||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
||||
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
|
||||
|
||||
cubeb_stream_start(input_stream);
|
||||
cubeb_stream_start(output_stream);
|
||||
delay(150);
|
||||
cubeb_stream_stop(output_stream);
|
||||
cubeb_stream_stop(input_stream);
|
||||
|
||||
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
|
||||
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
|
||||
std::vector<double> & output_frames = user_data->output_frames;
|
||||
std::vector<double> & input_frames = user_data->input_frames;
|
||||
ASSERT_LE(output_frames.size(), input_frames.size())
|
||||
<< "#Output frames should be less or equal to #input frames";
|
||||
|
||||
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
|
||||
|
||||
/* extract vectors of just the relevant signal from output and input */
|
||||
auto output_frames_signal_start = output_frames.begin();
|
||||
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
|
||||
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
|
||||
auto input_frames_signal_start = input_frames.begin() + phase;
|
||||
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
|
||||
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
|
||||
|
||||
compare_signals(trimmed_output_frames, trimmed_input_frames);
|
||||
}
|
||||
|
||||
TEST(cubeb, loopback_device_selection)
|
||||
{
|
||||
run_loopback_device_selection_test(true);
|
||||
run_loopback_device_selection_test(false);
|
||||
}
|
@ -114,7 +114,8 @@ downmix_test(float const * data, cubeb_channel_layout in_layout, cubeb_channel_l
|
||||
STREAM_FORMAT,
|
||||
STREAM_FREQUENCY,
|
||||
layout_infos[in_layout].channels,
|
||||
in_layout
|
||||
in_layout,
|
||||
CUBEB_STREAM_PREF_NONE
|
||||
};
|
||||
|
||||
cubeb_stream_params out_params = {
|
||||
@ -124,7 +125,8 @@ downmix_test(float const * data, cubeb_channel_layout in_layout, cubeb_channel_l
|
||||
// smaller than or equal to the input channels.
|
||||
(out_layout == CUBEB_LAYOUT_UNDEFINED) ?
|
||||
layout_infos[in_layout].channels : layout_infos[out_layout].channels,
|
||||
out_layout
|
||||
out_layout,
|
||||
CUBEB_STREAM_PREF_NONE
|
||||
};
|
||||
|
||||
if (!cubeb_should_downmix(&in_params, &out_params)) {
|
||||
|
@ -68,6 +68,7 @@ TEST(cubeb, overload_callback)
|
||||
output_params.rate = 48000;
|
||||
output_params.channels = 2;
|
||||
output_params.layout = CUBEB_LAYOUT_STEREO;
|
||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
||||
ASSERT_EQ(r, CUBEB_OK);
|
||||
|
@ -94,6 +94,7 @@ TEST(cubeb, record)
|
||||
params.rate = SAMPLE_FREQUENCY;
|
||||
params.channels = 1;
|
||||
params.layout = CUBEB_LAYOUT_UNDEFINED;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, ¶ms, NULL, nullptr,
|
||||
4096, data_cb_record, state_cb_record, &stream_state);
|
||||
|
@ -333,6 +333,7 @@ void test_resampler_duplex(uint32_t input_channels, uint32_t output_channels,
|
||||
input_params.rate = input_rate;
|
||||
state.output_rate = output_params.rate = output_rate;
|
||||
state.target_rate = target_rate;
|
||||
input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
long got;
|
||||
|
||||
cubeb_resampler * resampler =
|
||||
|
@ -125,6 +125,8 @@ TEST(cubeb, context_variables)
|
||||
params.format = STREAM_FORMAT;
|
||||
params.rate = STREAM_RATE;
|
||||
params.layout = STREAM_LAYOUT;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_get_min_latency(ctx, ¶ms, &value);
|
||||
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
|
||||
if (r == CUBEB_OK) {
|
||||
@ -160,6 +162,7 @@ TEST(cubeb, init_destroy_stream)
|
||||
params.rate = STREAM_RATE;
|
||||
params.channels = STREAM_CHANNELS;
|
||||
params.layout = STREAM_LAYOUT;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
@ -186,6 +189,7 @@ TEST(cubeb, init_destroy_multiple_streams)
|
||||
params.rate = STREAM_RATE;
|
||||
params.channels = STREAM_CHANNELS;
|
||||
params.layout = STREAM_LAYOUT;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
|
||||
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
||||
@ -216,6 +220,7 @@ TEST(cubeb, configure_stream)
|
||||
params.rate = STREAM_RATE;
|
||||
params.channels = 2; // panning
|
||||
params.layout = CUBEB_LAYOUT_STEREO;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
@ -247,6 +252,7 @@ TEST(cubeb, configure_stream_undefined_layout)
|
||||
params.rate = STREAM_RATE;
|
||||
params.channels = 2; // panning
|
||||
params.layout = CUBEB_LAYOUT_UNDEFINED;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
@ -282,6 +288,7 @@ test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
|
||||
params.rate = STREAM_RATE;
|
||||
params.channels = STREAM_CHANNELS;
|
||||
params.layout = STREAM_LAYOUT;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
|
||||
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
||||
@ -366,6 +373,7 @@ TEST(cubeb, init_destroy_multiple_contexts_and_streams)
|
||||
params.rate = STREAM_RATE;
|
||||
params.channels = STREAM_CHANNELS;
|
||||
params.layout = STREAM_LAYOUT;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
|
||||
r = common_init(&ctx[i], "test_sanity");
|
||||
@ -404,6 +412,7 @@ TEST(cubeb, basic_stream_operations)
|
||||
params.rate = STREAM_RATE;
|
||||
params.channels = STREAM_CHANNELS;
|
||||
params.layout = STREAM_LAYOUT;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
@ -452,6 +461,7 @@ TEST(cubeb, stream_position)
|
||||
params.rate = STREAM_RATE;
|
||||
params.channels = STREAM_CHANNELS;
|
||||
params.layout = STREAM_LAYOUT;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
@ -585,6 +595,7 @@ TEST(cubeb, drain)
|
||||
params.rate = STREAM_RATE;
|
||||
params.channels = STREAM_CHANNELS;
|
||||
params.layout = STREAM_LAYOUT;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
||||
test_drain_data_callback, test_drain_state_callback, &dummy);
|
||||
|
@ -95,6 +95,7 @@ TEST(cubeb, tone)
|
||||
params.rate = SAMPLE_FREQUENCY;
|
||||
params.channels = 1;
|
||||
params.layout = CUBEB_LAYOUT_MONO;
|
||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
std::unique_ptr<cb_user_data> user_data(new cb_user_data());
|
||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
||||
|
@ -53,16 +53,18 @@ extern "C" {
|
||||
output_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
||||
output_params.rate = rate;
|
||||
output_params.channels = 2;
|
||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
cubeb_stream_params input_params;
|
||||
input_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
||||
input_params.rate = rate;
|
||||
input_params.channels = 1;
|
||||
input_params.prefs = CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
cubeb_stream * stm;
|
||||
rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
|
||||
NULL, input_params,
|
||||
NULL, output_params,
|
||||
NULL, &input_params,
|
||||
NULL, &output_params,
|
||||
latency_frames,
|
||||
data_cb, state_cb,
|
||||
NULL);
|
||||
@ -213,6 +215,15 @@ typedef enum {
|
||||
CUBEB_LAYOUT_MAX
|
||||
} cubeb_channel_layout;
|
||||
|
||||
/** Miscellaneous stream preferences. */
|
||||
typedef enum {
|
||||
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
|
||||
CUBEB_STREAM_PREF_LOOPBACK = 0x01 /**< Request a loopback stream. Should be
|
||||
specified on the input params and an
|
||||
output device to loopback from should
|
||||
be passed in place of an input device. */
|
||||
} cubeb_stream_prefs;
|
||||
|
||||
/** Stream format initialization parameters. */
|
||||
typedef struct {
|
||||
cubeb_sample_format format; /**< Requested sample format. One of
|
||||
@ -220,6 +231,7 @@ typedef struct {
|
||||
uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
|
||||
uint32_t channels; /**< Requested channel count. Valid range is [1, 8]. */
|
||||
cubeb_channel_layout layout; /**< Requested channel layout. This must be consistent with the provided channels. */
|
||||
cubeb_stream_prefs prefs; /**< Requested preferences. */
|
||||
} cubeb_stream_params;
|
||||
|
||||
/** Audio device description */
|
||||
|
@ -890,6 +890,10 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
||||
|
||||
*stream = NULL;
|
||||
|
||||
if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
switch (stream_params->format) {
|
||||
case CUBEB_SAMPLE_S16LE:
|
||||
format = SND_PCM_FORMAT_S16_LE;
|
||||
|
@ -135,8 +135,8 @@ struct cubeb_stream {
|
||||
cubeb_device_changed_callback device_changed_callback = nullptr;
|
||||
owned_critical_section device_changed_callback_lock;
|
||||
/* Stream creation parameters */
|
||||
cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
|
||||
cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
|
||||
cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
|
||||
cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
|
||||
device_info input_device;
|
||||
device_info output_device;
|
||||
/* User pointer of data_callback */
|
||||
@ -2360,6 +2360,12 @@ audiounit_setup_stream(cubeb_stream * stm)
|
||||
{
|
||||
stm->mutex.assert_current_thread_owns();
|
||||
|
||||
if ((stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) ||
|
||||
(stm->output_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK)) {
|
||||
LOG("(%p) Loopback not supported for audiounit.", stm);
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
int r = 0;
|
||||
|
||||
device_info in_dev_info = stm->input_device;
|
||||
|
@ -743,6 +743,12 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
|
||||
if (input_device || output_device)
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
|
||||
// Loopback is unsupported
|
||||
if ((input_stream_params && (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
|
||||
(output_stream_params && (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
*stream = NULL;
|
||||
|
||||
// Find a free stream.
|
||||
|
@ -1291,6 +1291,11 @@ opensl_validate_stream_param(cubeb_stream_params * stream_params)
|
||||
(stream_params->channels < 1 || stream_params->channels > 32))) {
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
if ((stream_params &&
|
||||
(stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
|
||||
LOG("Loopback is not supported");
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
|
@ -807,6 +807,9 @@ create_pa_stream(cubeb_stream * stm,
|
||||
(stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
|
||||
(stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
||||
CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels))));
|
||||
if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
*pa_stm = NULL;
|
||||
pa_sample_spec ss;
|
||||
ss.format = to_pulse_format(stream_params->format);
|
||||
|
@ -279,11 +279,19 @@ sndio_stream_init(cubeb * context,
|
||||
memset(s, 0, sizeof(cubeb_stream));
|
||||
s->mode = 0;
|
||||
if (input_stream_params) {
|
||||
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||
DPR("sndio_stream_init(), loopback not supported\n");
|
||||
goto err;
|
||||
}
|
||||
s->mode |= SIO_REC;
|
||||
format = input_stream_params->format;
|
||||
rate = input_stream_params->rate;
|
||||
}
|
||||
if (output_stream_params) {
|
||||
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||
DPR("sndio_stream_init(), loopback not supported\n");
|
||||
goto err;
|
||||
}
|
||||
s->mode |= SIO_PLAY;
|
||||
format = output_stream_params->format;
|
||||
rate = output_stream_params->rate;
|
||||
|
@ -193,12 +193,12 @@ struct cubeb_stream {
|
||||
/* Mixer pameters. We need to convert the input stream to this
|
||||
samplerate/channel layout, as WASAPI does not resample nor upmix
|
||||
itself. */
|
||||
cubeb_stream_params input_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
|
||||
cubeb_stream_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
|
||||
cubeb_stream_params input_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
|
||||
cubeb_stream_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
|
||||
/* Stream parameters. This is what the client requested,
|
||||
* and what will be presented in the callback. */
|
||||
cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
|
||||
cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED };
|
||||
cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
|
||||
cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
|
||||
/* The input and output device, or NULL for default. */
|
||||
std::unique_ptr<const wchar_t[]> input_device;
|
||||
std::unique_ptr<const wchar_t[]> output_device;
|
||||
@ -207,6 +207,10 @@ struct cubeb_stream {
|
||||
cubeb_state_callback state_callback = nullptr;
|
||||
cubeb_data_callback data_callback = nullptr;
|
||||
wasapi_refill_callback refill_callback = nullptr;
|
||||
/* True when a loopback device is requested with no output device. In this
|
||||
case a dummy output device is opened to drive the loopback, but should not
|
||||
be exposed. */
|
||||
bool has_dummy_output = false;
|
||||
void * user_ptr = nullptr;
|
||||
/* Lifetime considerations:
|
||||
- client, render_client, audio_clock and audio_stream_volume are interface
|
||||
@ -564,9 +568,9 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
|
||||
{
|
||||
XASSERT(!stm->draining);
|
||||
/* If we need to upmix after resampling, resample into the mix buffer to
|
||||
avoid a copy. */
|
||||
avoid a copy. Avoid exposing output if it is a dummy stream. */
|
||||
void * dest = nullptr;
|
||||
if (has_output(stm)) {
|
||||
if (has_output(stm) && !stm->has_dummy_output) {
|
||||
if (cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
|
||||
dest = stm->mix_buffer.data();
|
||||
} else {
|
||||
@ -595,9 +599,10 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
|
||||
|
||||
/* If this is not true, there will be glitches.
|
||||
It is alright to have produced less frames if we are draining, though. */
|
||||
XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm));
|
||||
XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm) || stm->has_dummy_output);
|
||||
|
||||
if (has_output(stm) && cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
|
||||
// We don't bother mixing dummy output as it will be silenced, otherwise mix output if needed
|
||||
if (!stm->has_dummy_output && has_output(stm) && cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
|
||||
XASSERT(dest == stm->mix_buffer.data());
|
||||
unsigned long dest_len = out_frames * stm->output_stream_params.channels;
|
||||
XASSERT(dest_len <= stm->mix_buffer.size() / stm->bytes_per_sample);
|
||||
@ -612,22 +617,12 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
|
||||
|
||||
/* This helper grabs all the frames available from a capture client, put them in
|
||||
* linear_input_buffer. linear_input_buffer should be cleared before the
|
||||
* callback exits. */
|
||||
* callback exits. This helper does not work with exclusive mode streams. */
|
||||
bool get_input_buffer(cubeb_stream * stm)
|
||||
{
|
||||
HRESULT hr;
|
||||
UINT32 padding_in;
|
||||
|
||||
XASSERT(has_input(stm));
|
||||
|
||||
hr = stm->input_client->GetCurrentPadding(&padding_in);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Failed to get padding");
|
||||
return false;
|
||||
}
|
||||
XASSERT(padding_in <= stm->input_buffer_frame_count);
|
||||
UINT32 total_available_input = padding_in;
|
||||
|
||||
HRESULT hr;
|
||||
BYTE * input_packet = NULL;
|
||||
DWORD flags;
|
||||
UINT64 dev_pos;
|
||||
@ -635,17 +630,18 @@ bool get_input_buffer(cubeb_stream * stm)
|
||||
/* Get input packets until we have captured enough frames, and put them in a
|
||||
* contiguous buffer. */
|
||||
uint32_t offset = 0;
|
||||
while (offset != total_available_input) {
|
||||
hr = stm->capture_client->GetNextPacketSize(&next);
|
||||
// If the input stream is event driven we should only ever expect to read a
|
||||
// single packet each time. However, if we're pulling from the stream we may
|
||||
// need to grab multiple packets worth of frames that have accumulated (so
|
||||
// need a loop).
|
||||
for (hr = stm->capture_client->GetNextPacketSize(&next);
|
||||
next > 0;
|
||||
hr = stm->capture_client->GetNextPacketSize(&next)) {
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LOG("cannot get next packet size: %lx", hr);
|
||||
return false;
|
||||
}
|
||||
/* This can happen if the capture stream has stopped. Just return in this
|
||||
* case. */
|
||||
if (!next) {
|
||||
break;
|
||||
}
|
||||
|
||||
UINT32 packet_size;
|
||||
hr = stm->capture_client->GetBuffer(&input_packet,
|
||||
@ -658,6 +654,21 @@ bool get_input_buffer(cubeb_stream * stm)
|
||||
return false;
|
||||
}
|
||||
XASSERT(packet_size == next);
|
||||
// We do not explicitly handle the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
|
||||
// flag. There a two primary (non exhaustive) scenarios we anticipate this
|
||||
// flag being set in:
|
||||
// - The first GetBuffer after Start has this flag undefined. In this
|
||||
// case the flag may be set but is meaningless and can be ignored.
|
||||
// - If a glitch is introduced into the input. This should not happen
|
||||
// for event based inputs, and should be mitigated by using a dummy
|
||||
// stream to drive input in the case of input only loopback. Without
|
||||
// a dummy output, input only loopback would glitch on silence. However,
|
||||
// the dummy input should push silence to the loopback and prevent
|
||||
// discontinuities. See https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/
|
||||
// As the first scenario can be ignored, and we anticipate the second
|
||||
// scenario is mitigated, we ignore the flag.
|
||||
// For more info: https://msdn.microsoft.com/en-us/library/windows/desktop/dd370859(v=vs.85).aspx,
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd371458(v=vs.85).aspx
|
||||
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
|
||||
LOG("insert silence: ps=%u", packet_size);
|
||||
stm->linear_input_buffer->push_silence(packet_size * stm->input_stream_params.channels);
|
||||
@ -687,8 +698,7 @@ bool get_input_buffer(cubeb_stream * stm)
|
||||
offset += packet_size;
|
||||
}
|
||||
|
||||
XASSERT(stm->linear_input_buffer->length() >= total_available_input &&
|
||||
offset == total_available_input);
|
||||
XASSERT(stm->linear_input_buffer->length() >= offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -774,18 +784,36 @@ refill_callback_duplex(cubeb_stream * stm)
|
||||
return false;
|
||||
}
|
||||
|
||||
ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
|
||||
input_frames, output_frames);
|
||||
if (stm->has_dummy_output) {
|
||||
ALOGV("Duplex callback (dummy output): input frames: %Iu, output frames: %Iu",
|
||||
input_frames, output_frames);
|
||||
|
||||
refill(stm,
|
||||
stm->linear_input_buffer->data(),
|
||||
input_frames,
|
||||
output_buffer,
|
||||
output_frames);
|
||||
// We don't want to expose the dummy output to the callback so don't pass
|
||||
// the output buffer (it will be released later with silence in it)
|
||||
refill(stm,
|
||||
stm->linear_input_buffer->data(),
|
||||
input_frames,
|
||||
nullptr,
|
||||
0);
|
||||
} else {
|
||||
ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
|
||||
input_frames, output_frames);
|
||||
|
||||
refill(stm,
|
||||
stm->linear_input_buffer->data(),
|
||||
input_frames,
|
||||
output_buffer,
|
||||
output_frames);
|
||||
}
|
||||
|
||||
stm->linear_input_buffer->clear();
|
||||
|
||||
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
|
||||
if (stm->has_dummy_output) {
|
||||
// If output is a dummy output, make sure it's silent
|
||||
hr = stm->render_client->ReleaseBuffer(output_frames, AUDCLNT_BUFFERFLAGS_SILENT);
|
||||
} else {
|
||||
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
LOG("failed to release buffer: %lx", hr);
|
||||
return false;
|
||||
@ -1517,6 +1545,11 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
|
||||
{
|
||||
com_ptr<IMMDevice> device;
|
||||
HRESULT hr;
|
||||
bool is_loopback = stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK;
|
||||
if (is_loopback && direction != eCapture) {
|
||||
LOG("Loopback pref can only be used with capture streams!\n");
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
stm->stream_reset_lock.assert_current_thread_owns();
|
||||
bool try_again = false;
|
||||
@ -1530,9 +1563,16 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
} else {
|
||||
hr = get_default_endpoint(device, direction);
|
||||
// If caller has requested loopback but not specified a device, look for
|
||||
// the default render device. Otherwise look for the default device
|
||||
// appropriate to the direction.
|
||||
hr = get_default_endpoint(device, is_loopback ? eRender : direction);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
|
||||
if (is_loopback) {
|
||||
LOG("Could not get default render endpoint for loopback, error: %lx\n", hr);
|
||||
} else {
|
||||
LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr);
|
||||
}
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
@ -1603,9 +1643,18 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
|
||||
mix_params->format, mix_params->rate, mix_params->channels,
|
||||
CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].name);
|
||||
|
||||
DWORD flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
|
||||
|
||||
// Check if a loopback device should be requested. Note that event callbacks
|
||||
// do not work with loopback devices, so only request these if not looping.
|
||||
if (is_loopback) {
|
||||
flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
|
||||
} else {
|
||||
flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
}
|
||||
|
||||
hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
|
||||
AUDCLNT_STREAMFLAGS_NOPERSIST,
|
||||
flags,
|
||||
frames_to_hns(stm, stm->latency),
|
||||
0,
|
||||
mix_format.get(),
|
||||
@ -1626,11 +1675,14 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
|
||||
stm->mix_buffer.resize(frames_to_bytes_before_mix(stm, *buffer_frame_count));
|
||||
}
|
||||
|
||||
hr = audio_client->SetEventHandle(event);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could set the event handle for the %s client %lx.",
|
||||
DIRECTION_NAME, hr);
|
||||
return CUBEB_ERROR;
|
||||
// Events are used if not looping back
|
||||
if (!is_loopback) {
|
||||
hr = audio_client->SetEventHandle(event);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could set the event handle for the %s client %lx.",
|
||||
DIRECTION_NAME, hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
hr = audio_client->GetService(riid, render_or_capture_client.receive_vpp());
|
||||
@ -1691,6 +1743,27 @@ int setup_wasapi_stream(cubeb_stream * stm)
|
||||
silent_buffer_count);
|
||||
}
|
||||
|
||||
// If we don't have an output device but are requesting a loopback device,
|
||||
// we attempt to open that same device in output mode in order to drive the
|
||||
// loopback via the output events.
|
||||
stm->has_dummy_output = false;
|
||||
if (!has_output(stm) && stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||
stm->output_stream_params.rate = stm->input_stream_params.rate;
|
||||
stm->output_stream_params.channels = stm->input_stream_params.channels;
|
||||
stm->output_stream_params.layout = stm->input_stream_params.layout;
|
||||
if (stm->input_device) {
|
||||
size_t len = wcslen(stm->input_device.get());
|
||||
std::unique_ptr<wchar_t[]> tmp(new wchar_t[len + 1]);
|
||||
if (wcsncpy_s(tmp.get(), len + 1, stm->input_device.get(), len) != 0) {
|
||||
LOG("Failed to copy device identifier while copying input stream"
|
||||
" configuration to output stream configuration to drive loopback.");
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
stm->output_device = move(tmp);
|
||||
}
|
||||
stm->has_dummy_output = true;
|
||||
}
|
||||
|
||||
if (has_output(stm)) {
|
||||
LOG("(%p) Setup render: device=%p", stm, stm->output_device.get());
|
||||
rv = setup_wasapi_stream_one_side(stm,
|
||||
|
@ -414,6 +414,11 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||
/* Loopback is not supported */
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
*stream = NULL;
|
||||
|
||||
memset(&wfx, 0, sizeof(wfx));
|
||||
|
@ -42,6 +42,7 @@ cp $1/test/test_audio.cpp gtest
|
||||
cp $1/test/test_devices.cpp gtest
|
||||
cp $1/test/test_duplex.cpp gtest
|
||||
cp $1/test/test_latency.cpp gtest
|
||||
cp $1/test/test_loopback.cpp gtest
|
||||
cp $1/test/test_mixer.cpp gtest
|
||||
cp $1/test/test_overload_callback.cpp gtest
|
||||
cp $1/test/test_record.cpp gtest
|
||||
|
@ -5808,9 +5808,6 @@ pref("media.block-autoplay-until-in-foreground", true);
|
||||
// Is Stylo CSS support built and enabled?
|
||||
// Only define these prefs if Stylo support is actually built in.
|
||||
#ifdef MOZ_STYLO
|
||||
// XXX: We should flip this pref to true once the blocked_domains is non-empty.
|
||||
pref("layout.css.stylo-blocklist.enabled", false);
|
||||
pref("layout.css.stylo-blocklist.blocked_domains", "");
|
||||
#ifdef MOZ_STYLO_ENABLE
|
||||
pref("layout.css.servo.enabled", true);
|
||||
#else
|
||||
|
1
servo/Cargo.lock
generated
1
servo/Cargo.lock
generated
@ -1024,7 +1024,6 @@ dependencies = [
|
||||
"simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"style 0.0.1",
|
||||
"style_traits 0.0.1",
|
||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -40,7 +40,6 @@ servo_geometry = {path = "../geometry"}
|
||||
servo_url = {path = "../url"}
|
||||
smallvec = "0.6"
|
||||
style = {path = "../style"}
|
||||
style_traits = {path = "../style_traits"}
|
||||
time = "0.1.12"
|
||||
unicode-bidi = {version = "0.3", features = ["with_serde"]}
|
||||
unicode-script = {version = "0.1", features = ["harfbuzz"]}
|
||||
|
@ -29,14 +29,13 @@ use std::collections::HashMap;
|
||||
use std::f32;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use style::values::computed::Filter;
|
||||
use style_traits::cursor::CursorKind;
|
||||
use text::TextRun;
|
||||
use text::glyph::ByteIndex;
|
||||
use webrender_api::{BoxShadowClipMode, ClipId, ColorF, ExtendMode, GradientStop, ImageKey};
|
||||
use webrender_api::{ImageRendering, LayoutPoint, LayoutRect, LayoutSize, LayoutVector2D};
|
||||
use webrender_api::{LineStyle, LocalClip, MixBlendMode, NormalBorder, RepeatMode, ScrollPolicy};
|
||||
use webrender_api::{ScrollSensitivity, StickyOffsetBounds, TransformStyle};
|
||||
use webrender_api::{BorderRadius, BorderWidths, BoxShadowClipMode, ClipId, ColorF, ExtendMode};
|
||||
use webrender_api::{FilterOp, GradientStop, ImageBorder, ImageKey, ImageRendering, LayoutPoint};
|
||||
use webrender_api::{LayoutRect, LayoutSize, LayoutVector2D, LineStyle, LocalClip, MixBlendMode};
|
||||
use webrender_api::{NormalBorder, ScrollPolicy, ScrollSensitivity, StickyOffsetBounds};
|
||||
use webrender_api::TransformStyle;
|
||||
|
||||
pub use style::dom::OpaqueNode;
|
||||
|
||||
@ -204,7 +203,7 @@ pub struct StackingContext {
|
||||
pub z_index: i32,
|
||||
|
||||
/// CSS filters to be applied to this stacking context (including opacity).
|
||||
pub filters: Vec<Filter>,
|
||||
pub filters: Vec<FilterOp>,
|
||||
|
||||
/// The blend mode with which this stacking context blends with its backdrop.
|
||||
pub mix_blend_mode: MixBlendMode,
|
||||
@ -233,7 +232,7 @@ impl StackingContext {
|
||||
bounds: &Rect<Au>,
|
||||
overflow: &Rect<Au>,
|
||||
z_index: i32,
|
||||
filters: Vec<Filter>,
|
||||
filters: Vec<FilterOp>,
|
||||
mix_blend_mode: MixBlendMode,
|
||||
transform: Option<Transform3D<f32>>,
|
||||
transform_style: TransformStyle,
|
||||
@ -370,7 +369,7 @@ pub struct ClipScrollNode {
|
||||
pub clip: ClippingRegion,
|
||||
|
||||
/// The rect of the contents that can be scrolled inside of the scroll root.
|
||||
pub content_rect: Rect<Au>,
|
||||
pub content_rect: LayoutRect,
|
||||
|
||||
/// The type of this ClipScrollNode.
|
||||
pub node_type: ClipScrollNodeType,
|
||||
@ -419,7 +418,7 @@ pub struct BaseDisplayItem {
|
||||
|
||||
impl BaseDisplayItem {
|
||||
#[inline(always)]
|
||||
pub fn new(bounds: &Rect<Au>,
|
||||
pub fn new(bounds: Rect<Au>,
|
||||
metadata: DisplayItemMetadata,
|
||||
local_clip: LocalClip,
|
||||
section: DisplayListSection,
|
||||
@ -427,12 +426,12 @@ impl BaseDisplayItem {
|
||||
clipping_and_scrolling: ClippingAndScrolling)
|
||||
-> BaseDisplayItem {
|
||||
BaseDisplayItem {
|
||||
bounds: *bounds,
|
||||
metadata: metadata,
|
||||
local_clip: local_clip,
|
||||
section: section,
|
||||
stacking_context_id: stacking_context_id,
|
||||
clipping_and_scrolling: clipping_and_scrolling,
|
||||
bounds,
|
||||
metadata,
|
||||
local_clip,
|
||||
section,
|
||||
stacking_context_id,
|
||||
clipping_and_scrolling,
|
||||
}
|
||||
}
|
||||
|
||||
@ -646,7 +645,7 @@ pub struct DisplayItemMetadata {
|
||||
pub node: OpaqueNode,
|
||||
/// The value of the `cursor` property when the mouse hovers over this display item. If `None`,
|
||||
/// this display item is ineligible for pointer events (`pointer-events: none`).
|
||||
pub pointing: Option<CursorKind>,
|
||||
pub pointing: Option<u16>,
|
||||
}
|
||||
|
||||
/// Paints a solid color.
|
||||
@ -789,28 +788,6 @@ pub struct RadialGradientDisplayItem {
|
||||
pub tile_spacing: LayoutSize,
|
||||
}
|
||||
|
||||
/// A border that is made of image segments.
|
||||
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub struct ImageBorder {
|
||||
/// The image this border uses, border-image-source.
|
||||
pub image: WebRenderImageInfo,
|
||||
|
||||
/// How to slice the image, as per border-image-slice.
|
||||
pub slice: SideOffsets2D<u32>,
|
||||
|
||||
/// Outsets for the border, as per border-image-outset.
|
||||
pub outset: SideOffsets2D<f32>,
|
||||
|
||||
/// If fill is true, draw the center patch of the image.
|
||||
pub fill: bool,
|
||||
|
||||
/// How to repeat or stretch horizontal edges (border-image-repeat).
|
||||
pub repeat_horizontal: RepeatMode,
|
||||
|
||||
/// How to repeat or stretch vertical edges (border-image-repeat).
|
||||
pub repeat_vertical: RepeatMode,
|
||||
}
|
||||
|
||||
/// A border that is made of linear gradient
|
||||
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
|
||||
pub struct GradientBorder {
|
||||
@ -847,7 +824,7 @@ pub struct BorderDisplayItem {
|
||||
pub base: BaseDisplayItem,
|
||||
|
||||
/// Border widths.
|
||||
pub border_widths: SideOffsets2D<Au>,
|
||||
pub border_widths: BorderWidths,
|
||||
|
||||
/// Details for specific border type
|
||||
pub details: BorderDetails,
|
||||
@ -949,7 +926,7 @@ pub struct BoxShadowDisplayItem {
|
||||
pub spread_radius: f32,
|
||||
|
||||
/// The border radius of this shadow.
|
||||
pub border_radius: BorderRadii<Au>,
|
||||
pub border_radius: BorderRadius,
|
||||
|
||||
/// How we should clip the result.
|
||||
pub clip_mode: BoxShadowClipMode,
|
||||
|
@ -58,7 +58,6 @@ extern crate servo_url;
|
||||
extern crate simd;
|
||||
extern crate smallvec;
|
||||
extern crate style;
|
||||
extern crate style_traits;
|
||||
extern crate time;
|
||||
extern crate unicode_bidi;
|
||||
extern crate unicode_script;
|
||||
|
@ -30,7 +30,7 @@ use gfx::display_list::{BaseDisplayItem, BorderDetails, BorderDisplayItem, BLUR_
|
||||
use gfx::display_list::{BorderRadii, BoxShadowDisplayItem, ClipScrollNode};
|
||||
use gfx::display_list::{ClipScrollNodeIndex, ClipScrollNodeType, ClippingAndScrolling};
|
||||
use gfx::display_list::{ClippingRegion, DisplayItem, DisplayItemMetadata, DisplayList};
|
||||
use gfx::display_list::{DisplayListSection, GradientDisplayItem, IframeDisplayItem, ImageBorder};
|
||||
use gfx::display_list::{DisplayListSection, GradientDisplayItem, IframeDisplayItem};
|
||||
use gfx::display_list::{ImageDisplayItem, LineDisplayItem, OpaqueNode};
|
||||
use gfx::display_list::{PopAllTextShadowsDisplayItem, PushTextShadowDisplayItem};
|
||||
use gfx::display_list::{RadialGradientDisplayItem, SolidColorDisplayItem, StackingContext};
|
||||
@ -68,15 +68,15 @@ use style::values::computed::{Gradient, NumberOrPercentage};
|
||||
use style::values::computed::effects::SimpleShadow;
|
||||
use style::values::computed::pointing::Cursor;
|
||||
use style::values::generics::background::BackgroundSize;
|
||||
use style::values::generics::effects::Filter;
|
||||
use style::values::generics::image::{GradientKind, Image, PaintWorklet};
|
||||
use style_traits::CSSPixel;
|
||||
use style_traits::ToCss;
|
||||
use style_traits::cursor::CursorKind;
|
||||
use table_cell::CollapsedBordersForCell;
|
||||
use webrender_api::{self, BoxShadowClipMode, ClipId, ClipMode, ColorF, ComplexClipRegion};
|
||||
use webrender_api::{ImageRendering, LayoutSize, LayoutVector2D, LineStyle};
|
||||
use webrender_api::{LocalClip, NormalBorder, ScrollPolicy, ScrollSensitivity, StickyOffsetBounds};
|
||||
use webrender_api::{self, BorderSide, BoxShadowClipMode, ClipId, ClipMode, ColorF};
|
||||
use webrender_api::{ComplexClipRegion, FilterOp, ImageBorder, ImageRendering, LayoutRect};
|
||||
use webrender_api::{LayoutSize, LayoutVector2D, LineStyle, LocalClip, NinePatchDescriptor};
|
||||
use webrender_api::{NormalBorder, ScrollPolicy, ScrollSensitivity, StickyOffsetBounds};
|
||||
|
||||
trait ResolvePercentage {
|
||||
fn resolve(&self, length: u32) -> u32;
|
||||
@ -246,7 +246,7 @@ impl StackingContextCollectionState {
|
||||
id: Some(ClipId::root_scroll_node(pipeline_id.to_webrender())),
|
||||
parent_index: ClipScrollNodeIndex(0),
|
||||
clip: ClippingRegion::from_rect(&TypedRect::zero()),
|
||||
content_rect: Rect::zero(),
|
||||
content_rect: LayoutRect::zero(),
|
||||
node_type: ClipScrollNodeType::ScrollFrame(ScrollSensitivity::ScriptAndInputEvents),
|
||||
};
|
||||
|
||||
@ -387,10 +387,11 @@ impl<'a> DisplayListBuildState<'a> {
|
||||
};
|
||||
|
||||
BaseDisplayItem::new(
|
||||
&bounds,
|
||||
*bounds,
|
||||
DisplayItemMetadata {
|
||||
node: node,
|
||||
pointing: cursor,
|
||||
node,
|
||||
// Store cursor id in display list.
|
||||
pointing: cursor.map(|x| x as u16),
|
||||
},
|
||||
clip,
|
||||
section,
|
||||
@ -816,7 +817,7 @@ fn calculate_inner_bounds(mut bounds: Rect<Au>, offsets: SideOffsets2D<Au>) -> R
|
||||
}
|
||||
|
||||
fn simple_normal_border(color: ColorF, style: webrender_api::BorderStyle) -> NormalBorder {
|
||||
let side = webrender_api::BorderSide { color, style };
|
||||
let side = BorderSide { color, style };
|
||||
NormalBorder {
|
||||
left: side,
|
||||
right: side,
|
||||
@ -844,6 +845,36 @@ fn calculate_inner_border_radii(
|
||||
radii
|
||||
}
|
||||
|
||||
fn build_image_border_details(
|
||||
webrender_image: WebRenderImageInfo,
|
||||
border_style_struct: &style_structs::Border,
|
||||
) -> Option<BorderDetails> {
|
||||
let corners = &border_style_struct.border_image_slice.offsets;
|
||||
let border_image_repeat = &border_style_struct.border_image_repeat;
|
||||
if let Some(image_key) = webrender_image.key {
|
||||
Some(BorderDetails::Image(ImageBorder {
|
||||
image_key: image_key,
|
||||
patch: NinePatchDescriptor {
|
||||
width: webrender_image.width,
|
||||
height: webrender_image.height,
|
||||
slice: SideOffsets2D::new(
|
||||
corners.0.resolve(webrender_image.height),
|
||||
corners.1.resolve(webrender_image.width),
|
||||
corners.2.resolve(webrender_image.height),
|
||||
corners.3.resolve(webrender_image.width),
|
||||
),
|
||||
},
|
||||
fill: border_style_struct.border_image_slice.fill,
|
||||
// TODO(gw): Support border-image-outset
|
||||
outset: SideOffsets2D::zero(),
|
||||
repeat_horizontal: border_image_repeat.0.to_layout(),
|
||||
repeat_vertical: border_image_repeat.1.to_layout(),
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl FragmentDisplayListBuilding for Fragment {
|
||||
fn collect_stacking_contexts_for_blocklike_fragment(
|
||||
&mut self,
|
||||
@ -1292,7 +1323,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
),
|
||||
blur_radius: box_shadow.base.blur.px(),
|
||||
spread_radius: box_shadow.spread.px(),
|
||||
border_radius,
|
||||
border_radius: border_radius.to_border_radius(),
|
||||
clip_mode: if box_shadow.inset {
|
||||
BoxShadowClipMode::Inset
|
||||
} else {
|
||||
@ -1352,13 +1383,6 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
);
|
||||
}
|
||||
|
||||
let colors = SideOffsets2D::new(
|
||||
style.resolve_color(colors.top),
|
||||
style.resolve_color(colors.right),
|
||||
style.resolve_color(colors.bottom),
|
||||
style.resolve_color(colors.left),
|
||||
);
|
||||
|
||||
// If this border collapses, then we draw outside the boundaries we were given.
|
||||
let mut bounds = *bounds;
|
||||
if let BorderPaintingMode::Collapse(collapsed_borders) = border_painting_mode {
|
||||
@ -1376,35 +1400,28 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
|
||||
let border_radius = build_border_radius(&bounds, border_style_struct);
|
||||
|
||||
match border_style_struct.border_image_source {
|
||||
Either::First(_) => {
|
||||
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
|
||||
base: base,
|
||||
border_widths: border.to_physical(style.writing_mode),
|
||||
details: BorderDetails::Normal(NormalBorder {
|
||||
left: webrender_api::BorderSide {
|
||||
color: colors.left.to_layout(),
|
||||
style: border_style.left.to_layout(),
|
||||
},
|
||||
right: webrender_api::BorderSide {
|
||||
color: colors.right.to_layout(),
|
||||
style: border_style.right.to_layout(),
|
||||
},
|
||||
top: webrender_api::BorderSide {
|
||||
color: colors.top.to_layout(),
|
||||
style: border_style.top.to_layout(),
|
||||
},
|
||||
bottom: webrender_api::BorderSide {
|
||||
color: colors.bottom.to_layout(),
|
||||
style: border_style.bottom.to_layout(),
|
||||
},
|
||||
radius: border_radius.to_border_radius(),
|
||||
}),
|
||||
})));
|
||||
},
|
||||
let details = match border_style_struct.border_image_source {
|
||||
Either::First(_) => Some(BorderDetails::Normal(NormalBorder {
|
||||
left: BorderSide {
|
||||
color: style.resolve_color(colors.left).to_layout(),
|
||||
style: border_style.left.to_layout(),
|
||||
},
|
||||
right: BorderSide {
|
||||
color: style.resolve_color(colors.right).to_layout(),
|
||||
style: border_style.right.to_layout(),
|
||||
},
|
||||
top: BorderSide {
|
||||
color: style.resolve_color(colors.top).to_layout(),
|
||||
style: border_style.top.to_layout(),
|
||||
},
|
||||
bottom: BorderSide {
|
||||
color: style.resolve_color(colors.bottom).to_layout(),
|
||||
style: border_style.bottom.to_layout(),
|
||||
},
|
||||
radius: border_radius.to_border_radius(),
|
||||
})),
|
||||
Either::Second(Image::Gradient(ref gradient)) => {
|
||||
let border_widths = border.to_physical(style.writing_mode);
|
||||
let details = match gradient.kind {
|
||||
Some(match gradient.kind {
|
||||
GradientKind::Linear(angle_or_corner) => {
|
||||
BorderDetails::Gradient(display_list::GradientBorder {
|
||||
gradient: convert_linear_gradient(
|
||||
@ -1430,80 +1447,39 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
outset: SideOffsets2D::zero(),
|
||||
})
|
||||
},
|
||||
};
|
||||
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
|
||||
base,
|
||||
border_widths,
|
||||
details,
|
||||
})));
|
||||
})
|
||||
},
|
||||
Either::Second(Image::PaintWorklet(ref paint_worklet)) => {
|
||||
// TODO: this size should be increased by border-image-outset
|
||||
let size = self.border_box.size.to_physical(style.writing_mode);
|
||||
let webrender_image =
|
||||
self.get_webrender_image_for_paint_worklet(state, style, paint_worklet, size);
|
||||
if let Some(webrender_image) = webrender_image {
|
||||
let corners = &border_style_struct.border_image_slice.offsets;
|
||||
let border_image_repeat = &border_style_struct.border_image_repeat;
|
||||
|
||||
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
|
||||
base: base,
|
||||
border_widths: border.to_physical(style.writing_mode),
|
||||
details: BorderDetails::Image(ImageBorder {
|
||||
image: webrender_image,
|
||||
fill: border_style_struct.border_image_slice.fill,
|
||||
slice: SideOffsets2D::new(
|
||||
corners.0.resolve(webrender_image.height),
|
||||
corners.1.resolve(webrender_image.width),
|
||||
corners.2.resolve(webrender_image.height),
|
||||
corners.3.resolve(webrender_image.width),
|
||||
),
|
||||
// TODO(gw): Support border-image-outset
|
||||
outset: SideOffsets2D::zero(),
|
||||
repeat_horizontal: border_image_repeat.0.to_layout(),
|
||||
repeat_vertical: border_image_repeat.1.to_layout(),
|
||||
}),
|
||||
})));
|
||||
}
|
||||
self.get_webrender_image_for_paint_worklet(state, style, paint_worklet, size)
|
||||
.and_then(|image| build_image_border_details(image, border_style_struct))
|
||||
},
|
||||
Either::Second(Image::Rect(..)) => {
|
||||
// TODO: Handle border-image with `-moz-image-rect`.
|
||||
None
|
||||
},
|
||||
Either::Second(Image::Element(..)) => {
|
||||
// TODO: Handle border-image with `-moz-element`.
|
||||
None
|
||||
},
|
||||
Either::Second(Image::Url(ref image_url)) => {
|
||||
if let Some(url) = image_url.url() {
|
||||
let webrender_image = state.layout_context.get_webrender_image_for_url(
|
||||
Either::Second(Image::Url(ref image_url)) => image_url
|
||||
.url()
|
||||
.and_then(|url| {
|
||||
state.layout_context.get_webrender_image_for_url(
|
||||
self.node,
|
||||
url.clone(),
|
||||
UsePlaceholder::No,
|
||||
);
|
||||
if let Some(webrender_image) = webrender_image {
|
||||
let corners = &border_style_struct.border_image_slice.offsets;
|
||||
let border_image_repeat = &border_style_struct.border_image_repeat;
|
||||
|
||||
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
|
||||
base: base,
|
||||
border_widths: border.to_physical(style.writing_mode),
|
||||
details: BorderDetails::Image(ImageBorder {
|
||||
image: webrender_image,
|
||||
fill: border_style_struct.border_image_slice.fill,
|
||||
slice: SideOffsets2D::new(
|
||||
corners.0.resolve(webrender_image.height),
|
||||
corners.1.resolve(webrender_image.width),
|
||||
corners.2.resolve(webrender_image.height),
|
||||
corners.3.resolve(webrender_image.width),
|
||||
),
|
||||
// TODO(gw): Support border-image-outset
|
||||
outset: SideOffsets2D::zero(),
|
||||
repeat_horizontal: border_image_repeat.0.to_layout(),
|
||||
repeat_vertical: border_image_repeat.1.to_layout(),
|
||||
}),
|
||||
})));
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
.and_then(|image| build_image_border_details(image, border_style_struct)),
|
||||
};
|
||||
if let Some(details) = details {
|
||||
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
|
||||
base,
|
||||
border_widths: border.to_physical(style.writing_mode).to_layout(),
|
||||
details,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1531,10 +1507,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
// absolute bounds.
|
||||
let mut bounds = *bounds;
|
||||
let offset = width + Au::from(style.get_outline().outline_offset);
|
||||
bounds.origin.x = bounds.origin.x - offset;
|
||||
bounds.origin.y = bounds.origin.y - offset;
|
||||
bounds.size.width = bounds.size.width + offset + offset;
|
||||
bounds.size.height = bounds.size.height + offset + offset;
|
||||
bounds = bounds.inflate(offset, offset);
|
||||
|
||||
// Append the outline to the display list.
|
||||
let color = style
|
||||
@ -1549,7 +1522,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
);
|
||||
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
|
||||
base: base,
|
||||
border_widths: SideOffsets2D::new_all_same(width),
|
||||
border_widths: SideOffsets2D::new_all_same(width).to_layout(),
|
||||
details: BorderDetails::Normal(simple_normal_border(color, outline_style.to_layout())),
|
||||
})));
|
||||
}
|
||||
@ -1576,7 +1549,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
);
|
||||
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
|
||||
base: base,
|
||||
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
|
||||
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(),
|
||||
details: BorderDetails::Normal(simple_normal_border(
|
||||
ColorF::rgb(0, 0, 200),
|
||||
webrender_api::BorderStyle::Solid,
|
||||
@ -1623,7 +1596,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
);
|
||||
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
|
||||
base: base,
|
||||
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
|
||||
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)).to_layout(),
|
||||
details: BorderDetails::Normal(simple_normal_border(
|
||||
ColorF::rgb(0, 0, 200),
|
||||
webrender_api::BorderStyle::Solid,
|
||||
@ -2084,9 +2057,9 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
|
||||
// Create the filter pipeline.
|
||||
let effects = self.style().get_effects();
|
||||
let mut filters = effects.filter.0.clone();
|
||||
let mut filters: Vec<FilterOp> = effects.filter.0.iter().map(ToLayout::to_layout).collect();
|
||||
if effects.opacity != 1.0 {
|
||||
filters.push(Filter::Opacity(effects.opacity.into()))
|
||||
filters.push(FilterOp::Opacity(effects.opacity.into(), effects.opacity));
|
||||
}
|
||||
|
||||
StackingContext::new(
|
||||
@ -2095,7 +2068,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
&border_box,
|
||||
&overflow,
|
||||
self.effective_z_index(),
|
||||
filters.into(),
|
||||
filters,
|
||||
self.style().get_effects().mix_blend_mode.to_layout(),
|
||||
self.transform_matrix(&border_box),
|
||||
self.style().get_used_transform_style().to_layout(),
|
||||
@ -2171,8 +2144,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||
}
|
||||
|
||||
// Create display items for text decorations.
|
||||
let text_decorations =
|
||||
self.style().get_inheritedtext().text_decorations_in_effect;
|
||||
let text_decorations = self.style().get_inheritedtext().text_decorations_in_effect;
|
||||
|
||||
let stacking_relative_content_box = LogicalRect::from_physical(
|
||||
self.style.writing_mode,
|
||||
@ -2697,7 +2669,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||
id: None,
|
||||
parent_index: self.clipping_and_scrolling().scrolling,
|
||||
clip: ClippingRegion::from_rect(border_box),
|
||||
content_rect: Rect::zero(),
|
||||
content_rect: LayoutRect::zero(),
|
||||
node_type: ClipScrollNodeType::StickyFrame(sticky_frame_data),
|
||||
});
|
||||
|
||||
@ -2755,7 +2727,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||
id: Some(new_clip_scroll_node_id),
|
||||
parent_index: self.clipping_and_scrolling().scrolling,
|
||||
clip: clip,
|
||||
content_rect: Rect::new(content_box.origin, content_size),
|
||||
content_rect: Rect::new(content_box.origin, content_size).to_layout(),
|
||||
node_type: ClipScrollNodeType::ScrollFrame(sensitivity),
|
||||
});
|
||||
|
||||
@ -2808,7 +2780,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||
id: None,
|
||||
parent_index: self.clipping_and_scrolling().scrolling,
|
||||
clip: ClippingRegion::from_rect(&clip_rect),
|
||||
content_rect: Rect::zero(), // content_rect isn't important for clips.
|
||||
content_rect: LayoutRect::zero(), // content_rect isn't important for clips.
|
||||
node_type: ClipScrollNodeType::Clip,
|
||||
});
|
||||
|
||||
@ -3148,7 +3120,7 @@ impl BaseFlowDisplayListBuilding for BaseFlow {
|
||||
);
|
||||
state.add_display_item(DisplayItem::Border(Box::new(BorderDisplayItem {
|
||||
base: base,
|
||||
border_widths: SideOffsets2D::new_all_same(Au::from_px(2)),
|
||||
border_widths: SideOffsets2D::new_all_same(Au::from_px(2)).to_layout(),
|
||||
details: BorderDetails::Normal(simple_normal_border(
|
||||
color,
|
||||
webrender_api::BorderStyle::Solid,
|
||||
|
@ -77,7 +77,7 @@ impl WebRenderDisplayListConverter for DisplayList {
|
||||
impl WebRenderDisplayItemConverter for DisplayItem {
|
||||
fn prim_info(&self) -> webrender_api::LayoutPrimitiveInfo {
|
||||
let tag = match self.base().metadata.pointing {
|
||||
Some(cursor) => Some((self.base().metadata.node.0 as u64, cursor as u16)),
|
||||
Some(cursor) => Some((self.base().metadata.node.0 as u64, cursor)),
|
||||
None => None,
|
||||
};
|
||||
webrender_api::LayoutPrimitiveInfo {
|
||||
@ -173,29 +173,11 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
||||
}
|
||||
},
|
||||
DisplayItem::Border(ref item) => {
|
||||
let widths = item.border_widths.to_layout();
|
||||
|
||||
let details = match item.details {
|
||||
BorderDetails::Normal(ref border) => {
|
||||
webrender_api::BorderDetails::Normal(*border)
|
||||
},
|
||||
BorderDetails::Image(ref image) => match image.image.key {
|
||||
None => return,
|
||||
Some(key) => {
|
||||
webrender_api::BorderDetails::Image(webrender_api::ImageBorder {
|
||||
image_key: key,
|
||||
patch: webrender_api::NinePatchDescriptor {
|
||||
width: image.image.width,
|
||||
height: image.image.height,
|
||||
slice: image.slice,
|
||||
},
|
||||
fill: image.fill,
|
||||
outset: image.outset,
|
||||
repeat_horizontal: image.repeat_horizontal,
|
||||
repeat_vertical: image.repeat_vertical,
|
||||
})
|
||||
},
|
||||
},
|
||||
BorderDetails::Image(ref image) => webrender_api::BorderDetails::Image(*image),
|
||||
BorderDetails::Gradient(ref gradient) => {
|
||||
webrender_api::BorderDetails::Gradient(webrender_api::GradientBorder {
|
||||
gradient: builder.create_gradient(
|
||||
@ -222,7 +204,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
||||
},
|
||||
};
|
||||
|
||||
builder.push_border(&self.prim_info(), widths, details);
|
||||
builder.push_border(&self.prim_info(), item.border_widths, details);
|
||||
},
|
||||
DisplayItem::Gradient(ref item) => {
|
||||
let gradient = builder.create_gradient(
|
||||
@ -265,7 +247,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
||||
item.color,
|
||||
item.blur_radius,
|
||||
item.spread_radius,
|
||||
item.border_radius.to_border_radius(),
|
||||
item.border_radius,
|
||||
item.clip_mode,
|
||||
);
|
||||
},
|
||||
@ -303,11 +285,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
||||
stacking_context.transform_style,
|
||||
perspective,
|
||||
stacking_context.mix_blend_mode,
|
||||
stacking_context
|
||||
.filters
|
||||
.iter()
|
||||
.map(ToLayout::to_layout)
|
||||
.collect(),
|
||||
stacking_context.filters.clone(),
|
||||
);
|
||||
},
|
||||
DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(),
|
||||
@ -328,7 +306,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
|
||||
.define_scroll_frame_with_parent(
|
||||
node.id,
|
||||
parent_id,
|
||||
node.content_rect.to_layout(),
|
||||
node.content_rect,
|
||||
node.clip.main.to_layout(),
|
||||
node.clip.get_complex_clips(),
|
||||
None,
|
||||
|
@ -714,10 +714,13 @@ impl MallocSizeOf for url::Host {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::BorderRadius);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::BorderStyle);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::BorderWidths);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::BoxShadowClipMode);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::ClipAndScrollInfo);
|
||||
@ -728,8 +731,12 @@ malloc_size_of_is_0!(webrender_api::ColorF);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::ExtendMode);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::FilterOp);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::GradientStop);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::ImageBorder);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::ImageKey);
|
||||
#[cfg(feature = "servo")]
|
||||
malloc_size_of_is_0!(webrender_api::ImageRendering);
|
||||
|
@ -54,6 +54,7 @@ use dom::htmloptionelement::HTMLOptionElement;
|
||||
use dom::htmloutputelement::HTMLOutputElement;
|
||||
use dom::htmlparagraphelement::HTMLParagraphElement;
|
||||
use dom::htmlparamelement::HTMLParamElement;
|
||||
use dom::htmlpictureelement::HTMLPictureElement;
|
||||
use dom::htmlpreelement::HTMLPreElement;
|
||||
use dom::htmlprogresselement::HTMLProgressElement;
|
||||
use dom::htmlquoteelement::HTMLQuoteElement;
|
||||
@ -309,6 +310,7 @@ pub fn create_native_html_element(
|
||||
local_name!("output") => make!(HTMLOutputElement),
|
||||
local_name!("p") => make!(HTMLParagraphElement),
|
||||
local_name!("param") => make!(HTMLParamElement),
|
||||
local_name!("picture") => make!(HTMLPictureElement),
|
||||
local_name!("plaintext") => make!(HTMLPreElement),
|
||||
local_name!("pre") => make!(HTMLPreElement),
|
||||
local_name!("progress") => make!(HTMLProgressElement),
|
||||
|
36
servo/components/script/dom/htmlpictureelement.rs
Normal file
36
servo/components/script/dom/htmlpictureelement.rs
Normal file
@ -0,0 +1,36 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::bindings::codegen::Bindings::HTMLPictureElementBinding;
|
||||
use dom::bindings::root::DomRoot;
|
||||
use dom::document::Document;
|
||||
use dom::htmlelement::HTMLElement;
|
||||
use dom::node::Node;
|
||||
use dom_struct::dom_struct;
|
||||
use html5ever::{LocalName, Prefix};
|
||||
|
||||
#[dom_struct]
|
||||
pub struct HTMLPictureElement {
|
||||
htmlelement: HTMLElement,
|
||||
}
|
||||
|
||||
impl HTMLPictureElement {
|
||||
fn new_inherited(local_name: LocalName,
|
||||
prefix: Option<Prefix>,
|
||||
document: &Document) -> HTMLPictureElement {
|
||||
HTMLPictureElement {
|
||||
htmlelement:
|
||||
HTMLElement::new_inherited(local_name, prefix, document)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn new(local_name: LocalName,
|
||||
prefix: Option<Prefix>,
|
||||
document: &Document) -> DomRoot<HTMLPictureElement> {
|
||||
Node::reflect_node(Box::new(HTMLPictureElement::new_inherited(local_name, prefix, document)),
|
||||
document,
|
||||
HTMLPictureElementBinding::Wrap)
|
||||
}
|
||||
}
|
@ -348,6 +348,7 @@ pub mod htmloptionscollection;
|
||||
pub mod htmloutputelement;
|
||||
pub mod htmlparagraphelement;
|
||||
pub mod htmlparamelement;
|
||||
pub mod htmlpictureelement;
|
||||
pub mod htmlpreelement;
|
||||
pub mod htmlprogresselement;
|
||||
pub mod htmlquoteelement;
|
||||
|
@ -0,0 +1,7 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#htmlpictureelement
|
||||
[HTMLConstructor]
|
||||
interface HTMLPictureElement : HTMLElement {};
|
@ -132,3 +132,14 @@ yaml:
|
||||
- '**/*.yml'
|
||||
- '**/*.yaml'
|
||||
- '**/.ymllint'
|
||||
|
||||
shellcheck:
|
||||
description: shellcheck run over the gecko codebase
|
||||
platform: lint/opt
|
||||
treeherder:
|
||||
symbol: shell
|
||||
run:
|
||||
mach: lint -l shellcheck -f treeherder
|
||||
when:
|
||||
files-changed:
|
||||
- '**/*.sh'
|
||||
|
@ -62,6 +62,7 @@ linux64-nightly/opt:
|
||||
- opt-only-tests
|
||||
- desktop-screenshot-capture
|
||||
- awsy
|
||||
- linux-talos-profiling
|
||||
linux64-devedition/opt:
|
||||
build-platform: linux64-devedition-nightly/opt
|
||||
test-sets:
|
||||
|
@ -135,6 +135,22 @@ linux-qr-tests:
|
||||
- reftest
|
||||
- xpcshell
|
||||
|
||||
linux-talos-profiling:
|
||||
- talos-chrome-profiling
|
||||
- talos-dromaeojs-profiling
|
||||
- talos-g1-profiling
|
||||
- talos-g2-profiling
|
||||
- talos-g3-profiling
|
||||
- talos-g4-profiling
|
||||
- talos-g5-profiling
|
||||
- talos-other-profiling
|
||||
- talos-perf-reftest-profiling
|
||||
- talos-perf-reftest-singletons-profiling
|
||||
- talos-speedometer-profiling
|
||||
- talos-svgr-profiling
|
||||
- talos-tp5o-profiling
|
||||
- talos-tp6-profiling
|
||||
|
||||
windows-qr-tests:
|
||||
- crashtest
|
||||
- mochitest-gpu
|
||||
@ -297,6 +313,7 @@ macosx64-talos-profiling:
|
||||
- talos-perf-reftest-singletons-profiling
|
||||
- talos-speedometer-profiling
|
||||
- talos-svgr-profiling
|
||||
- talos-tp5o-profiling
|
||||
- talos-tp6-profiling
|
||||
|
||||
linux32-tests:
|
||||
|
@ -47,7 +47,7 @@ for locale in $locales; do
|
||||
done
|
||||
|
||||
# Generate snapcraft manifest
|
||||
sed -e "s/@VERSION@/${VERSION}/g" -e "s/@BUILD_NUMBER@/${BUILD_NUMBER}/g" snapcraft.yaml.in > ${WORKSPACE}/snapcraft.yaml
|
||||
sed -e "s/@VERSION@/${VERSION}/g" -e "s/@BUILD_NUMBER@/${BUILD_NUMBER}/g" snapcraft.yaml.in > "${WORKSPACE}/snapcraft.yaml"
|
||||
cd "${WORKSPACE}"
|
||||
snapcraft
|
||||
|
||||
|
@ -23,6 +23,8 @@ test -n "$IMAGE_NAME" || raise_error "IMAGE_NAME must be provided."
|
||||
# proxy socket that the worker user can use.
|
||||
export DOCKER_SOCKET=/var/run/docker.proxy
|
||||
socat UNIX-LISTEN:$DOCKER_SOCKET,fork,group=worker,mode=0775 UNIX-CLIENT:/var/run/docker.sock </dev/null &
|
||||
# Disable check until new version is tested.
|
||||
# shellcheck disable=SC2064
|
||||
trap "kill $!" EXIT
|
||||
|
||||
LOAD_COMMAND=
|
||||
|
@ -5,9 +5,7 @@
|
||||
|
||||
cd /setup || exit
|
||||
|
||||
# shellcheck source=taskcluster/docker/recipes/common.sh
|
||||
. /setup/common.sh
|
||||
# shellcheck source=taskcluster/docker/recipes/install-mercurial.sh
|
||||
. /setup/install-mercurial.sh
|
||||
|
||||
rm -rf /setup
|
||||
|
@ -91,16 +91,13 @@ apt-get install -y -f "${apt_packages[@]}"
|
||||
|
||||
dpkg-reconfigure locales
|
||||
|
||||
# shellcheck source=taskcluster/docker/recipes/common.sh
|
||||
. /setup/common.sh
|
||||
# shellcheck source=taskcluster/docker/recipes/install-mercurial.sh
|
||||
. /setup/install-mercurial.sh
|
||||
|
||||
pip install --upgrade pip
|
||||
|
||||
pip install virtualenv
|
||||
|
||||
# shellcheck source=taskcluster/docker/recipes/install-node.sh
|
||||
. /setup/install-node.sh
|
||||
|
||||
# Install custom-built Debian packages. These come from a set of repositories
|
||||
|
@ -3,6 +3,10 @@
|
||||
"chromez-e10s": {
|
||||
"tests": ["tresize"]
|
||||
},
|
||||
"chromez-profiling-e10s": {
|
||||
"talos_options": ["--geckoProfile"],
|
||||
"tests": ["tresize"]
|
||||
},
|
||||
"chromez-stylo-disabled-e10s": {
|
||||
"talos_options": ["--disable-stylo"],
|
||||
"tests": ["tresize"]
|
||||
@ -10,6 +14,10 @@
|
||||
"dromaeojs-e10s": {
|
||||
"tests": ["dromaeo_css", "kraken"]
|
||||
},
|
||||
"dromaeojs-profiling-e10s": {
|
||||
"talos_options": ["--geckoProfile"],
|
||||
"tests": ["dromaeo_css", "kraken"]
|
||||
},
|
||||
"dromaeojs-stylo-disabled-e10s": {
|
||||
"talos_options": ["--disable-stylo"],
|
||||
"tests": ["dromaeo_css", "kraken"]
|
||||
@ -17,6 +25,10 @@
|
||||
"other-e10s": {
|
||||
"tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup"]
|
||||
},
|
||||
"other-profiling-e10s": {
|
||||
"talos_options": ["--geckoProfile"],
|
||||
"tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup"]
|
||||
},
|
||||
"other-stylo-disabled-e10s": {
|
||||
"talos_options": ["--disable-stylo"],
|
||||
"tests": ["a11yr", "ts_paint", "tpaint", "sessionrestore", "sessionrestore_many_windows", "sessionrestore_no_auto_restore", "tabpaint", "cpstartup"]
|
||||
@ -25,6 +37,11 @@
|
||||
"tests": ["tp5o_scroll", "glterrain"],
|
||||
"pagesets_name": "tp5n.zip"
|
||||
},
|
||||
"g1-profiling-e10s": {
|
||||
"tests": ["tp5o_scroll", "glterrain"],
|
||||
"talos_options": ["--geckoProfile"],
|
||||
"pagesets_name": "tp5n.zip"
|
||||
},
|
||||
"g1-stylo-disabled-e10s": {
|
||||
"tests": ["tp5o_scroll", "glterrain"],
|
||||
"talos_options": ["--disable-stylo"],
|
||||
@ -34,6 +51,11 @@
|
||||
"tests": ["damp", "tps"],
|
||||
"pagesets_name": "tp5n.zip"
|
||||
},
|
||||
"g2-profiling-e10s": {
|
||||
"tests": ["damp", "tps"],
|
||||
"talos_options": ["--geckoProfile"],
|
||||
"pagesets_name": "tp5n.zip"
|
||||
},
|
||||
"g2-stylo-disabled-e10s": {
|
||||
"tests": ["damp", "tps"],
|
||||
"talos_options": ["--disable-stylo"],
|
||||
@ -42,6 +64,10 @@
|
||||
"g3-e10s": {
|
||||
"tests": ["dromaeo_dom"]
|
||||
},
|
||||
"g3-profiling-e10s": {
|
||||
"tests": ["dromaeo_dom"],
|
||||
"talos_options": ["--geckoProfile"]
|
||||
},
|
||||
"g3-stylo-disabled-e10s": {
|
||||
"tests": ["dromaeo_dom"],
|
||||
"talos_options": ["--disable-stylo"]
|
||||
@ -49,6 +75,10 @@
|
||||
"g4-e10s": {
|
||||
"tests": ["basic_compositor_video", "glvideo", "displaylist_mutate", "rasterflood_svg", "rasterflood_gradient"]
|
||||
},
|
||||
"g4-profiling-e10s": {
|
||||
"tests": ["basic_compositor_video", "glvideo"],
|
||||
"talos_options": ["--geckoProfile"]
|
||||
},
|
||||
"g4-stylo-disabled-e10s": {
|
||||
"tests": ["basic_compositor_video", "glvideo"],
|
||||
"talos_options": ["--disable-stylo"]
|
||||
@ -57,6 +87,11 @@
|
||||
"tests": ["ts_paint_webext", "tp5o_webext"],
|
||||
"pagesets_name": "tp5n.zip"
|
||||
},
|
||||
"g5-profiling-e10s": {
|
||||
"tests": ["ts_paint_webext", "tp5o_webext"],
|
||||
"talos_options": ["--geckoProfile"],
|
||||
"pagesets_name": "tp5n.zip"
|
||||
},
|
||||
"g5-stylo-disabled-e10s": {
|
||||
"tests": ["ts_paint_webext", "tp5o_webext"],
|
||||
"talos_options": ["--disable-stylo"],
|
||||
@ -65,6 +100,10 @@
|
||||
"svgr-e10s": {
|
||||
"tests": ["tsvgx", "tsvgr_opacity", "tart", "tscrollx", "tsvg_static"]
|
||||
},
|
||||
"svgr-profiling-e10s": {
|
||||
"tests": ["tsvgx", "tsvgr_opacity", "tart", "tscrollx", "tsvg_static"],
|
||||
"talos_options": ["--geckoProfile"]
|
||||
},
|
||||
"svgr-stylo-disabled-e10s": {
|
||||
"tests": ["tsvgx", "tsvgr_opacity", "tart", "tscrollx", "tsvg_static"],
|
||||
"talos_options": ["--disable-stylo"]
|
||||
@ -72,6 +111,10 @@
|
||||
"perf-reftest-e10s": {
|
||||
"tests": ["perf_reftest"]
|
||||
},
|
||||
"perf-reftest-profiling-e10s": {
|
||||
"tests": ["perf_reftest"],
|
||||
"talos_options": ["--geckoProfile"]
|
||||
},
|
||||
"perf-reftest-stylo-disabled-e10s": {
|
||||
"tests": ["perf_reftest"],
|
||||
"talos_options": ["--disable-stylo"]
|
||||
@ -79,6 +122,10 @@
|
||||
"perf-reftest-singletons-e10s": {
|
||||
"tests": ["perf_reftest_singletons"]
|
||||
},
|
||||
"perf-reftest-singletons-profiling-e10s": {
|
||||
"tests": ["perf_reftest_singletons"],
|
||||
"talos_options": ["--geckoProfile"]
|
||||
},
|
||||
"perf-reftest-singletons-stylo-disabled-e10s": {
|
||||
"tests": ["perf_reftest_singletons"],
|
||||
"talos_options": ["--disable-stylo"]
|
||||
@ -86,6 +133,10 @@
|
||||
"speedometer-e10s": {
|
||||
"tests": ["speedometer", "stylebench"]
|
||||
},
|
||||
"speedometer-profiling-e10s": {
|
||||
"talos_options": ["--geckoProfile"],
|
||||
"tests": ["speedometer"]
|
||||
},
|
||||
"speedometer-stylo-disabled-e10s": {
|
||||
"talos_options": ["--disable-stylo"],
|
||||
"tests": ["speedometer"]
|
||||
@ -94,6 +145,11 @@
|
||||
"tests": ["tp5o"],
|
||||
"pagesets_name": "tp5n.zip"
|
||||
},
|
||||
"tp5o-profiling-e10s": {
|
||||
"tests": ["tp5o"],
|
||||
"pagesets_name": "tp5n.zip",
|
||||
"talos_options": ["--geckoProfile"]
|
||||
},
|
||||
"tp5o-stylo-disabled-e10s": {
|
||||
"tests": ["tp5o"],
|
||||
"pagesets_name": "tp5n.zip",
|
||||
@ -127,6 +183,18 @@
|
||||
"--firstNonBlankPaint"
|
||||
]
|
||||
},
|
||||
"tp6-profiling-e10s": {
|
||||
"tests": ["tp6_google", "tp6_youtube", "tp6_amazon", "tp6_facebook"],
|
||||
"mitmproxy_release_bin_osx": "mitmproxy-2.0.2-osx.tar.gz",
|
||||
"mitmproxy_release_bin_linux64": "mitmproxy-2.0.2-linux.tar.gz",
|
||||
"mitmproxy_recording_set": "mitmproxy-recording-set-win10.zip",
|
||||
"talos_options": [
|
||||
"--geckoProfile",
|
||||
"--mitmproxy",
|
||||
"mitmproxy-recording-google.mp mitmproxy-recording-youtube.mp mitmproxy-recording-amazon.mp mitmproxy-recording-facebook.mp",
|
||||
"--firstNonBlankPaint"
|
||||
]
|
||||
},
|
||||
"tp6-stylo-disabled-e10s": {
|
||||
"tests": ["tp6_google", "tp6_youtube", "tp6_amazon", "tp6_facebook"],
|
||||
"mitmproxy_release_bin_osx": "mitmproxy-2.0.2-osx.tar.gz",
|
||||
|
16
toolkit/library/gtest/rust/Cargo.lock
generated
16
toolkit/library/gtest/rust/Cargo.lock
generated
@ -45,7 +45,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "audioipc"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -68,7 +68,7 @@ dependencies = [
|
||||
name = "audioipc-client"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"audioipc 0.2.0",
|
||||
"audioipc 0.2.1",
|
||||
"cubeb-backend 0.2.0",
|
||||
"cubeb-core 0.1.0",
|
||||
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -81,11 +81,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "audioipc-server"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"audioipc 0.2.0",
|
||||
"audioipc 0.2.1",
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cubeb 0.3.1",
|
||||
"cubeb 0.3.2",
|
||||
"cubeb-core 0.1.0",
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -316,7 +316,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cubeb"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
dependencies = [
|
||||
"cubeb-core 0.1.0",
|
||||
"libcubeb-sys 0.1.0",
|
||||
@ -582,9 +582,9 @@ name = "gkrust-shared"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"audioipc-client 0.2.0",
|
||||
"audioipc-server 0.2.0",
|
||||
"audioipc-server 0.2.1",
|
||||
"cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cubeb 0.3.1",
|
||||
"cubeb 0.3.2",
|
||||
"cubeb-backend 0.2.0",
|
||||
"cubeb-core 0.1.0",
|
||||
"cubeb-pulse 0.0.2",
|
||||
|
16
toolkit/library/rust/Cargo.lock
generated
16
toolkit/library/rust/Cargo.lock
generated
@ -45,7 +45,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "audioipc"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -68,7 +68,7 @@ dependencies = [
|
||||
name = "audioipc-client"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"audioipc 0.2.0",
|
||||
"audioipc 0.2.1",
|
||||
"cubeb-backend 0.2.0",
|
||||
"cubeb-core 0.1.0",
|
||||
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -81,11 +81,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "audioipc-server"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"audioipc 0.2.0",
|
||||
"audioipc 0.2.1",
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cubeb 0.3.1",
|
||||
"cubeb 0.3.2",
|
||||
"cubeb-core 0.1.0",
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -316,7 +316,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cubeb"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
dependencies = [
|
||||
"cubeb-core 0.1.0",
|
||||
"libcubeb-sys 0.1.0",
|
||||
@ -580,9 +580,9 @@ name = "gkrust-shared"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"audioipc-client 0.2.0",
|
||||
"audioipc-server 0.2.0",
|
||||
"audioipc-server 0.2.1",
|
||||
"cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cubeb 0.3.1",
|
||||
"cubeb 0.3.2",
|
||||
"cubeb-backend 0.2.0",
|
||||
"cubeb-core 0.1.0",
|
||||
"cubeb-pulse 0.0.2",
|
||||
|
@ -19,7 +19,6 @@
|
||||
fill-opacity: 0.2;
|
||||
}
|
||||
|
||||
image.close-icon,
|
||||
.close-icon > .button-icon,
|
||||
.close-icon > .button-box > .button-icon,
|
||||
.close-icon > .toolbarbutton-icon {
|
||||
|
@ -155,7 +155,7 @@ def lint(paths, config, **lintargs):
|
||||
|
||||
files = find_shell_scripts(config, paths)
|
||||
|
||||
base_command = [binary, '-x', '-f', 'json']
|
||||
base_command = [binary, '-f', 'json']
|
||||
if config.get('excludecodes'):
|
||||
base_command.extend(['-e', ','.join(config.get('excludecodes'))])
|
||||
|
||||
|
@ -2,11 +2,11 @@
|
||||
shellcheck:
|
||||
description: Shell script linter
|
||||
include:
|
||||
- taskcluster/docker/funsize-update-generator/scripts/
|
||||
- taskcluster/docker/
|
||||
exclude: []
|
||||
# 1090: https://github.com/koalaman/shellcheck/wiki/SC1090
|
||||
# 'Can't follow a non-constant source'
|
||||
extensions: ['sh']
|
||||
excludecodes: ['1090']
|
||||
excludecodes: ['1090', '1091']
|
||||
type: external
|
||||
payload: shell:lint
|
||||
|
@ -6887,7 +6887,13 @@ nsWindow::GetCSDSupportLevel() {
|
||||
} else if (strstr(currentDesktop, "LXQt") != nullptr) {
|
||||
sCSDSupportLevel = CSD_SUPPORT_FULL;
|
||||
} else {
|
||||
// Release or beta builds are not supposed to be broken
|
||||
// so disable titlebar rendering on untested/unknown systems.
|
||||
#if defined(RELEASE_OR_BETA)
|
||||
sCSDSupportLevel = CSD_SUPPORT_NONE;
|
||||
#else
|
||||
sCSDSupportLevel = CSD_SUPPORT_FLAT;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
sCSDSupportLevel = CSD_SUPPORT_NONE;
|
||||
|
Loading…
Reference in New Issue
Block a user