This commit is contained in:
Ryan VanderMeulen 2015-08-11 10:59:41 -04:00
Родитель 6c3414e564 c0f04387c3
Коммит e2d3873930
193 изменённых файлов: 2157 добавлений и 1572 удалений

Просмотреть файл

@ -102,7 +102,6 @@ pref("network.predictor.max-db-size", 2097152); // bytes
pref("network.predictor.preserve", 50); // percentage of predictor data to keep when cleaning up
/* session history */
pref("browser.sessionhistory.max_total_viewers", 1);
pref("browser.sessionhistory.max_entries", 50);
pref("browser.sessionhistory.contentViewerTimeout", 360);

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="36b945cb98a4e9009d57b8c20a720fc1a5905452"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "9a8880a95ee4a4aea7895d4e2bcab31bc49ea281",
"git_revision": "2d7f369fd923b6df3b00d76844c413c1202c04ba",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "aa0cf409f343d966a552839ab5d3bc85d1ebdda9",
"revision": "5c86f4cf87b91d2bfe9a3e49aa8706b452e4f97e",
"repo_path": "integration/gaia-central"
}

Просмотреть файл

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="36b945cb98a4e9009d57b8c20a720fc1a5905452"/>

Просмотреть файл

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9a8880a95ee4a4aea7895d4e2bcab31bc49ea281"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2d7f369fd923b6df3b00d76844c413c1202c04ba"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

Просмотреть файл

@ -5369,11 +5369,27 @@
canvas.mozOpaque = true;
canvas.width = 160 * scale;
canvas.height = 90 * scale;
if (!gMultiProcessBrowser) {
// Bug 863512 - Make page thumbnails work in e10s
let toDrag;
if (gMultiProcessBrowser) {
// Create a panel to use it in setDragImage
// which will tell xul to render a panel that follows
// the pointer while a dnd session is on.
var panel = document.createElement("panel");
panel.setAttribute("type", "drag");
panel.appendChild(canvas);
document.documentElement.appendChild(panel);
// PageThumb is async with e10s but that's fine
// since we can update the panel during the dnd.
PageThumbs.captureToCanvas(browser, canvas);
toDrag = panel;
} else {
// For the non e10s case we can just use PageThumbs
// sync. No need for xul magic, the native dnd will
// be fine, so let's use the canvas for setDragImage.
PageThumbs.captureToCanvas(browser, canvas);
toDrag = canvas;
}
dt.setDragImage(canvas, -16 * scale, -16 * scale);
dt.setDragImage(toDrag, -16 * scale, -16 * scale);
// _dragData.offsetX/Y give the coordinates that the mouse should be
// positioned relative to the corner of the new window created upon
@ -5738,7 +5754,7 @@
xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected"
class="tab-text tab-label"
role="presentation"/>
<xul:image xbl:inherits="soundplaying,pinned,muted"
<xul:image xbl:inherits="soundplaying,pinned,muted,visuallyselected"
anonid="soundplaying-icon"
class="tab-icon-sound"
role="presentation"/>

Просмотреть файл

@ -158,7 +158,7 @@ function* test_browser_swapping(tab, browser) {
yield test_swapped_browser(tab, newBrowser, true)
// FIXME: this is needed to work around bug 1190903.
yield new Promise(resolve => setTimeout(resolve, 1000));
yield new Promise(resolve => setTimeout(resolve, 3000));
// Now, test swapping with a muted but not playing tab.
// Note that the tab remains muted, so we only need to pause playback.
@ -262,6 +262,7 @@ add_task(function*() {
});
});
requestLongerTimeout(2);
add_task(function* test_page() {
yield BrowserTestUtils.withNewTab({
gBrowser,

Просмотреть файл

@ -9,6 +9,7 @@ this.EXPORTED_SYMBOLS = ["SessionStorage"];
const Cu = Components.utils;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/BrowserUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -72,9 +73,9 @@ let SessionStorageInternal = {
return;
}
// Get the root domain of the current history entry
// and use that as a key for the per-host storage data.
let origin = principal.jarPrefix + principal.originNoSuffix;
// Get the origin of the current history entry
// and use that as a key for the per-principal storage data.
let origin = principal.origin;
if (visitedOrigins.has(origin)) {
// Don't read a host twice.
return;
@ -102,10 +103,9 @@ let SessionStorageInternal = {
* {"example.com": {"key": "value", "my_number": 123}}
*/
restore: function (aDocShell, aStorageData) {
for (let host of Object.keys(aStorageData)) {
let data = aStorageData[host];
let uri = Services.io.newURI(host, null, null);
let principal = Services.scriptSecurityManager.getDocShellCodebasePrincipal(uri, aDocShell);
for (let origin of Object.keys(aStorageData)) {
let data = aStorageData[origin];
let principal = BrowserUtils.principalFromOrigin(origin);
let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);

Просмотреть файл

@ -4,82 +4,41 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<style>
use:not(:target) {
.icon:not(:target) {
display: none;
}
.icon {
fill: #4d4d4d;
fill: #262626;
}
.icon.hover {
fill: #333333;
}
.icon.pressed {
fill: #000;
}
.icon.dark {
fill: #ccc;
}
.icon.dark.hover {
fill: #b2b2b2;
}
.icon.dark.pressed {
.icon > .outline {
fill: #fff;
}
.muted {
opacity: .7;
stroke: #4d4d4d;
stroke-width: 0;
.icon.white {
fill: #fff;
}
.muted.hover {
opacity: .85;
stroke: #333333;
}
.muted.pressed {
opacity: 1;
stroke: #000;
}
.muted.dark {
stroke: #ccc;
}
.muted.dark.hover {
stroke: #b2b2b2;
}
.muted.dark.pressed {
stroke: #fff;
.icon.white > .outline {
fill: #000;
fill-opacity: .5;
}
</style>
<defs>
<clipPath id="clip-wave">
<path d="M 11,7 l 3,-8 l 2,0 l 0,18 l -2,0 l -3,-8 z" />
</clipPath>
<mask id="disabled-cutout">
<rect width="16" height="16" fill="#fff" />
<line x1="4" y1="14" x2="14" y2="4" stroke="#000" stroke-width="2" />
</mask>
<g id="shape-tab-audio">
<rect x="3" y="6" width="5" height="4" rx="1" ry="1" />
<polygon points="5.5,6.5 9,3 9,13 5.5,9.5" />
<path d="M 10,6.5 a 1.5 1.5 0 0,1 0,3 z" />
<path d="M 10,4 a 4 4 0 0,1 0,8 l 0,-1 a 3 3 0 0,0 0,-6 z" clip-path="url(#clip-wave)" />
</g>
<g id="shape-tab-audio-muted">
<g mask="url(#disabled-cutout)">
<rect x="4" y="6" width="5" height="4" rx="1" ry="1" />
<polygon points="6.5,6.5 10,3 10,13 6.5,9.5" />
</g>
<line x1="3" y1="14" x2="13" y2="4" stroke-width="1.5" />
</g>
</defs>
<use id="tab-audio" class="icon" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-hover" class="icon hover" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-pressed" class="icon pressed" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-muted" class="icon muted" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-muted-hover" class="icon muted hover" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-muted-pressed" class="icon muted pressed" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-dark" class="icon dark" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-dark-hover" class="icon hover dark" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-dark-pressed" class="icon pressed dark" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-muted-dark" class="icon muted dark" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-muted-dark-hover" class="icon muted hover dark" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-muted-dark-pressed" class="icon muted pressed dark" xlink:href="#shape-tab-audio-muted" />
<g id="tab-audio" class="icon">
<path class="outline" d="M12.4,3.6l-1-0.6l-0.9,2.5H10V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5H4C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.6l3.6,3.6 c0.3,0.3,0.9,0.1,0.9-0.4v-3.7h0.5l0.9,2.5l1-0.6C14,11.5,15,9.8,15,8S14,4.5,12.4,3.6z M9,13l-3-3H4c-0.6,0-1-0.4-1-1V7 c0-0.6,0.4-1,1-1h2l3-3V13z M10,9.5v-3c0.8,0,1.5,0.7,1.5,1.5S10.8,9.5,10,9.5z M11.9,11.5l-0.4-0.9C12.4,10,13,9.1,13,8 s-0.6-2-1.4-2.5l0.3-1C13.2,5.2,14,6.5,14,8S13.2,10.8,11.9,11.5z"/>
<path d="M4,6C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h2l3,3V3L6,6H4z M10,6.5v3c0.8,0,1.5-0.7,1.5-1.5S10.8,6.5,10,6.5z M11.9,4.5 l-0.4,0.9C12.4,6,13,6.9,13,8s-0.6,2-1.4,2.5l0.4,0.9c1.2-0.7,2.1-2,2.1-3.5S13.2,5.2,11.9,4.5z"/>
</g>
<g id="tab-audio-muted" class="icon">
<path class="outline" d="M5.6,5H4C2.9,5,2,5.9,2,7v2c0,0.7,0.3,1.3,0.9,1.7l-1.8,1.8l2.5,2.5l3-3l2.6,2.6c0.3,0.3,0.9,0.1,0.9-0.4V8.5l3.9-3.9 l-2.5-2.5L10,3.5V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5z"/>
<path d="M11.5,3.5L9,5.9V3L6,6H4C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h0.9l-2.5,2.5l1.1,1.1l9-9L11.5,3.5z M9,13V9.7l-1.7,1.7L9,13z"/>
</g>
<g id="tab-audio-white" class="icon white">
<path class="outline" d="M12.4,3.6l-1-0.6l-0.9,2.5H10V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5H4C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.6l3.6,3.6 c0.3,0.3,0.9,0.1,0.9-0.4v-3.7h0.5l0.9,2.5l1-0.6C14,11.5,15,9.8,15,8S14,4.5,12.4,3.6z M9,13l-3-3H4c-0.6,0-1-0.4-1-1V7 c0-0.6,0.4-1,1-1h2l3-3V13z M10,9.5v-3c0.8,0,1.5,0.7,1.5,1.5S10.8,9.5,10,9.5z M11.9,11.5l-0.4-0.9C12.4,10,13,9.1,13,8 s-0.6-2-1.4-2.5l0.3-1C13.2,5.2,14,6.5,14,8S13.2,10.8,11.9,11.5z"/>
<path d="M4,6C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h2l3,3V3L6,6H4z M10,6.5v3c0.8,0,1.5-0.7,1.5-1.5S10.8,6.5,10,6.5z M11.9,4.5 l-0.4,0.9C12.4,6,13,6.9,13,8s-0.6,2-1.4,2.5l0.4,0.9c1.2-0.7,2.1-2,2.1-3.5S13.2,5.2,11.9,4.5z"/>
</g>
<g id="tab-audio-white-muted" class="icon white">
<path class="outline" d="M5.6,5H4C2.9,5,2,5.9,2,7v2c0,0.7,0.3,1.3,0.9,1.7l-1.8,1.8l2.5,2.5l3-3l2.6,2.6c0.3,0.3,0.9,0.1,0.9-0.4V8.5l3.9-3.9 l-2.5-2.5L10,3.5V1.8c0-0.4-0.5-0.7-0.9-0.4L5.6,5z"/>
<path d="M11.5,3.5L9,5.9V3L6,6H4C3.4,6,3,6.4,3,7v2c0,0.6,0.4,1,1,1h0.9l-2.5,2.5l1.1,1.1l9-9L11.5,3.5z M9,13V9.7l-1.7,1.7L9,13z"/>
</g>
</svg>

До

Ширина:  |  Высота:  |  Размер: 3.0 KiB

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

Просмотреть файл

@ -4,83 +4,84 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
<style>
use:not(:target) {
.icon:not(:target) {
display: none;
}
.icon {
fill: #666;
fill: #333;
opacity: .75;
}
.icon.hover {
fill: #4d4d4d;
fill: #1a1a1a;
opacity: .85;
}
.icon.pressed {
fill: #000;
fill: #0d0d0d;
opacity: .95;
}
.icon.dark {
fill: #999;
}
.icon.dark.hover {
fill: #b2b2b2;
}
.icon.dark.pressed {
.icon.white {
fill: #fff;
}
.muted {
opacity: .7;
stroke: #666;
stroke-width: 0;
.icon.white.hover {
opacity: .9;
}
.muted.hover {
opacity: .85;
stroke: #4d4d4d;
}
.muted.pressed {
.icon.white.pressed {
opacity: 1;
stroke: #000;
}
.muted.dark {
stroke: #999;
.icon.white > .outline {
fill: #000;
fill-opacity: .5;
}
.muted.dark.hover {
stroke: #b2b2b2;
}
.muted.dark.pressed {
stroke: #fff;
.icon.backgroundTab,
.icon.backgroundTab.hover,
.icon.backgroundTab.pressed {
fill: -moz-MenuBarText;
}
</style>
<defs>
<clipPath id="clip-wave">
<path d="M 10,7 l 3,-8 l 2,0 l 0,18 l -2,0 l -3,-8 z" />
</clipPath>
<mask id="disabled-cutout">
<rect width="16" height="16" fill="#fff" />
<line x1="4" y1="14" x2="14" y2="4" stroke="#000" stroke-width="2" />
</mask>
<g id="shape-tab-audio">
<rect x="2" y="5" width="6" height="6" rx="2" ry="2" />
<polygon points="4,6 9,2 9,14 4,10" />
<path d="M 10,7 a 1 1 0 0,1 0,2 z" />
<path d="M 10,5 a 3 3 0 0,1 0,6 l 0,-1 a 2 2 0 0,0 0,-4 z" clip-path="url(#clip-wave)" />
<path d="M 10,3 a 5 5 0 0,1 0,10 l 0,-1 a 4 4 0 0,0 0,-8 z" clip-path="url(#clip-wave)" />
</g>
<g id="shape-tab-audio-muted">
<g mask="url(#disabled-cutout)">
<rect x="3" y="5" width="6" height="6" rx="2" ry="2" />
<polygon points="5,6 10,2 10,14 5,10" />
</g>
<line x1="2" y1="13" x2="14" y2="3" stroke-width="1.5" />
</g>
</defs>
<use id="tab-audio" class="icon" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-hover" class="icon hover" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-pressed" class="icon pressed" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-muted" class="icon muted" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-muted-hover" class="icon muted hover" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-muted-pressed" class="icon muted pressed" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-dark" class="icon dark" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-dark-hover" class="icon hover dark" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-dark-pressed" class="icon pressed dark" xlink:href="#shape-tab-audio"/>
<use id="tab-audio-muted-dark" class="icon muted dark" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-muted-dark-hover" class="icon muted hover dark" xlink:href="#shape-tab-audio-muted" />
<use id="tab-audio-muted-dark-pressed" class="icon muted pressed dark" xlink:href="#shape-tab-audio-muted" />
<path id="tab-audio" class="icon" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
<path id="tab-audio-hover" class="icon hover" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
<path id="tab-audio-pressed" class="icon pressed" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
<path id="tab-audio-muted" class="icon" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
<path id="tab-audio-muted-hover" class="icon hover" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
<path id="tab-audio-muted-pressed" class="icon pressed" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
<path id="tab-audio-backgroundTab" class="icon backgroundTab" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
<path id="tab-audio-backgroundTab-hover" class="icon backgroundTab hover" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
<path id="tab-audio-backgroundTab-pressed" class="icon backgroundTab pressed" d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
<path id="tab-audio-backgroundTab-muted" class="icon backgroundTab" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
<path id="tab-audio-backgroundTab-muted-hover" class="icon backgroundTab hover" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
<path id="tab-audio-backgroundTab-muted-pressed" class="icon backgroundTab pressed" d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
<g id="tab-audio-white" class="icon white">
<path class="outline" d="M9,2v12l-3.8-3H4c-1.1,0-2-0.9-2-2V7c0-1.1,0.9-2,2-2h1.2L9,2 M11.4,3.2C13.5,3.8,15,5.7,15,8 s-1.5,4.2-3.5,4.7l-0.4-0.9c1.7-0.4,2.9-2,2.9-3.8s-1.2-3.4-3-3.9L11.4,3.2 M10.7,5.1C12,5.4,13,6.6,13,8s-1,2.6-2.2,2.9L10.4,10 C11.3,9.8,12,9,12,8s-0.7-1.8-1.6-2L10.7,5.1 M10,7c0.6,0,1,0.4,1,1s-0.4,1-1,1V7 M10-0.1L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2 c0,1.7,1.3,3,3,3h0.9l3.5,2.8l1.6,1.3V14v-2.2l0.2,0.4l0.4,0.9l0.3,0.8l0.8-0.2C14.2,13,16,10.7,16,8c0-2.7-1.7-5-4.3-5.8L10.8,2 l-0.4,0.8l-0.4,0.9L10,3.9V2V-0.1L10-0.1z"/>
<path d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
</g>
<g id="tab-audio-white-hover" class="icon white hover">
<path class="outline" d="M9,2v12l-3.8-3H4c-1.1,0-2-0.9-2-2V7c0-1.1,0.9-2,2-2h1.2L9,2 M11.4,3.2C13.5,3.8,15,5.7,15,8 s-1.5,4.2-3.5,4.7l-0.4-0.9c1.7-0.4,2.9-2,2.9-3.8s-1.2-3.4-3-3.9L11.4,3.2 M10.7,5.1C12,5.4,13,6.6,13,8s-1,2.6-2.2,2.9L10.4,10 C11.3,9.8,12,9,12,8s-0.7-1.8-1.6-2L10.7,5.1 M10,7c0.6,0,1,0.4,1,1s-0.4,1-1,1V7 M10-0.1L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2 c0,1.7,1.3,3,3,3h0.9l3.5,2.8l1.6,1.3V14v-2.2l0.2,0.4l0.4,0.9l0.3,0.8l0.8-0.2C14.2,13,16,10.7,16,8c0-2.7-1.7-5-4.3-5.8L10.8,2 l-0.4,0.8l-0.4,0.9L10,3.9V2V-0.1L10-0.1z"/>
<path d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
</g>
<g id="tab-audio-white-pressed" class="icon white pressed">
<path class="outline" d="M9,2v12l-3.8-3H4c-1.1,0-2-0.9-2-2V7c0-1.1,0.9-2,2-2h1.2L9,2 M11.4,3.2C13.5,3.8,15,5.7,15,8 s-1.5,4.2-3.5,4.7l-0.4-0.9c1.7-0.4,2.9-2,2.9-3.8s-1.2-3.4-3-3.9L11.4,3.2 M10.7,5.1C12,5.4,13,6.6,13,8s-1,2.6-2.2,2.9L10.4,10 C11.3,9.8,12,9,12,8s-0.7-1.8-1.6-2L10.7,5.1 M10,7c0.6,0,1,0.4,1,1s-0.4,1-1,1V7 M10-0.1L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2 c0,1.7,1.3,3,3,3h0.9l3.5,2.8l1.6,1.3V14v-2.2l0.2,0.4l0.4,0.9l0.3,0.8l0.8-0.2C14.2,13,16,10.7,16,8c0-2.7-1.7-5-4.3-5.8L10.8,2 l-0.4,0.8l-0.4,0.9L10,3.9V2V-0.1L10-0.1z"/>
<path d="M4,5C2.9,5,2,5.9,2,7v2c0,1.1,0.9,2,2,2h1.2L9,14V2L5.2,5H4z M11,8c0-0.6-0.4-1-1-1v2C10.6,9,11,8.6,11,8z M13,8 c0-1.4-1-2.6-2.3-2.9L10.4,6C11.3,6.2,12,7,12,8s-0.7,1.8-1.6,2l0.4,0.9C12,10.6,13,9.4,13,8z M11.4,3.2l-0.4,0.9 C12.8,4.6,14,6.2,14,8s-1.2,3.4-2.9,3.8l0.4,0.9C13.5,12.2,15,10.3,15,8S13.5,3.8,11.4,3.2z"/>
</g>
<g id="tab-audio-muted-white" class="icon muted white">
<path class="outline" d="M9,2v4.3l3.5-2.9l0.9,1.2l-11,9l-1-1.2l1.9-1.5C2.6,10.6,2,9.9,2,9V7c0-1.1,0.9-2,2-2h1.2L9,2 M9,10v4l-2.5-2L9,10 M10-0.1 L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2c0,0.7,0.3,1.4,0.7,2l-0.8,0.7l-0.8,0.6l0.6,0.8l1,1.2L2.3,15l0.8-0.6l2.3-1.9l0.4,0.3l2.5,2 l1.6,1.3V14v-4V8.7l4.1-3.4l0.8-0.6l-0.6-0.8l-0.9-1.2L12.7,2l-0.8,0.6L10,4.2V2V-0.1L10-0.1z"/>
<path d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
</g>
<g id="tab-audio-muted-white-hover" class="icon muted white hover">
<path class="outline" d="M9,2v4.3l3.5-2.9l0.9,1.2l-11,9l-1-1.2l1.9-1.5C2.6,10.6,2,9.9,2,9V7c0-1.1,0.9-2,2-2h1.2L9,2 M9,10v4l-2.5-2L9,10 M10-0.1 L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2c0,0.7,0.3,1.4,0.7,2l-0.8,0.7l-0.8,0.6l0.6,0.8l1,1.2L2.3,15l0.8-0.6l2.3-1.9l0.4,0.3l2.5,2 l1.6,1.3V14v-4V8.7l4.1-3.4l0.8-0.6l-0.6-0.8l-0.9-1.2L12.7,2l-0.8,0.6L10,4.2V2V-0.1L10-0.1z"/>
<path d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
</g>
<g id="tab-audio-muted-white-pressed" class="icon muted white pressed">
<path class="outline" d="M9,2v4.3l3.5-2.9l0.9,1.2l-11,9l-1-1.2l1.9-1.5C2.6,10.6,2,9.9,2,9V7c0-1.1,0.9-2,2-2h1.2L9,2 M9,10v4l-2.5-2L9,10 M10-0.1 L8.4,1.2L4.9,4H4C2.3,4,1,5.3,1,7v2c0,0.7,0.3,1.4,0.7,2l-0.8,0.7l-0.8,0.6l0.6,0.8l1,1.2L2.3,15l0.8-0.6l2.3-1.9l0.4,0.3l2.5,2 l1.6,1.3V14v-4V8.7l4.1-3.4l0.8-0.6l-0.6-0.8l-0.9-1.2L12.7,2l-0.8,0.6L10,4.2V2V-0.1L10-0.1z"/>
<path d="M12.5,3.4L9,6.3V2L5.2,5H4C2.9,5,2,5.9,2,7v2c0,0.9,0.6,1.6,1.4,1.9l-1.9,1.5l1,1.2l11-9L12.5,3.4z M9,14v-4l-2.5,2L9,14z"/>
</g>
</svg>

До

Ширина:  |  Высота:  |  Размер: 3.1 KiB

После

Ширина:  |  Высота:  |  Размер: 8.7 KiB

Просмотреть файл

@ -123,48 +123,16 @@
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio");
}
.tab-icon-overlay[soundplaying][pinned]:hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover");
}
.tab-icon-overlay[soundplaying][pinned]:hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed");
}
.tab-icon-overlay[muted][pinned]:not([crashed]) {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
}
.tab-icon-overlay[muted][pinned]:not([crashed]):hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover");
#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:not(:hover) {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white");
}
.tab-icon-overlay[muted][pinned]:not([crashed]):hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed");
}
#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned] {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-dark");
}
#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover");
}
#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed");
}
#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]) {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
}
#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]):hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover");
}
#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]):hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed");
#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:not([crashed]):not(:hover) {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-white-muted");
}
.tab-throbber[busy] {
@ -199,51 +167,75 @@
}
.tab-icon-sound[soundplaying] {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab");
}
.tab-icon-sound[soundplaying]:hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-hover");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-hover");
}
.tab-icon-sound[soundplaying]:hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-pressed");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-pressed");
}
.tab-icon-sound[muted] {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-muted");
}
.tab-icon-sound[muted]:hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-hover");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-muted-hover");
}
.tab-icon-sound[muted]:hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-backgroundTab-muted-pressed");
}
.tab-icon-sound[visuallyselected=true][soundplaying] {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio");
}
.tab-icon-sound[visuallyselected=true][soundplaying]:hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-hover");
}
.tab-icon-sound[visuallyselected=true][soundplaying]:hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-pressed");
}
.tab-icon-sound[visuallyselected=true][muted] {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted");
}
.tab-icon-sound[visuallyselected=true][muted]:hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-hover");
}
.tab-icon-sound[visuallyselected=true][muted]:hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-pressed");
}
#TabsToolbar[brighttext] .tab-icon-sound[soundplaying] {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-dark");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white");
}
#TabsToolbar[brighttext] .tab-icon-sound[soundplaying]:hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-dark-hover");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white-hover");
}
#TabsToolbar[brighttext] .tab-icon-sound[soundplaying]:hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-dark-pressed");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-white-pressed");
}
#TabsToolbar[brighttext] .tab-icon-sound[muted] {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-dark");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white");
}
#TabsToolbar[brighttext] .tab-icon-sound[muted]:hover {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-dark-hover");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white-hover");
}
#TabsToolbar[brighttext] .tab-icon-sound[muted]:hover:active {
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-dark-pressed");
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-white-pressed");
}
.tab-background,

Просмотреть файл

@ -20,7 +20,7 @@ DEFINES += -DAPP_BUILDID=$(APP_BUILDID)
APP_INI_DEPS += $(DEPTH)/config/autoconf.mk
MOZ_SOURCE_STAMP := $(firstword $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg parent --template='{node|short}\n' 2>/dev/null))
MOZ_SOURCE_STAMP := $(firstword $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg parent --template='{node}\n' 2>/dev/null))
ifdef MOZ_SOURCE_STAMP
DEFINES += -DMOZ_SOURCE_STAMP='$(MOZ_SOURCE_STAMP)'
endif

Просмотреть файл

@ -60,7 +60,7 @@ else
ac_add_options "--with-compiler-wrapper=python2.7 $topsrcdir/sccache/sccache.py"
mk_add_options MOZ_PREFLIGHT_ALL+=build/sccache.mk
mk_add_options MOZ_POSTFLIGHT_ALL+=build/sccache.mk
mk_add_options "export UPLOAD_EXTRA_FILES+=sccache.log.gz"
mk_add_options "UPLOAD_EXTRA_FILES+=sccache.log.gz"
case "$platform" in
win*)
# sccache supports a special flag to create depfiles.

Просмотреть файл

@ -190,9 +190,10 @@ WANT_MOZCONFIG_MK = 1
endif
ifdef WANT_MOZCONFIG_MK
# For now, only output "export" lines from mach environment --format=client.mk output.
MOZCONFIG_MK_LINES := $(filter export||%,$(MOZCONFIG_OUT_LINES))
$(OBJDIR)/.mozconfig.mk: $(FOUND_MOZCONFIG) $(call mkdir_deps,$(OBJDIR)) $(OBJDIR)/CLOBBER
# For now, only output "export" lines and lines containing UPLOAD_EXTRA_FILES
# from mach environment --format=client.mk output.
MOZCONFIG_MK_LINES := $(filter export||% UPLOAD_EXTRA_FILES% %UPLOAD_EXTRA_FILES%,$(MOZCONFIG_OUT_LINES))
$(OBJDIR)/.mozconfig.mk: $(TOPSRCDIR)/client.mk $(FOUND_MOZCONFIG) $(call mkdir_deps,$(OBJDIR)) $(OBJDIR)/CLOBBER
$(if $(MOZCONFIG_MK_LINES),( $(foreach line,$(MOZCONFIG_MK_LINES), echo '$(subst ||, ,$(line))';) )) > $@
# Include that makefile so that it is created. This should not actually change
@ -201,12 +202,6 @@ $(OBJDIR)/.mozconfig.mk: $(FOUND_MOZCONFIG) $(call mkdir_deps,$(OBJDIR)) $(OBJDI
include $(OBJDIR)/.mozconfig.mk
endif
# UPLOAD_EXTRA_FILES is appended to and exported from mozconfig, which makes
# submakes as well as configure add even more to that, so just unexport it
# for submakes to pick it from .mozconfig.mk and for configure to pick it
# from mach environment.
unexport UPLOAD_EXTRA_FILES
# Print out any options loaded from mozconfig.
all realbuild clean distclean export libs install realclean::
ifneq (,$(strip $(MOZCONFIG_OUT_FILTERED)))

Просмотреть файл

@ -526,7 +526,7 @@ File::GetLastModifiedDate(ErrorResult& aRv)
return Date();
}
return Date(value);
return Date(JS::TimeClip(value));
}
int64_t

Просмотреть файл

@ -231,8 +231,6 @@ StructuredCloneHelper::Write(JSContext* aCx,
if (!StructuredCloneHelperInternal::Write(aCx, aValue, aTransfer)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
}
mTransferringPort.Clear();
}
void
@ -492,11 +490,6 @@ StructuredCloneHelper::WriteTransferCallback(JSContext* aCx,
MessagePortBase* port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
if (NS_SUCCEEDED(rv)) {
if (mTransferringPort.Contains(port)) {
// No duplicates.
return false;
}
// We use aExtraData to store the index of this new port identifier.
*aExtraData = mPortIdentifiers.Length();
MessagePortIdentifier* identifier = mPortIdentifiers.AppendElement();
@ -505,8 +498,6 @@ StructuredCloneHelper::WriteTransferCallback(JSContext* aCx,
return false;
}
mTransferringPort.AppendElement(port);
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
*aOwnership = JS::SCTAG_TMO_CUSTOM;
*aContent = nullptr;

Просмотреть файл

@ -233,11 +233,6 @@ private:
// outside that method. For this reason it's a raw pointer.
nsISupports* MOZ_NON_OWNING_REF mParent;
// This hashtable contains the ports while doing write (transferring and
// mapping transferred objects to the objects in the clone). It's an empty
// array outside the 'Write()' method.
nsTArray<nsRefPtr<MessagePortBase>> mTransferringPort;
// This array contains the ports once we've finished the reading. It's
// generated from the mPortIdentifiers array.
nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;

Просмотреть файл

@ -142,6 +142,24 @@ function runTests(obj) {
});
})
// no dup transfering
.then(function() {
if (!obj.transferableObjects) {
return;
}
// MessagePort
return new Promise(function(r, rr) {
var mc = new MessageChannel();
obj.send(42, [mc.port1, mc.port1]).then(function(received) {
ok(false, "Duplicate ports should throw!");
}, function() {
ok(true, "Duplicate ports should throw!");
})
.then(r);
});
})
// non transfering tests
.then(function() {
if (obj.transferableObjects) {

Просмотреть файл

@ -6,8 +6,9 @@
#include "mozilla/dom/Date.h"
#include "jsapi.h" // for JS_ObjectIsDate, JS_NewDateObjectMsec
#include "jsapi.h" // for JS_ObjectIsDate
#include "jsfriendapi.h" // for DateGetMsecSinceEpoch
#include "js/Date.h" // for JS::NewDateObject, JS::ClippedTime, JS::TimeClip
#include "js/RootingAPI.h" // for Rooted, MutableHandle
#include "js/Value.h" // for Value
#include "mozilla/FloatingPoint.h" // for IsNaN, UnspecifiedNaN
@ -15,30 +16,22 @@
namespace mozilla {
namespace dom {
Date::Date()
: mMsecSinceEpoch(UnspecifiedNaN<double>())
{
}
bool
Date::IsUndefined() const
{
return IsNaN(mMsecSinceEpoch);
}
bool
Date::SetTimeStamp(JSContext* aCx, JSObject* aObject)
{
JS::Rooted<JSObject*> obj(aCx, aObject);
MOZ_ASSERT(JS_ObjectIsDate(aCx, obj));
mMsecSinceEpoch = js::DateGetMsecSinceEpoch(aCx, obj);
double msecs = js::DateGetMsecSinceEpoch(aCx, obj);
JS::ClippedTime time = JS::TimeClip(msecs);
MOZ_ASSERT(NumbersAreIdentical(msecs, time.toDouble()));
mMsecSinceEpoch = time;
return true;
}
bool
Date::ToDateObject(JSContext* aCx, JS::MutableHandle<JS::Value> aRval) const
{
JSObject* obj = JS_NewDateObjectMsec(aCx, mMsecSinceEpoch);
JSObject* obj = JS::NewDateObject(aCx, mMsecSinceEpoch);
if (!obj) {
return false;
}

Просмотреть файл

@ -9,6 +9,7 @@
#ifndef mozilla_dom_Date_h
#define mozilla_dom_Date_h
#include "js/Date.h"
#include "js/TypeDecls.h"
namespace mozilla {
@ -17,21 +18,33 @@ namespace dom {
class Date
{
public:
// Not inlining much here to avoid the includes we'd need.
Date();
explicit Date(double aMilliseconds)
Date() {}
explicit Date(JS::ClippedTime aMilliseconds)
: mMsecSinceEpoch(aMilliseconds)
{}
bool IsUndefined() const;
double TimeStamp() const
bool IsUndefined() const
{
return !mMsecSinceEpoch.isValid();
}
JS::ClippedTime TimeStamp() const
{
return mMsecSinceEpoch;
}
void SetTimeStamp(double aMilliseconds)
// Returns an integer in the range [-8.64e15, +8.64e15] (-0 excluded), *or*
// returns NaN. DO NOT ASSUME THIS IS FINITE!
double ToDouble() const
{
return mMsecSinceEpoch.toDouble();
}
void SetTimeStamp(JS::ClippedTime aMilliseconds)
{
mMsecSinceEpoch = aMilliseconds;
}
// Can return false if CheckedUnwrap fails. This will NOT throw;
// callers should do it as needed.
bool SetTimeStamp(JSContext* aCx, JSObject* aObject);
@ -39,7 +52,7 @@ public:
bool ToDateObject(JSContext* aCx, JS::MutableHandle<JS::Value> aRval) const;
private:
double mMsecSinceEpoch;
JS::ClippedTime mMsecSinceEpoch;
};
} // namespace dom

Просмотреть файл

@ -4319,7 +4319,7 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
PRTime since = 0;
if (aOptions.mSince.WasPassed() && !aOptions.mSince.Value().IsUndefined()) {
since = PRTime(aOptions.mSince.Value().TimeStamp());
since = PRTime(aOptions.mSince.Value().TimeStamp().toDouble());
}
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,

Просмотреть файл

@ -10,6 +10,7 @@
#include "js/Value.h"
#include "js/RootingAPI.h"
#include "jsapi.h"
#include "js/Date.h"
#include "mozilla/dom/FileModeBinding.h"
#include "nsDebug.h"
#include "nsIFileStreams.h"
@ -49,7 +50,7 @@ MetadataHelper::GetSuccessResult(JSContext* aCx,
if (mParams->LastModifiedRequested()) {
double msec = mParams->LastModified();
JSObject *date = JS_NewDateObjectMsec(aCx, msec);
JSObject *date = JS::NewDateObject(aCx, JS::TimeClip(msec));
NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY);
JS::Rooted<JS::Value> dateRoot(aCx, JS::ObjectValue(*date));

Просмотреть файл

@ -1514,12 +1514,12 @@ HTMLInputElement::ConvertStringToNumber(nsAString& aValue,
return false;
}
double date = JS::MakeDate(year, month - 1, day);
if (IsNaN(date)) {
JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day));
if (!time.isValid()) {
return false;
}
aResultValue = Decimal::fromDouble(date);
aResultValue = Decimal::fromDouble(time.toDouble());
return true;
}
case NS_FORM_INPUT_TIME:
@ -1762,7 +1762,8 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
return Nullable<Date>();
}
return Nullable<Date>(Date(JS::MakeDate(year, month - 1, day)));
JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day));
return Nullable<Date>(Date(time));
}
case NS_FORM_INPUT_TIME:
{
@ -1773,7 +1774,11 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
return Nullable<Date>();
}
return Nullable<Date>(Date(millisecond));
JS::ClippedTime time = JS::TimeClip(millisecond);
MOZ_ASSERT(time.toDouble() == millisecond,
"HTML times are restricted to the day after the epoch and "
"never clip");
return Nullable<Date>(Date(time));
}
}
@ -1795,7 +1800,7 @@ HTMLInputElement::SetValueAsDate(Nullable<Date> aDate, ErrorResult& aRv)
return;
}
SetValue(Decimal::fromDouble(aDate.Value().TimeStamp()));
SetValue(Decimal::fromDouble(aDate.Value().TimeStamp().toDouble()));
}
NS_IMETHODIMP

Просмотреть файл

@ -20,6 +20,7 @@
#include "IndexedDatabaseInlines.h"
#include "IndexedDatabaseManager.h"
#include "js/Class.h"
#include "js/Date.h"
#include "js/StructuredClone.h"
#include "KeyPath.h"
#include "mozilla/Endian.h"
@ -749,8 +750,8 @@ public:
return false;
}
JS::Rooted<JSObject*> date(aCx,
JS_NewDateObjectMsec(aCx, aData.lastModifiedDate));
JS::ClippedTime time = JS::TimeClip(aData.lastModifiedDate);
JS::Rooted<JSObject*> date(aCx, JS::NewDateObject(aCx, time));
if (NS_WARN_IF(!date)) {
return false;
}

Просмотреть файл

@ -8,6 +8,7 @@
#include "Key.h"
#include <algorithm>
#include "js/Date.h"
#include "js/Value.h"
#include "jsfriendapi.h"
#include "mozilla/Endian.h"
@ -237,7 +238,11 @@ Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd,
}
else if (*aPos - aTypeOffset == eDate) {
double msec = static_cast<double>(DecodeNumber(aPos, aEnd));
JSObject* date = JS_NewDateObjectMsec(aCx, msec);
JS::ClippedTime time = JS::TimeClip(msec);
MOZ_ASSERT(msec == time.toDouble(),
"encoding from a Date object not containing an invalid date "
"means we should always have clipped values");
JSObject* date = JS::NewDateObject(aCx, time);
if (!date) {
IDB_WARNING("Failed to make date!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;

Просмотреть файл

@ -429,8 +429,8 @@ public:
}
T firstEnd = std::max(mIntervals[0].mStart, aInterval.mStart);
T secondStart = std::min(mIntervals.LastElement().mEnd, aInterval.mEnd);
ElemType startInterval(mIntervals[0].mStart, firstEnd, aInterval.mFuzz);
ElemType endInterval(secondStart, mIntervals.LastElement().mEnd, aInterval.mFuzz);
ElemType startInterval(mIntervals[0].mStart, firstEnd);
ElemType endInterval(secondStart, mIntervals.LastElement().mEnd);
SelfType intervals(Move(startInterval));
intervals += Move(endInterval);
return Intersection(intervals);

Просмотреть файл

@ -89,6 +89,7 @@ MediaFormatReader::Shutdown()
MOZ_ASSERT(OnTaskQueue());
mDemuxerInitRequest.DisconnectIfExists();
mDecodersInitRequest.DisconnectIfExists();
mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
mSeekPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
mSkipRequest.DisconnectIfExists();
@ -270,19 +271,19 @@ MediaFormatReader::AsyncReadMetadata()
{
MOZ_ASSERT(OnTaskQueue());
nsRefPtr<MetadataPromise> p = mMetadataPromise.Ensure(__func__);
if (mInitDone) {
// We are returning from dormant.
if (!EnsureDecodersSetup()) {
return MetadataPromise::CreateAndReject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
if (!EnsureDecodersCreated()) {
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
return p;
}
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
metadata->mInfo = mInfo;
metadata->mTags = nullptr;
return MetadataPromise::CreateAndResolve(metadata, __func__);
MOZ_ASSERT(!mDecodersInitRequest.Exists());
EnsureDecodersInitialized();
return p;
}
nsRefPtr<MetadataPromise> p = mMetadataPromise.Ensure(__func__);
mDemuxerInitRequest.Begin(mDemuxer->Init()
->Then(OwnerThread(), __func__, this,
&MediaFormatReader::OnDemuxerInitDone,
@ -372,16 +373,24 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
MOZ_ASSERT(mAudioTrackDemuxer);
}
mInitDone = true;
if (!IsWaitingOnCDMResource() && !EnsureDecodersSetup()) {
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
} else {
if (IsWaitingOnCDMResource()) {
// Decoder can't be allocated before CDM resource is ready, so resolving the
// mMetadataPromise here to wait for CDM on MDSM.
mInitDone = true;
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
metadata->mInfo = mInfo;
metadata->mTags = nullptr;
mMetadataPromise.Resolve(metadata, __func__);
return;
}
if (!EnsureDecodersCreated()) {
mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
return;
}
MOZ_ASSERT(!mDecodersInitRequest.Exists());
EnsureDecodersInitialized();
}
void
@ -396,10 +405,9 @@ MediaFormatReader::OnDemuxerInitFailed(DemuxerFailureReason aFailure)
}
bool
MediaFormatReader::EnsureDecodersSetup()
MediaFormatReader::EnsureDecodersCreated()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(mInitDone);
if (!mPlatform) {
if (IsEncrypted()) {
@ -439,6 +447,7 @@ MediaFormatReader::EnsureDecodersSetup()
NS_ENSURE_TRUE(IsSupportedAudioMimeType(mInfo.mAudio.mMimeType),
false);
mAudio.mDecoderInitialized = false;
mAudio.mDecoder =
mPlatform->CreateDecoder(mAudio.mInfo ?
*mAudio.mInfo->GetAsAudioInfo() :
@ -446,14 +455,13 @@ MediaFormatReader::EnsureDecodersSetup()
mAudio.mTaskQueue,
mAudio.mCallback);
NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, false);
nsresult rv = mAudio.mDecoder->Init();
NS_ENSURE_SUCCESS(rv, false);
}
if (HasVideo() && !mVideo.mDecoder) {
NS_ENSURE_TRUE(IsSupportedVideoMimeType(mInfo.mVideo.mMimeType),
false);
mVideo.mDecoderInitialized = false;
if (mSharedDecoderManager &&
mPlatform->SupportsSharedDecoders(mInfo.mVideo)) {
mVideo.mDecoder =
@ -476,13 +484,86 @@ MediaFormatReader::EnsureDecodersSetup()
mDecoder->GetImageContainer());
}
NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, false);
nsresult rv = mVideo.mDecoder->Init();
NS_ENSURE_SUCCESS(rv, false);
}
return true;
}
bool
MediaFormatReader::EnsureDecodersInitialized()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(mVideo.mDecoder || mAudio.mDecoder);
// DecodeDemuxedSample() could call this function before mDecodersInitRequest
// is completed. And it is ok to return false here because DecodeDemuxedSample
// will call ScheduleUpdate() again.
// It also avoids calling decoder->Init() multiple times.
if (mDecodersInitRequest.Exists()) {
return false;
}
nsTArray<nsRefPtr<MediaDataDecoder::InitPromise>> promises;
if (mVideo.mDecoder && !mVideo.mDecoderInitialized) {
promises.AppendElement(mVideo.mDecoder->Init());
}
if (mAudio.mDecoder && !mAudio.mDecoderInitialized) {
promises.AppendElement(mAudio.mDecoder->Init());
}
if (promises.Length()) {
mDecodersInitRequest.Begin(MediaDataDecoder::InitPromise::All(OwnerThread(), promises)
->Then(OwnerThread(), __func__, this,
&MediaFormatReader::OnDecoderInitDone,
&MediaFormatReader::OnDecoderInitFailed));
}
LOG("Init decoders: audio: %p, audio init: %d, video: %p, video init: %d",
mAudio.mDecoder.get(), mAudio.mDecoderInitialized,
mVideo.mDecoder.get(), mVideo.mDecoderInitialized);
// Return false if any decoder is under initialization.
return !promises.Length();
}
void
MediaFormatReader::OnDecoderInitDone(const nsTArray<TrackType>& aTrackTypes)
{
MOZ_ASSERT(OnTaskQueue());
mDecodersInitRequest.Complete();
for (const auto& track : aTrackTypes) {
auto& decoder = GetDecoderData(track);
decoder.mDecoderInitialized = true;
ScheduleUpdate(track);
}
if (!mMetadataPromise.IsEmpty()) {
mInitDone = true;
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
metadata->mInfo = mInfo;
metadata->mTags = nullptr;
mMetadataPromise.Resolve(metadata, __func__);
}
}
void
MediaFormatReader::OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason)
{
MOZ_ASSERT(OnTaskQueue());
mDecodersInitRequest.Complete();
NS_WARNING("Failed to init decoder");
mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
NotifyError(TrackType::kAudioTrack);
NotifyError(TrackType::kVideoTrack);
}
void
MediaFormatReader::ReadUpdatedMetadata(MediaInfo* aInfo)
{
@ -509,7 +590,7 @@ MediaFormatReader::DisableHardwareAcceleration()
Flush(TrackInfo::kVideoTrack);
mVideo.mDecoder->Shutdown();
mVideo.mDecoder = nullptr;
if (!EnsureDecodersSetup()) {
if (!EnsureDecodersCreated()) {
LOG("Unable to re-create decoder, aborting");
NotifyError(TrackInfo::kVideoTrack);
return;
@ -559,13 +640,6 @@ MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe,
return VideoDataPromise::CreateAndReject(CANCELED, __func__);
}
if (!EnsureDecodersSetup()) {
NS_WARNING("Error constructing decoders");
return VideoDataPromise::CreateAndReject(DECODE_ERROR, __func__);
}
MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder);
mVideo.mForceDecodeAhead = aForceDecodeAhead;
media::TimeUnit timeThreshold{media::TimeUnit::FromMicroseconds(aTimeThreshold)};
if (ShouldSkip(aSkipToNextKeyframe, timeThreshold)) {
@ -657,13 +731,9 @@ MediaFormatReader::RequestAudioData()
return AudioDataPromise::CreateAndReject(CANCELED, __func__);
}
if (!EnsureDecodersSetup()) {
NS_WARNING("Error constructing decoders");
return AudioDataPromise::CreateAndReject(DECODE_ERROR, __func__);
}
nsRefPtr<AudioDataPromise> p = mAudio.mPromise.Ensure(__func__);
ScheduleUpdate(TrackInfo::kAudioTrack);
ScheduleUpdate(TrackType::kAudioTrack);
return p;
}
@ -877,6 +947,17 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
return;
}
if (!EnsureDecodersCreated()) {
NS_WARNING("Error constructing decoders");
NotifyError(aTrack);
return;
}
if (!EnsureDecodersInitialized()) {
ScheduleUpdate(aTrack);
return;
}
LOGV("Giving %s input to decoder", TrackTypeToStr(aTrack));
// Decode all our demuxed frames.
@ -912,15 +993,9 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
Flush(aTrack);
decoder.mDecoder->Shutdown();
decoder.mDecoder = nullptr;
if (!EnsureDecodersSetup()) {
LOG("Unable to re-create decoder, aborting");
NotifyError(aTrack);
return;
}
LOGV("%s decoder:%p created for sid:%u",
TrackTypeToStr(aTrack), decoder.mDecoder.get(), info->GetID());
if (sample->mKeyframe) {
decoder.mQueuedSamples.MoveElementsFrom(samples);
ScheduleUpdate(aTrack);
} else {
MOZ_ASSERT(decoder.mTimeThreshold.isNothing());
LOG("Stream change occurred on a non-keyframe. Seeking to:%lld",
@ -953,8 +1028,8 @@ MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
}
decoder.mTimeThreshold.reset();
}));
return;
}
return;
}
LOGV("Input:%lld (dts:%lld kf:%d)",
@ -1343,7 +1418,8 @@ MediaFormatReader::Seek(int64_t aTime, int64_t aUnused)
return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
mPendingSeekTime.emplace(media::TimeUnit::FromMicroseconds(aTime));
mOriginalSeekTime = Some(media::TimeUnit::FromMicroseconds(aTime));
mPendingSeekTime = mOriginalSeekTime;
nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
@ -1377,6 +1453,34 @@ MediaFormatReader::OnSeekFailed(TrackType aTrack, DemuxerFailureReason aResult)
}
if (aResult == DemuxerFailureReason::WAITING_FOR_DATA) {
if (HasVideo() && aTrack == TrackType::kAudioTrack &&
mOriginalSeekTime.isSome() &&
mPendingSeekTime.ref() != mOriginalSeekTime.ref()) {
// We have failed to seek audio where video seeked to earlier.
// Attempt to seek instead to the closest point that we know we have in
// order to limit A/V sync discrepency.
// Ensure we have the most up to date buffered ranges.
UpdateReceivedNewData(TrackType::kAudioTrack);
Maybe<media::TimeUnit> nextSeekTime;
// Find closest buffered time found after video seeked time.
for (const auto& timeRange : mAudio.mTimeRanges) {
if (timeRange.mStart >= mPendingSeekTime.ref()) {
nextSeekTime.emplace(timeRange.mStart);
break;
}
}
if (nextSeekTime.isNothing() ||
nextSeekTime.ref() > mOriginalSeekTime.ref()) {
nextSeekTime = mOriginalSeekTime;
LOG("Unable to seek audio to video seek time. A/V sync may be broken");
} else {
mOriginalSeekTime.reset();
}
mPendingSeekTime = nextSeekTime;
DoAudioSeek();
return;
}
NotifyWaitingForData(aTrack);
return;
}
@ -1406,6 +1510,7 @@ MediaFormatReader::OnVideoSeekCompleted(media::TimeUnit aTime)
if (HasAudio()) {
MOZ_ASSERT(mPendingSeekTime.isSome());
mPendingSeekTime = Some(aTime);
DoAudioSeek();
} else {
mPendingSeekTime.reset();

Просмотреть файл

@ -105,7 +105,10 @@ private:
void NotifyDemuxer(uint32_t aLength, int64_t aOffset);
void ReturnOutput(MediaData* aData, TrackType aTrack);
bool EnsureDecodersSetup();
bool EnsureDecodersCreated();
// It returns true when all decoders are initialized. False when there is pending
// initialization.
bool EnsureDecodersInitialized();
// Enqueues a task to call Update(aTrack) on the decoder task queue.
// Lock for corresponding track must be held.
@ -194,6 +197,7 @@ private:
, mWaitingForData(false)
, mReceivedNewData(false)
, mDiscontinuity(true)
, mDecoderInitialized(false)
, mOutputRequested(false)
, mInputExhausted(false)
, mError(false)
@ -242,6 +246,8 @@ private:
}
// MediaDataDecoder handler's variables.
// False when decoder is created. True when decoder Init() promise is resolved.
bool mDecoderInitialized;
bool mOutputRequested;
bool mInputExhausted;
bool mError;
@ -337,6 +343,9 @@ private:
DecoderData& GetDecoderData(TrackType aTrack);
void OnDecoderInitDone(const nsTArray<TrackType>& aTrackTypes);
void OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason);
// Demuxer objects.
void OnDemuxerInitDone(nsresult);
void OnDemuxerInitFailed(DemuxerFailureReason aFailure);
@ -408,9 +417,13 @@ private:
OnSeekFailed(TrackType::kAudioTrack, aFailure);
}
// Temporary seek information while we wait for the data
Maybe<media::TimeUnit> mOriginalSeekTime;
Maybe<media::TimeUnit> mPendingSeekTime;
MozPromiseHolder<SeekPromise> mSeekPromise;
// Pending decoders initialization.
MozPromiseRequestHolder<MediaDataDecoder::InitPromise::AllPromiseType> mDecodersInitRequest;
#ifdef MOZ_EME
nsRefPtr<CDMProxy> mCDMProxy;
#endif

Просмотреть файл

@ -278,8 +278,6 @@ CreateTestH264Decoder(layers::LayersBackend aBackend,
if (!decoder) {
return nullptr;
}
nsresult rv = decoder->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
return decoder.forget();
}
@ -293,7 +291,6 @@ MP4Decoder::IsVideoAccelerated(layers::LayersBackend aBackend)
return false;
}
bool result = decoder->IsHardwareAccelerated();
decoder->Shutdown();
return result;
}
@ -336,8 +333,6 @@ CreateTestAACDecoder(AudioInfo& aConfig)
if (!decoder) {
return nullptr;
}
nsresult rv = decoder->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
return decoder.forget();
}
@ -376,7 +371,6 @@ MP4Decoder::CanCreateAACDecoder()
MOZ_ARRAY_LENGTH(sTestAACExtraData));
nsRefPtr<MediaDataDecoder> decoder(CreateTestAACDecoder(config));
if (decoder) {
decoder->Shutdown();
result = true;
}
haveCachedResult = true;

Просмотреть файл

@ -19,7 +19,7 @@
#include "gmp-video-encode.h"
#include "GMPPlatform.h"
#include "mozilla/dom/CrashReporterChild.h"
#include "mozilla/Tokenizer.h"
#include "GMPUtils.h"
#include "prio.h"
using mozilla::dom::CrashReporterChild;
@ -341,24 +341,6 @@ ReadIntoString(nsIFile* aFile,
return rv;
}
static nsTArray<nsCString>
SplitAt(Tokenizer::Token aDelim, const nsACString& aInput)
{
nsTArray<nsCString> tokens;
Tokenizer tokenizer(aInput);
while (!tokenizer.HasFailed()) {
tokenizer.Record();
Tokenizer::Token token;
while (tokenizer.Next(token) && !token.Equals(aDelim))
; // Skip up to next delimeter, or EOF.
nsAutoCString value;
tokenizer.Claim(value);
tokens.AppendElement(value);
}
return tokens;
}
// Pre-load DLLs that need to be used by the EME plugin but that can't be
// loaded after the sandbox has started
bool
@ -387,7 +369,10 @@ GMPChild::PreLoadLibraries(const nsAString& aPluginPath)
return false;
}
nsTArray<nsCString> lines = SplitAt(Tokenizer::Token::NewLine(), info);
// Note: we pass "\r\n" to SplitAt so that we'll split lines delimited
// by \n (Unix), \r\n (Windows) and \r (old MacOSX).
nsTArray<nsCString> lines;
SplitAt("\r\n", info, lines);
for (nsCString line : lines) {
// Make lowercase.
std::transform(line.BeginWriting(),
@ -401,8 +386,8 @@ GMPChild::PreLoadLibraries(const nsAString& aPluginPath)
continue;
}
// Line starts with "libraries:".
nsTArray<nsCString> libs = SplitAt(Tokenizer::Token::Char(','),
Substring(line, offset + strlen(libraries)));
nsTArray<nsCString> libs;
SplitAt(",", Substring(line, offset + strlen(libraries)), libs);
for (nsCString lib : libs) {
lib.Trim(" ");
for (const char* whiteListedLib : whitelist) {

Просмотреть файл

@ -158,8 +158,9 @@ CreateRecord(const char* aRecordName,
GMPRecord** aOutRecord,
GMPRecordClient* aClient)
{
if (aRecordNameSize > GMP_MAX_RECORD_NAME_SIZE) {
NS_WARNING("GMP tried to CreateRecord with too long record name");
if (aRecordNameSize > GMP_MAX_RECORD_NAME_SIZE ||
aRecordNameSize == 0) {
NS_WARNING("GMP tried to CreateRecord with too long or 0 record name");
return GMPGenericErr;
}
GMPStorageChild* storage = sChild->GetGMPStorage();

Просмотреть файл

@ -4,7 +4,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GMPStorageParent.h"
#include "mozilla/SyncRunnable.h"
#include "plhash.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
@ -12,8 +11,8 @@
#include "GMPParent.h"
#include "gmp-storage.h"
#include "mozilla/unused.h"
#include "nsTHashtable.h"
#include "nsDataHashtable.h"
#include "mozilla/Endian.h"
#include "nsClassHashtable.h"
#include "prio.h"
#include "mozIGeckoMediaPluginService.h"
#include "nsContentCID.h"
@ -84,244 +83,43 @@ GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
return NS_OK;
}
enum OpenFileMode { ReadWrite, Truncate };
static nsresult
OpenStorageFile(const nsCString& aRecordName,
const nsCString& aNodeId,
const OpenFileMode aMode,
PRFileDesc** aOutFD)
{
MOZ_ASSERT(aOutFD);
nsCOMPtr<nsIFile> f;
nsresult rv = GetGMPStorageDir(getter_AddRefs(f), aNodeId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoString recordNameHash;
recordNameHash.AppendInt(HashString(aRecordName.get()));
f->Append(recordNameHash);
auto mode = PR_RDWR | PR_CREATE_FILE;
if (aMode == Truncate) {
mode |= PR_TRUNCATE;
}
return f->OpenNSPRFileDesc(mode, PR_IRWXU, aOutFD);
}
static nsresult
RemoveStorageFile(const nsCString& aRecordName,
const nsCString& aNodeId)
{
nsCOMPtr<nsIFile> f;
nsresult rv = GetGMPStorageDir(getter_AddRefs(f), aNodeId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoString recordNameHash;
recordNameHash.AppendInt(HashString(aRecordName.get()));
f->Append(recordNameHash);
return f->Remove(/* bool recursive= */ false);
}
PLDHashOperator
CloseFile(const nsACString& key, PRFileDesc*& entry, void* cx)
{
if (PR_Close(entry) != PR_SUCCESS) {
NS_WARNING("GMPDiskStorage failed to close file.");
}
return PL_DHASH_REMOVE;
}
// Disk-backed GMP storage. Records are stored in files on disk in
// the profile directory. The record name is a hash of the filename,
// and we resolve hash collisions by just adding 1 to the hash code.
// The format of records on disk is:
// 4 byte, uint32_t $recordNameLength, in little-endian byte order,
// record name (i.e. $recordNameLength bytes, no null terminator)
// record bytes (entire remainder of file)
class GMPDiskStorage : public GMPStorage {
public:
explicit GMPDiskStorage(const nsCString& aNodeId)
: mNodeId(aNodeId)
{
}
~GMPDiskStorage() {
mFiles.Enumerate(CloseFile, nullptr);
MOZ_ASSERT(!mFiles.Count());
}
virtual GMPErr Open(const nsCString& aRecordName) override
{
MOZ_ASSERT(!IsOpen(aRecordName));
PRFileDesc* fd = nullptr;
if (NS_FAILED(OpenStorageFile(aRecordName, mNodeId, ReadWrite, &fd))) {
NS_WARNING("Failed to open storage file.");
return GMPGenericErr;
}
mFiles.Put(aRecordName, fd);
return GMPNoErr;
}
virtual bool IsOpen(const nsCString& aRecordName) override {
return mFiles.Contains(aRecordName);
}
static
GMPErr ReadRecordMetadata(PRFileDesc* aFd,
int32_t& aOutFileLength,
int32_t& aOutRecordLength,
nsACString& aOutRecordName)
{
int32_t fileLength = PR_Seek(aFd, 0, PR_SEEK_END);
PR_Seek(aFd, 0, PR_SEEK_SET);
if (fileLength > GMP_MAX_RECORD_SIZE) {
// Refuse to read big records.
return GMPQuotaExceededErr;
}
aOutFileLength = fileLength;
aOutRecordLength = 0;
// At the start of the file the length of the record name is stored in a
// size_t (host byte order) followed by the record name at the start of
// the file. The record name is not null terminated. The remainder of the
// file is the record's data.
size_t recordNameLength = 0;
if (fileLength == 0 || sizeof(recordNameLength) >= (size_t)fileLength) {
// Record file is empty, or doesn't even have enough contents to
// store the record name length and/or record name. Report record
// as empty.
return GMPNoErr;
}
int32_t bytesRead = PR_Read(aFd, &recordNameLength, sizeof(recordNameLength));
if (sizeof(recordNameLength) != bytesRead ||
recordNameLength > fileLength - sizeof(recordNameLength)) {
// Record file has invalid contents. Report record as empty.
return GMPNoErr;
}
nsCString recordName;
recordName.SetLength(recordNameLength);
bytesRead = PR_Read(aFd, recordName.BeginWriting(), recordNameLength);
if (bytesRead != (int32_t)recordNameLength) {
// Record file has invalid contents. Report record as empty.
return GMPGenericErr;
}
MOZ_ASSERT(fileLength > 0 && (size_t)fileLength >= sizeof(recordNameLength) + recordNameLength);
int32_t recordLength = fileLength - (sizeof(recordNameLength) + recordNameLength);
aOutRecordLength = recordLength;
aOutRecordName = recordName;
return GMPNoErr;
}
virtual GMPErr Read(const nsCString& aRecordName,
nsTArray<uint8_t>& aOutBytes) override
{
// Our error strategy is to report records with invalid contents as
// containing 0 bytes. Zero length records are considered "deleted" by
// the GMPStorage API.
aOutBytes.SetLength(0);
PRFileDesc* fd = mFiles.Get(aRecordName);
if (!fd) {
return GMPGenericErr;
}
int32_t fileLength = 0;
int32_t recordLength = 0;
nsCString recordName;
GMPErr err = ReadRecordMetadata(fd,
fileLength,
recordLength,
recordName);
if (NS_WARN_IF(GMP_FAILED(err))) {
return err;
}
if (recordLength == 0) {
// Record is empoty but not invalid, or it's invalid and we're going to
// just act like it's empty and let the client overwrite it.
return GMPNoErr;
}
if (!aRecordName.Equals(recordName)) {
NS_WARNING("Hash collision in GMPStorage");
return GMPGenericErr;
}
// After calling ReadRecordMetadata, we should be ready to read the
// record data.
MOZ_ASSERT(PR_Available(fd) == recordLength);
aOutBytes.SetLength(recordLength);
int32_t bytesRead = PR_Read(fd, aOutBytes.Elements(), recordLength);
return (bytesRead == recordLength) ? GMPNoErr : GMPGenericErr;
}
virtual GMPErr Write(const nsCString& aRecordName,
const nsTArray<uint8_t>& aBytes) override
{
PRFileDesc* fd = mFiles.Get(aRecordName);
if (!fd) {
return GMPGenericErr;
}
// Write operations overwrite the entire record. So close it now.
PR_Close(fd);
mFiles.Remove(aRecordName);
// Writing 0 bytes means removing (deleting) the file.
if (aBytes.Length() == 0) {
nsresult rv = RemoveStorageFile(aRecordName, mNodeId);
if (NS_WARN_IF(NS_FAILED(rv))) {
// Could not delete file -> Continue with trying to erase the contents.
} else {
return GMPNoErr;
// Close all open file handles.
for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) {
Record* record = iter.UserData();
if (record->mFileDesc) {
PR_Close(record->mFileDesc);
record->mFileDesc = nullptr;
}
}
// Write operations overwrite the entire record. So re-open the file
// in truncate mode, to clear its contents.
if (NS_FAILED(OpenStorageFile(aRecordName, mNodeId, Truncate, &fd))) {
return GMPGenericErr;
}
mFiles.Put(aRecordName, fd);
// Store the length of the record name followed by the record name
// at the start of the file.
int32_t bytesWritten = 0;
if (aBytes.Length() > 0) {
size_t recordNameLength = aRecordName.Length();
bytesWritten = PR_Write(fd, &recordNameLength, sizeof(recordNameLength));
if (NS_WARN_IF(bytesWritten != sizeof(recordNameLength))) {
return GMPGenericErr;
}
bytesWritten = PR_Write(fd, aRecordName.get(), recordNameLength);
if (NS_WARN_IF(bytesWritten != (int32_t)recordNameLength)) {
return GMPGenericErr;
}
}
bytesWritten = PR_Write(fd, aBytes.Elements(), aBytes.Length());
return (bytesWritten == (int32_t)aBytes.Length()) ? GMPNoErr : GMPGenericErr;
}
virtual GMPErr GetRecordNames(nsTArray<nsCString>& aOutRecordNames) override
{
nsresult Init() {
// Build our index of records on disk.
nsCOMPtr<nsIFile> storageDir;
nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return GMPGenericErr;
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsISimpleEnumerator> iter;
rv = storageDir->GetDirectoryEntries(getter_AddRefs(iter));
if (NS_FAILED(rv)) {
return GMPGenericErr;
return NS_ERROR_FAILURE;
}
bool hasMore;
@ -336,66 +134,367 @@ public:
continue;
}
nsAutoCString leafName;
rv = dirEntry->GetNativeLeafName(leafName);
if (NS_FAILED(rv)) {
continue;
}
PRFileDesc* fd = nullptr;
if (NS_FAILED(dirEntry->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) {
continue;
}
int32_t fileLength = 0;
int32_t recordLength = 0;
nsCString recordName;
GMPErr err = ReadRecordMetadata(fd,
fileLength,
recordLength,
recordName);
nsresult err = ReadRecordMetadata(fd, recordLength, recordName);
PR_Close(fd);
if (NS_WARN_IF(GMP_FAILED(err))) {
return err;
}
if (recordName.IsEmpty() || recordLength == 0) {
if (NS_FAILED(err)) {
// File is not a valid storage file. Don't index it. Delete the file,
// to make our indexing faster in future.
dirEntry->Remove(false);
continue;
}
// Ensure the file name is the hash of the record name stored in the
// record file. Otherwise it's not a valid record.
nsAutoCString recordNameHash;
recordNameHash.AppendInt(HashString(recordName.get()));
if (!recordNameHash.Equals(leafName)) {
nsAutoString filename;
rv = dirEntry->GetLeafName(filename);
if (NS_FAILED(rv)) {
continue;
}
aOutRecordNames.AppendElement(recordName);
mRecords.Put(recordName, new Record(filename, recordName));
}
return NS_OK;
}
GMPErr Open(const nsCString& aRecordName) override
{
MOZ_ASSERT(!IsOpen(aRecordName));
nsresult rv;
Record* record = nullptr;
if (!mRecords.Get(aRecordName, &record)) {
// New file.
nsAutoString filename;
rv = GetUnusedFilename(aRecordName, filename);
if (NS_WARN_IF(NS_FAILED(rv))) {
return GMPGenericErr;
}
record = new Record(filename, aRecordName);
mRecords.Put(aRecordName, record);
}
MOZ_ASSERT(record);
if (record->mFileDesc) {
NS_WARNING("Tried to open already open record");
return GMPRecordInUse;
}
rv = OpenStorageFile(record->mFilename, ReadWrite, &record->mFileDesc);
if (NS_WARN_IF(NS_FAILED(rv))) {
return GMPGenericErr;
}
MOZ_ASSERT(IsOpen(aRecordName));
return GMPNoErr;
}
virtual void Close(const nsCString& aRecordName) override
bool IsOpen(const nsCString& aRecordName) override {
// We are open if we have a record indexed, and it has a valid
// file descriptor.
Record* record = nullptr;
return mRecords.Get(aRecordName, &record) &&
!!record->mFileDesc;
}
GMPErr Read(const nsCString& aRecordName,
nsTArray<uint8_t>& aOutBytes) override
{
PRFileDesc* fd = mFiles.Get(aRecordName);
if (fd) {
if (PR_Close(fd) == PR_SUCCESS) {
mFiles.Remove(aRecordName);
if (!IsOpen(aRecordName)) {
return GMPClosedErr;
}
Record* record = nullptr;
mRecords.Get(aRecordName, &record);
MOZ_ASSERT(record && !!record->mFileDesc); // IsOpen() guarantees this.
// Our error strategy is to report records with invalid contents as
// containing 0 bytes. Zero length records are considered "deleted" by
// the GMPStorage API.
aOutBytes.SetLength(0);
int32_t recordLength = 0;
nsCString recordName;
nsresult err = ReadRecordMetadata(record->mFileDesc,
recordLength,
recordName);
if (NS_FAILED(err) || recordLength == 0) {
// We failed to read the record metadata. Or the record is 0 length.
// Treat damaged records as empty.
// ReadRecordMetadata() could fail if the GMP opened a new record and
// tried to read it before anything was written to it..
return GMPNoErr;
}
if (!aRecordName.Equals(recordName)) {
NS_WARNING("Record file contains some other record's contents!");
return GMPRecordCorrupted;
}
// After calling ReadRecordMetadata, we should be ready to read the
// record data.
if (PR_Available(record->mFileDesc) != recordLength) {
NS_WARNING("Record file length mismatch!");
return GMPRecordCorrupted;
}
aOutBytes.SetLength(recordLength);
int32_t bytesRead = PR_Read(record->mFileDesc, aOutBytes.Elements(), recordLength);
return (bytesRead == recordLength) ? GMPNoErr : GMPRecordCorrupted;
}
GMPErr Write(const nsCString& aRecordName,
const nsTArray<uint8_t>& aBytes) override
{
if (!IsOpen(aRecordName)) {
return GMPClosedErr;
}
Record* record = nullptr;
mRecords.Get(aRecordName, &record);
MOZ_ASSERT(record && !!record->mFileDesc); // IsOpen() guarantees this.
// Write operations overwrite the entire record. So close it now.
PR_Close(record->mFileDesc);
record->mFileDesc = nullptr;
// Writing 0 bytes means removing (deleting) the file.
if (aBytes.Length() == 0) {
nsresult rv = RemoveStorageFile(record->mFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
// Could not delete file -> Continue with trying to erase the contents.
} else {
NS_WARNING("GMPDiskStorage failed to close file.");
return GMPNoErr;
}
}
// Write operations overwrite the entire record. So re-open the file
// in truncate mode, to clear its contents.
if (NS_FAILED(OpenStorageFile(record->mFilename,
Truncate,
&record->mFileDesc))) {
return GMPGenericErr;
}
// Store the length of the record name followed by the record name
// at the start of the file.
int32_t bytesWritten = 0;
char buf[sizeof(uint32_t)] = {0};
LittleEndian::writeUint32(buf, aRecordName.Length());
bytesWritten = PR_Write(record->mFileDesc, buf, MOZ_ARRAY_LENGTH(buf));
if (bytesWritten != MOZ_ARRAY_LENGTH(buf)) {
NS_WARNING("Failed to write GMPStorage record name length.");
return GMPRecordCorrupted;
}
bytesWritten = PR_Write(record->mFileDesc,
aRecordName.get(),
aRecordName.Length());
if (bytesWritten != (int32_t)aRecordName.Length()) {
NS_WARNING("Failed to write GMPStorage record name.");
return GMPRecordCorrupted;
}
bytesWritten = PR_Write(record->mFileDesc, aBytes.Elements(), aBytes.Length());
if (bytesWritten != (int32_t)aBytes.Length()) {
NS_WARNING("Failed to write GMPStorage record data.");
return GMPRecordCorrupted;
}
// Try to sync the file to disk, so that in the event of a crash,
// the record is less likely to be corrupted.
PR_Sync(record->mFileDesc);
return GMPNoErr;
}
GMPErr GetRecordNames(nsTArray<nsCString>& aOutRecordNames) override
{
for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) {
aOutRecordNames.AppendElement(iter.UserData()->mRecordName);
}
return GMPNoErr;
}
void Close(const nsCString& aRecordName) override
{
Record* record = nullptr;
mRecords.Get(aRecordName, &record);
if (record && !!record->mFileDesc) {
PR_Close(record->mFileDesc);
record->mFileDesc = nullptr;
}
MOZ_ASSERT(!IsOpen(aRecordName));
}
private:
nsDataHashtable<nsCStringHashKey, PRFileDesc*> mFiles;
// We store records in a file which is a hash of the record name.
// If there is a hash collision, we just keep adding 1 to the hash
// code, until we find a free slot.
nsresult GetUnusedFilename(const nsACString& aRecordName,
nsString& aOutFilename)
{
nsCOMPtr<nsIFile> storageDir;
nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
uint64_t recordNameHash = HashString(PromiseFlatCString(aRecordName).get());
for (int i = 0; i < 1000000; i++) {
nsCOMPtr<nsIFile> f;
rv = storageDir->Clone(getter_AddRefs(f));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoString hashStr;
hashStr.AppendInt(recordNameHash);
rv = f->Append(hashStr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
bool exists = false;
f->Exists(&exists);
if (!exists) {
// Filename not in use, we can write into this file.
aOutFilename = hashStr;
return NS_OK;
} else {
// Hash collision; just increment the hash name and try that again.
++recordNameHash;
continue;
}
}
// Somehow, we've managed to completely fail to find a vacant file name.
// Give up.
NS_WARNING("GetUnusedFilename had extreme hash collision!");
return NS_ERROR_FAILURE;
}
enum OpenFileMode { ReadWrite, Truncate };
nsresult OpenStorageFile(const nsAString& aFileLeafName,
const OpenFileMode aMode,
PRFileDesc** aOutFD)
{
MOZ_ASSERT(aOutFD);
nsCOMPtr<nsIFile> f;
nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
f->Append(aFileLeafName);
auto mode = PR_RDWR | PR_CREATE_FILE;
if (aMode == Truncate) {
mode |= PR_TRUNCATE;
}
return f->OpenNSPRFileDesc(mode, PR_IRWXU, aOutFD);
}
nsresult ReadRecordMetadata(PRFileDesc* aFd,
int32_t& aOutRecordLength,
nsACString& aOutRecordName)
{
int32_t offset = PR_Seek(aFd, 0, PR_SEEK_END);
PR_Seek(aFd, 0, PR_SEEK_SET);
if (offset < 0 || offset > GMP_MAX_RECORD_SIZE) {
// Refuse to read big records, or records where we can't get a length.
return NS_ERROR_FAILURE;
}
const uint32_t fileLength = static_cast<uint32_t>(offset);
// At the start of the file the length of the record name is stored in a
// uint32_t (little endian byte order) followed by the record name at the
// start of the file. The record name is not null terminated. The remainder
// of the file is the record's data.
if (fileLength < sizeof(uint32_t)) {
// Record file doesn't have enough contents to store the record name
// length. Fail.
return NS_ERROR_FAILURE;
}
// Read length, and convert to host byte order.
uint32_t recordNameLength = 0;
char buf[sizeof(recordNameLength)] = { 0 };
int32_t bytesRead = PR_Read(aFd, &buf, sizeof(recordNameLength));
recordNameLength = LittleEndian::readUint32(buf);
if (sizeof(recordNameLength) != bytesRead ||
recordNameLength == 0 ||
recordNameLength + sizeof(recordNameLength) > fileLength ||
recordNameLength > GMP_MAX_RECORD_NAME_SIZE) {
// Record file has invalid contents. Fail.
return NS_ERROR_FAILURE;
}
nsCString recordName;
recordName.SetLength(recordNameLength);
bytesRead = PR_Read(aFd, recordName.BeginWriting(), recordNameLength);
if ((uint32_t)bytesRead != recordNameLength) {
// Read failed.
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(fileLength >= sizeof(recordNameLength) + recordNameLength);
int32_t recordLength = fileLength - (sizeof(recordNameLength) + recordNameLength);
aOutRecordLength = recordLength;
aOutRecordName = recordName;
// Read cursor should be positioned after the record name, before the record contents.
if (PR_Seek(aFd, 0, PR_SEEK_CUR) != (int32_t)(sizeof(recordNameLength) + recordNameLength)) {
NS_WARNING("Read cursor mismatch after ReadRecordMetadata()");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult RemoveStorageFile(const nsString& aFilename)
{
nsCOMPtr<nsIFile> f;
nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = f->Append(aFilename);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return f->Remove(/* bool recursive= */ false);
}
struct Record {
Record(const nsAString& aFilename,
const nsACString& aRecordName)
: mFilename(aFilename)
, mRecordName(aRecordName)
, mFileDesc(0)
{}
~Record() {
MOZ_ASSERT(!mFileDesc);
}
nsString mFilename;
nsCString mRecordName;
PRFileDesc* mFileDesc;
};
// Hash record name to record data.
nsClassHashtable<nsCStringHashKey, Record> mRecords;
const nsAutoCString mNodeId;
};
class GMPMemoryStorage : public GMPStorage {
public:
virtual GMPErr Open(const nsCString& aRecordName) override
GMPErr Open(const nsCString& aRecordName) override
{
MOZ_ASSERT(!IsOpen(aRecordName));
@ -408,7 +507,7 @@ public:
return GMPNoErr;
}
virtual bool IsOpen(const nsCString& aRecordName) override {
bool IsOpen(const nsCString& aRecordName) override {
Record* record = nullptr;
if (!mRecords.Get(aRecordName, &record)) {
return false;
@ -416,8 +515,8 @@ public:
return record->mIsOpen;
}
virtual GMPErr Read(const nsCString& aRecordName,
nsTArray<uint8_t>& aOutBytes) override
GMPErr Read(const nsCString& aRecordName,
nsTArray<uint8_t>& aOutBytes) override
{
Record* record = nullptr;
if (!mRecords.Get(aRecordName, &record)) {
@ -427,8 +526,8 @@ public:
return GMPNoErr;
}
virtual GMPErr Write(const nsCString& aRecordName,
const nsTArray<uint8_t>& aBytes) override
GMPErr Write(const nsCString& aRecordName,
const nsTArray<uint8_t>& aBytes) override
{
Record* record = nullptr;
if (!mRecords.Get(aRecordName, &record)) {
@ -438,13 +537,15 @@ public:
return GMPNoErr;
}
virtual GMPErr GetRecordNames(nsTArray<nsCString>& aOutRecordNames) override
GMPErr GetRecordNames(nsTArray<nsCString>& aOutRecordNames) override
{
mRecords.EnumerateRead(EnumRecordNames, &aOutRecordNames);
for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) {
aOutRecordNames.AppendElement(iter.Key());
}
return GMPNoErr;
}
virtual void Close(const nsCString& aRecordName) override
void Close(const nsCString& aRecordName) override
{
Record* record = nullptr;
if (!mRecords.Get(aRecordName, &record)) {
@ -466,16 +567,6 @@ private:
bool mIsOpen;
};
static PLDHashOperator
EnumRecordNames(const nsACString& aKey,
Record* aRecord,
void* aUserArg)
{
nsTArray<nsCString>* names = reinterpret_cast<nsTArray<nsCString>*>(aUserArg);
names->AppendElement(aKey);
return PL_DHASH_NEXT;
}
nsClassHashtable<nsCStringHashKey, Record> mRecords;
};
@ -504,7 +595,12 @@ GMPStorageParent::Init()
return NS_ERROR_FAILURE;
}
if (persistent) {
mStorage = MakeUnique<GMPDiskStorage>(mNodeId);
UniquePtr<GMPDiskStorage> storage = MakeUnique<GMPDiskStorage>(mNodeId);
if (NS_FAILED(storage->Init())) {
NS_WARNING("Failed to initialize on disk GMP storage");
return NS_ERROR_FAILURE;
}
mStorage = Move(storage);
} else {
mStorage = MakeUnique<GMPMemoryStorage>();
}

Просмотреть файл

@ -9,6 +9,7 @@
#include "nsIFile.h"
#include "nsCOMPtr.h"
#include "nsLiteralString.h"
#include "nsCRTGlue.h"
namespace mozilla {
@ -36,4 +37,17 @@ EMEVoucherFileExists()
exists;
}
void
SplitAt(const char* aDelims,
const nsACString& aInput,
nsTArray<nsCString>& aOutTokens)
{
nsAutoCString str(aInput);
char* end = str.BeginWriting();
const char* start = nullptr;
while (!!(start = NS_strtok(aDelims, &end))) {
aOutTokens.AppendElement(nsCString(start));
}
}
} // namespace mozilla

Просмотреть файл

@ -7,8 +7,10 @@
#define GMPUtils_h_
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
class nsIFile;
class nsCString;
namespace mozilla {
@ -27,6 +29,11 @@ bool GetEMEVoucherPath(nsIFile** aPath);
bool EMEVoucherFileExists();
void
SplitAt(const char* aDelims,
const nsACString& aInput,
nsTArray<nsCString>& aOutTokens);
} // namespace mozilla
#endif

Просмотреть файл

@ -48,6 +48,7 @@ typedef enum {
GMPEndOfEnumeration = 11,
GMPInvalidArgErr = 12,
GMPAbortedErr = 13,
GMPRecordCorrupted = 14,
GMPLastErr // Placeholder, must be last. This enum's values must remain consecutive!
} GMPErr;

Просмотреть файл

@ -12,6 +12,10 @@
#include "nsDirectoryServiceDefs.h"
#include "nsIObserverService.h"
#include "GMPVideoDecoderProxy.h"
#include "GMPServiceParent.h"
#include "GMPService.h"
#include "GMPUtils.h"
#include "mozilla/StaticPtr.h"
#define GMP_DIR_NAME NS_LITERAL_STRING("gmp-fakeopenh264")
#define GMP_OLD_VERSION NS_LITERAL_STRING("1.0")
@ -21,6 +25,9 @@
#define EXPECT_OK(X) EXPECT_TRUE(NS_SUCCEEDED(X))
using namespace mozilla;
using namespace mozilla::gmp;
class GMPRemoveTest : public nsIObserver
, public GMPVideoDecoderCallbackProxy
{

Просмотреть файл

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "gtest/gtest.h"
#include "GMPUtils.h"
#include "nsString.h"
#include <string>
#include <vector>
using namespace std;
using namespace mozilla;
void TestSplitAt(const char* aInput,
const char* aDelims,
size_t aNumExpectedTokens,
const char* aExpectedTokens[])
{
nsCString input(aInput);
nsTArray<nsCString> tokens;
SplitAt(aDelims, input, tokens);
EXPECT_EQ(tokens.Length(), aNumExpectedTokens) << "Should get expected number of tokens";
for (size_t i = 0; i < tokens.Length(); i++) {
EXPECT_TRUE(tokens[i].EqualsASCII(aExpectedTokens[i]))
<< "Tokenize fail; expected=" << aExpectedTokens[i] << " got=" <<
tokens[i].BeginReading();
}
}
TEST(GeckoMediaPlugins, GMPUtils) {
{
const char* input = "1,2,3,4";
const char* delims = ",";
const char* tokens[] = { "1", "2", "3", "4" };
TestSplitAt(input, delims, MOZ_ARRAY_LENGTH(tokens), tokens);
}
{
const char* input = "a simple, comma, seperated, list";
const char* delims = ",";
const char* tokens[] = { "a simple", " comma", " seperated", " list" };
TestSplitAt(input, delims, MOZ_ARRAY_LENGTH(tokens), tokens);
}
{
const char* input = // Various platform line endings...
"line1\r\n" // Windows
"line2\r" // Old MacOSX
"line3\n" // Unix
"line4";
const char* delims = "\r\n";
const char* tokens[] = { "line1", "line2", "line3", "line4" };
TestSplitAt(input, delims, MOZ_ARRAY_LENGTH(tokens), tokens);
}
}

Просмотреть файл

@ -776,4 +776,46 @@ TEST(IntervalSet, Substraction)
EXPECT_EQ(1u, i0.Length());
EXPECT_EQ(5, i0[0].mStart);
EXPECT_EQ(8, i0[0].mEnd);
i0 = IntIntervals();
i0 += IntInterval(0, 10);
IntIntervals i2;
i2 += IntInterval(4, 6);
i0 -= i2;
EXPECT_EQ(2u, i0.Length());
EXPECT_EQ(0, i0[0].mStart);
EXPECT_EQ(4, i0[0].mEnd);
EXPECT_EQ(6, i0[1].mStart);
EXPECT_EQ(10, i0[1].mEnd);
i0 = IntIntervals();
i0 += IntInterval(0, 1);
i0 += IntInterval(3, 10);
EXPECT_EQ(2u, i0.Length());
// This fuzz should collapse i0 into [0,10).
i0.SetFuzz(1);
EXPECT_EQ(1u, i0.Length());
EXPECT_EQ(1, i0[0].mFuzz);
i2 = IntInterval(4, 6);
i0 -= i2;
EXPECT_EQ(2u, i0.Length());
EXPECT_EQ(0, i0[0].mStart);
EXPECT_EQ(4, i0[0].mEnd);
EXPECT_EQ(6, i0[1].mStart);
EXPECT_EQ(10, i0[1].mEnd);
EXPECT_EQ(1, i0[0].mFuzz);
EXPECT_EQ(1, i0[1].mFuzz);
i0 = IntIntervals();
i0 += IntInterval(0, 10);
// [4,6) with fuzz 1 used to fail because the complementary interval set
// [0,4)+[6,10) would collapse into [0,10).
i2 = IntInterval(4, 6);
i2.SetFuzz(1);
i0 -= i2;
EXPECT_EQ(2u, i0.Length());
EXPECT_EQ(0, i0[0].mStart);
EXPECT_EQ(4, i0[0].mEnd);
EXPECT_EQ(6, i0[1].mStart);
EXPECT_EQ(10, i0[1].mEnd);
}

Просмотреть файл

@ -10,7 +10,8 @@
#include "mozilla/ArrayUtils.h"
#include "MockMediaResource.h"
using namespace mp3;
using namespace mozilla;
using namespace mozilla::mp3;
// Regular MP3 file mock resource.
class MockMP3MediaResource : public MockMediaResource {

Просмотреть файл

@ -12,6 +12,7 @@
#include "TaskQueue.h"
#include "mozilla/ArrayUtils.h"
#include "MockMediaResource.h"
#include "VideoUtils.h"
using namespace mozilla;
using namespace mp4_demuxer;

Просмотреть файл

@ -9,6 +9,7 @@ UNIFIED_SOURCES += [
'TestAudioCompactor.cpp',
'TestGMPCrossOrigin.cpp',
'TestGMPRemoveAndDelete.cpp',
'TestGMPUtils.cpp',
'TestIntervalSet.cpp',
'TestMediaEventSource.cpp',
'TestMozPromise.cpp',

Просмотреть файл

@ -8,6 +8,7 @@
#define PlatformDecoderModule_h_
#include "MediaDecoderReader.h"
#include "mozilla/MozPromise.h"
#include "mozilla/layers/LayersTypes.h"
#include "nsTArray.h"
#include "mozilla/RefPtr.h"
@ -207,16 +208,24 @@ protected:
virtual ~MediaDataDecoder() {};
public:
enum DecoderFailureReason {
INIT_ERROR,
CANCELED
};
typedef TrackInfo::TrackType TrackType;
typedef MozPromise<TrackType, DecoderFailureReason, /* IsExclusive = */ true> InitPromise;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataDecoder)
// Initialize the decoder. The decoder should be ready to decode after
// this returns. The decoder should do any initialization here, rather
// Initialize the decoder. The decoder should be ready to decode once
// promise resolves. The decoder should do any initialization here, rather
// than in its constructor or PlatformDecoderModule::Create*Decoder(),
// so that if the MP4Reader needs to shutdown during initialization,
// it can call Shutdown() to cancel this operation. Any initialization
// that requires blocking the calling thread in this function *must*
// be done here so that it can be canceled by calling Shutdown()!
virtual nsresult Init() = 0;
virtual nsRefPtr<InitPromise> Init() = 0;
// Inserts a sample into the decoder's decode pipeline.
virtual nsresult Input(MediaRawData* aSample) = 0;
@ -251,6 +260,8 @@ public:
virtual nsresult Shutdown() = 0;
// Called from the state machine task queue or main thread.
// Decoder needs to decide whether or not hardware accelearation is supported
// after creating. It doesn't need to call Init() before calling this function.
virtual bool IsHardwareAccelerated() const { return false; }
// ConfigurationChanged will be called to inform the video or audio decoder

Просмотреть файл

@ -73,6 +73,7 @@ SharedDecoderManager::SharedDecoderManager()
: mTaskQueue(new FlushableTaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER)))
, mActiveProxy(nullptr)
, mActiveCallback(nullptr)
, mInit(false)
, mWaitForInternalDrain(false)
, mMonitor("SharedDecoderManager")
, mDecoderReleasedResources(false)
@ -111,11 +112,7 @@ SharedDecoderManager::CreateVideoDecoder(
mPDM = nullptr;
return nullptr;
}
nsresult rv = mDecoder->Init();
if (NS_FAILED(rv)) {
mDecoder = nullptr;
return nullptr;
}
mPDM = aPDM;
}
@ -143,8 +140,8 @@ SharedDecoderManager::Recreate(const VideoInfo& aConfig)
if (!mDecoder) {
return false;
}
nsresult rv = mDecoder->Init();
return rv == NS_OK;
mInit = false;
return true;
}
void
@ -181,6 +178,35 @@ SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy)
}
}
nsRefPtr<MediaDataDecoder::InitPromise>
SharedDecoderManager::InitDecoder()
{
if (!mInit && mDecoder) {
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
nsRefPtr<SharedDecoderManager> self = this;
nsRefPtr<MediaDataDecoder::InitPromise> p = mDecoderInitPromise.Ensure(__func__);
// The mTaskQueue is flushable which can't be used in MediaPromise. So we get
// the current AbstractThread instead of it. The MOZ_ASSERT above ensures
// we are running in AbstractThread so we won't get a nullptr.
mDecoderInitPromiseRequest.Begin(
mDecoder->Init()->Then(AbstractThread::GetCurrent(), __func__,
[self] (TrackInfo::TrackType aType) -> void {
self->mDecoderInitPromiseRequest.Complete();
self->mInit = true;
self->mDecoderInitPromise.ResolveIfExists(aType, __func__);
},
[self] (MediaDataDecoder::DecoderFailureReason aReason) -> void {
self->mDecoderInitPromiseRequest.Complete();
self->mDecoderInitPromise.RejectIfExists(aReason, __func__);
}));
return p;
}
return MediaDataDecoder::InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
}
void
SharedDecoderManager::DrainComplete()
{
@ -211,6 +237,7 @@ SharedDecoderManager::Shutdown()
mTaskQueue->AwaitShutdownAndIdle();
mTaskQueue = nullptr;
}
mDecoderInitPromiseRequest.DisconnectIfExists();
}
SharedDecoderProxy::SharedDecoderProxy(SharedDecoderManager* aManager,
@ -225,10 +252,14 @@ SharedDecoderProxy::~SharedDecoderProxy()
Shutdown();
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
SharedDecoderProxy::Init()
{
return NS_OK;
if (mManager->mActiveProxy != this) {
mManager->Select(this);
}
return mManager->InitDecoder();
}
nsresult

Просмотреть файл

@ -48,6 +48,8 @@ private:
virtual ~SharedDecoderManager();
void DrainComplete();
nsRefPtr<MediaDataDecoder::InitPromise> InitDecoder();
nsRefPtr<PlatformDecoderModule> mPDM;
nsRefPtr<MediaDataDecoder> mDecoder;
layers::LayersBackend mLayersBackend;
@ -56,6 +58,9 @@ private:
SharedDecoderProxy* mActiveProxy;
MediaDataDecoderCallback* mActiveCallback;
nsAutoPtr<MediaDataDecoderCallback> mCallback;
MozPromiseHolder<MediaDataDecoder::InitPromise> mDecoderInitPromise;
MozPromiseRequestHolder<MediaDataDecoder::InitPromise> mDecoderInitPromiseRequest;
bool mInit;
// access protected by mMonitor
bool mWaitForInternalDrain;
Monitor mMonitor;
@ -69,7 +74,7 @@ public:
MediaDataDecoderCallback* aCallback);
virtual ~SharedDecoderProxy();
virtual nsresult Init() override;
virtual nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Flush() override;
virtual nsresult Drain() override;

Просмотреть файл

@ -25,15 +25,17 @@ public:
BlankMediaDataDecoder(BlankMediaDataCreator* aCreator,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
MediaDataDecoderCallback* aCallback,
TrackInfo::TrackType aType)
: mCreator(aCreator)
, mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mType(aType)
{
}
virtual nsresult Init() override {
return NS_OK;
virtual nsRefPtr<InitPromise> Init() override {
return InitPromise::CreateAndResolve(mType, __func__);
}
virtual nsresult Shutdown() override {
@ -89,6 +91,7 @@ private:
nsAutoPtr<BlankMediaDataCreator> mCreator;
RefPtr<FlushableTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
TrackInfo::TrackType mType;
};
class BlankVideoDataCreator {
@ -221,7 +224,8 @@ public:
nsRefPtr<MediaDataDecoder> decoder =
new BlankMediaDataDecoder<BlankVideoDataCreator>(creator,
aVideoTaskQueue,
aCallback);
aCallback,
TrackInfo::kVideoTrack);
return decoder.forget();
}
@ -236,7 +240,8 @@ public:
nsRefPtr<MediaDataDecoder> decoder =
new BlankMediaDataDecoder<BlankAudioDataCreator>(creator,
aAudioTaskQueue,
aCallback);
aCallback,
TrackInfo::kAudioTrack);
return decoder.forget();
}

Просмотреть файл

@ -47,19 +47,19 @@ OpusDataDecoder::Shutdown()
return NS_OK;
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
OpusDataDecoder::Init()
{
size_t length = mInfo.mCodecSpecificConfig->Length();
uint8_t *p = mInfo.mCodecSpecificConfig->Elements();
if (length < sizeof(uint64_t)) {
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
int64_t codecDelay = BigEndian::readUint64(p);
length -= sizeof(uint64_t);
p += sizeof(uint64_t);
if (NS_FAILED(DecodeHeader(p, length))) {
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
int r;
@ -75,7 +75,7 @@ OpusDataDecoder::Init()
if (codecDelay != FramesToUsecs(mOpusParser->mPreSkip,
mOpusParser->mRate).value()) {
NS_WARNING("Invalid Opus header: CodecDelay and pre-skip do not match!");
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
if (mInfo.mRate != (uint32_t)mOpusParser->mRate) {
@ -85,7 +85,8 @@ OpusDataDecoder::Init()
NS_WARNING("Invalid Opus header: container and codec channels do not match!");
}
return r == OPUS_OK ? NS_OK : NS_ERROR_FAILURE;
return r == OPUS_OK ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
: InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
nsresult

Просмотреть файл

@ -21,7 +21,7 @@ public:
MediaDataDecoderCallback* aCallback);
~OpusDataDecoder();
nsresult Init() override;
nsRefPtr<InitPromise> Init() override;
nsresult Input(MediaRawData* aSample) override;
nsresult Flush() override;
nsresult Drain() override;

Просмотреть файл

@ -55,7 +55,7 @@ VPXDecoder::Shutdown()
return NS_OK;
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
VPXDecoder::Init()
{
vpx_codec_iface_t* dx = nullptr;
@ -65,9 +65,9 @@ VPXDecoder::Init()
dx = vpx_codec_vp9_dx();
}
if (!dx || vpx_codec_dec_init(&mVPX, dx, nullptr, 0)) {
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
return NS_OK;
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
}
nsresult

Просмотреть файл

@ -28,7 +28,7 @@ public:
~VPXDecoder();
nsresult Init() override;
nsRefPtr<InitPromise> Init() override;
nsresult Input(MediaRawData* aSample) override;
nsresult Flush() override;
nsresult Drain() override;

Просмотреть файл

@ -63,7 +63,7 @@ VorbisDataDecoder::Shutdown()
return NS_OK;
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
VorbisDataDecoder::Init()
{
vorbis_info_init(&mVorbisInfo);
@ -75,17 +75,17 @@ VorbisDataDecoder::Init()
uint8_t *p = mInfo.mCodecSpecificConfig->Elements();
for(int i = 0; i < 3; i++) {
if (available < 2) {
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
available -= 2;
size_t length = BigEndian::readUint16(p);
p += 2;
if (available < length) {
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
available -= length;
if (NS_FAILED(DecodeHeader((const unsigned char*)p, length))) {
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
p += length;
}
@ -94,12 +94,12 @@ VorbisDataDecoder::Init()
int r = vorbis_synthesis_init(&mVorbisDsp, &mVorbisInfo);
if (r) {
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
r = vorbis_block_init(&mVorbisDsp, &mVorbisBlock);
if (r) {
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
if (mInfo.mRate != (uint32_t)mVorbisDsp.vi->rate) {
@ -111,7 +111,7 @@ VorbisDataDecoder::Init()
("Invalid Vorbis header: container and codec channels do not match!"));
}
return NS_OK;
return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
}
nsresult

Просмотреть файл

@ -24,7 +24,7 @@ public:
MediaDataDecoderCallback* aCallback);
~VorbisDataDecoder();
nsresult Init() override;
nsRefPtr<InitPromise> Init() override;
nsresult Input(MediaRawData* aSample) override;
nsresult Flush() override;
nsresult Drain() override;

Просмотреть файл

@ -45,7 +45,7 @@ public:
{
}
virtual nsresult Init() override {
virtual nsRefPtr<InitPromise> Init() override {
MOZ_ASSERT(!mIsShutdown);
return mDecoder->Init();
}

Просмотреть файл

@ -167,7 +167,7 @@ GMPAudioDecoder::GMPInitDone(GMPAudioDecoderProxy* aGMP)
}
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
GMPAudioDecoder::Init()
{
MOZ_ASSERT(IsOnGMPThread());
@ -188,7 +188,8 @@ GMPAudioDecoder::Init()
NS_ProcessNextEvent(gmpThread, true);
}
return mGMP ? NS_OK : NS_ERROR_FAILURE;
return mGMP ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
: InitPromise::CreateAndReject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
}
nsresult

Просмотреть файл

@ -69,7 +69,7 @@ public:
{
}
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Flush() override;
virtual nsresult Drain() override;

Просмотреть файл

@ -211,7 +211,7 @@ GMPVideoDecoder::GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
}
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
GMPVideoDecoder::Init()
{
MOZ_ASSERT(IsOnGMPThread());
@ -232,7 +232,8 @@ GMPVideoDecoder::Init()
NS_ProcessNextEvent(gmpThread, true);
}
return mGMP ? NS_OK : NS_ERROR_FAILURE;
return mGMP ? InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__)
: InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
nsresult

Просмотреть файл

@ -84,7 +84,7 @@ public:
{
}
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Flush() override;
virtual nsresult Drain() override;

Просмотреть файл

@ -21,16 +21,21 @@ MediaDataDecoderCallbackProxy::FlushComplete()
mProxyDecoder->FlushComplete();
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
MediaDataDecoderProxy::InternalInit()
{
MOZ_ASSERT(!mIsShutdown);
return mProxyDecoder->Init();
}
nsRefPtr<MediaDataDecoder::InitPromise>
MediaDataDecoderProxy::Init()
{
MOZ_ASSERT(!mIsShutdown);
nsRefPtr<InitTask> task(new InitTask(mProxyDecoder));
nsresult rv = mProxyThread->Dispatch(task, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_SUCCESS(task->Result(), task->Result());
return NS_OK;
return ProxyMediaCall(mProxyThreadWrapper, this, __func__,
&MediaDataDecoderProxy::InternalInit);
}
nsresult

Просмотреть файл

@ -32,30 +32,6 @@ private:
nsRefPtr<MediaRawData> mSample;
};
class InitTask : public nsRunnable {
public:
explicit InitTask(MediaDataDecoder* aDecoder)
: mDecoder(aDecoder)
, mResultValid(false)
{}
NS_IMETHOD Run() {
mResult = mDecoder->Init();
mResultValid = true;
return NS_OK;
}
nsresult Result() {
MOZ_ASSERT(mResultValid);
return mResult;
}
private:
MediaDataDecoder* mDecoder;
nsresult mResult;
bool mResultValid;
};
template<typename T>
class Condition {
public:
@ -132,6 +108,7 @@ public:
, mIsShutdown(false)
#endif
{
mProxyThreadWrapper = CreateXPCOMAbstractThreadWrapper(aProxyThread, false);
}
// Ideally, this would return a regular MediaDataDecoderCallback pointer
@ -155,7 +132,7 @@ public:
// Init and Shutdown run synchronously on the proxy thread, all others are
// asynchronously and responded to via the MediaDataDecoderCallback.
// Note: the nsresults returned by the proxied decoder are lost.
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Flush() override;
virtual nsresult Drain() override;
@ -165,6 +142,8 @@ public:
void FlushComplete();
private:
nsRefPtr<InitPromise> InternalInit();
#ifdef DEBUG
bool IsOnProxyThread() {
return NS_GetCurrentThread() == mProxyThread;
@ -176,6 +155,7 @@ private:
nsRefPtr<MediaDataDecoder> mProxyDecoder;
nsCOMPtr<nsIThread> mProxyThread;
nsRefPtr<AbstractThread> mProxyThreadWrapper;
MediaDataDecoderCallbackProxy mProxyCallback;

Просмотреть файл

@ -53,14 +53,18 @@ public:
}
nsresult Init() override {
nsRefPtr<InitPromise> Init() override {
mSurfaceTexture = AndroidSurfaceTexture::Create();
if (!mSurfaceTexture) {
NS_WARNING("Failed to create SurfaceTexture for video decode\n");
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
return InitDecoder(mSurfaceTexture->JavaSurface());
if (NS_FAILED(InitDecoder(mSurfaceTexture->JavaSurface()))) {
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
}
void Cleanup() override {
@ -349,9 +353,17 @@ MediaCodecDataDecoder::~MediaCodecDataDecoder()
Shutdown();
}
nsresult MediaCodecDataDecoder::Init()
nsRefPtr<MediaDataDecoder::InitPromise> MediaCodecDataDecoder::Init()
{
return InitDecoder(nullptr);
nsresult rv = InitDecoder(nullptr);
TrackInfo::TrackType type =
(mType == MediaData::AUDIO_DATA ? TrackInfo::TrackType::kAudioTrack
: TrackInfo::TrackType::kVideoTrack);
return NS_SUCCEEDED(rv) ?
InitPromise::CreateAndResolve(type, __func__) :
InitPromise::CreateAndReject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
}
nsresult MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface)

Просмотреть файл

@ -52,7 +52,7 @@ public:
virtual ~MediaCodecDataDecoder();
virtual nsresult Init() override;
virtual nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
virtual nsresult Flush() override;
virtual nsresult Drain() override;
virtual nsresult Shutdown() override;

Просмотреть файл

@ -50,14 +50,15 @@ AppleATDecoder::~AppleATDecoder()
MOZ_ASSERT(!mConverter);
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
AppleATDecoder::Init()
{
if (!mFormatID) {
NS_ERROR("Non recognised format");
return NS_ERROR_FAILURE;
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
return NS_OK;
return InitPromise::CreateAndResolve(TrackType::kAudioTrack, __func__);
}
nsresult

Просмотреть файл

@ -25,7 +25,7 @@ public:
MediaDataDecoderCallback* aCallback);
virtual ~AppleATDecoder();
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Flush() override;
virtual nsresult Drain() override;

Просмотреть файл

@ -73,19 +73,10 @@ AppleVDADecoder::~AppleVDADecoder()
MOZ_COUNT_DTOR(AppleVDADecoder);
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
AppleVDADecoder::Init()
{
if (!gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding()) {
// This GPU is blacklisted for hardware decoding.
return NS_ERROR_FAILURE;
}
if (mDecoder) {
return NS_OK;
}
nsresult rv = InitializeSession();
return rv;
return InitPromise::CreateAndResolve(TrackType::kVideoTrack, __func__);
}
nsresult
@ -580,11 +571,18 @@ AppleVDADecoder::CreateVDADecoder(
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
{
nsRefPtr<AppleVDADecoder> decoder =
new AppleVDADecoder(aConfig, aVideoTaskQueue, aCallback, aImageContainer);
if (NS_FAILED(decoder->Init())) {
if (!gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding()) {
// This GPU is blacklisted for hardware decoding.
return nullptr;
}
nsRefPtr<AppleVDADecoder> decoder =
new AppleVDADecoder(aConfig, aVideoTaskQueue, aCallback, aImageContainer);
if (NS_FAILED(decoder->InitializeSession())) {
return nullptr;
}
return decoder.forget();
}

Просмотреть файл

@ -70,7 +70,7 @@ public:
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
virtual ~AppleVDADecoder();
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Flush() override;
virtual nsresult Drain() override;
@ -83,11 +83,15 @@ public:
nsresult OutputFrame(CVPixelBufferRef aImage,
nsAutoPtr<AppleFrameRef> aFrameRef);
// Method to set up the decompression session.
nsresult InitializeSession();
protected:
AppleFrameRef* CreateAppleFrameRef(const MediaRawData* aSample);
void DrainReorderedFrames();
void ClearReorderedFrames();
CFDictionaryRef CreateOutputConfiguration();
nsresult InitDecoder();
nsRefPtr<MediaByteBuffer> mExtraData;
nsRefPtr<FlushableTaskQueue> mTaskQueue;
@ -107,8 +111,6 @@ private:
// Method to pass a frame to VideoToolbox for decoding.
nsresult SubmitFrame(MediaRawData* aSample);
// Method to set up the decompression session.
nsresult InitializeSession();
CFDictionaryRef CreateDecoderSpecification();
};

Просмотреть файл

@ -51,11 +51,16 @@ AppleVTDecoder::~AppleVTDecoder()
MOZ_COUNT_DTOR(AppleVTDecoder);
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
AppleVTDecoder::Init()
{
nsresult rv = InitializeSession();
return rv;
if (NS_SUCCEEDED(rv)) {
return InitPromise::CreateAndResolve(TrackType::kVideoTrack, __func__);
}
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
nsresult

Просмотреть файл

@ -20,7 +20,7 @@ public:
MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer);
virtual ~AppleVTDecoder();
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Flush() override;
virtual nsresult Drain() override;

Просмотреть файл

@ -27,13 +27,13 @@ FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
mExtraData->AppendElements(*aConfig.mCodecSpecificConfig);
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
FFmpegAudioDecoder<LIBAV_VER>::Init()
{
nsresult rv = FFmpegDataDecoder::Init();
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = InitDecoder();
return NS_OK;
return rv == NS_OK ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
: InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
static AudioDataValue*

Просмотреть файл

@ -25,7 +25,7 @@ public:
const AudioInfo& aConfig);
virtual ~FFmpegAudioDecoder();
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Drain() override;
static AVCodecID GetCodecId(const nsACString& aMimeType);

Просмотреть файл

@ -58,7 +58,7 @@ ChoosePixelFormat(AVCodecContext* aCodecContext, const PixelFormat* aFormats)
}
nsresult
FFmpegDataDecoder<LIBAV_VER>::Init()
FFmpegDataDecoder<LIBAV_VER>::InitDecoder()
{
StaticMutexAutoLock mon(sMonitor);

Просмотреть файл

@ -28,7 +28,7 @@ public:
static bool Link();
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override = 0;
virtual nsresult Input(MediaRawData* aSample) override = 0;
virtual nsresult Flush() override;
virtual nsresult Drain() override = 0;
@ -36,6 +36,7 @@ public:
protected:
AVFrame* PrepareFrame();
nsresult InitDecoder();
FlushableTaskQueue* mTaskQueue;
AVCodecContext* mCodecContext;

Просмотреть файл

@ -39,16 +39,17 @@ FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
mExtraData->AppendElements(*aConfig.mExtraData);
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
FFmpegH264Decoder<LIBAV_VER>::Init()
{
nsresult rv = FFmpegDataDecoder::Init();
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(InitDecoder())) {
return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
}
mCodecContext->get_buffer = AllocateBufferCb;
mCodecContext->release_buffer = ReleaseBufferCb;
return NS_OK;
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
}
int64_t

Просмотреть файл

@ -36,7 +36,7 @@ public:
ImageContainer* aImageContainer);
virtual ~FFmpegH264Decoder();
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Drain() override;
virtual nsresult Flush() override;

Просмотреть файл

@ -37,6 +37,8 @@ public:
virtual bool HasQueuedSample() override;
virtual TrackType GetTrackType() override { return TrackType::kAudioTrack; }
private:
nsresult CreateAudioData(int64_t aStreamOffset,
AudioData** aOutData);

Просмотреть файл

@ -37,7 +37,7 @@ GonkMediaDataDecoder::~GonkMediaDataDecoder()
MOZ_COUNT_DTOR(GonkMediaDataDecoder);
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
GonkMediaDataDecoder::Init()
{
sp<MediaCodecProxy> decoder;
@ -45,7 +45,7 @@ GonkMediaDataDecoder::Init()
mDecoder = decoder;
mDrainComplete = false;
return NS_OK;
return InitPromise::CreateAndResolve(mManager->GetTrackType(), __func__);
}
nsresult

Просмотреть файл

@ -18,6 +18,8 @@ class MediaRawData;
// Manage the data flow from inputting encoded data and outputting decode data.
class GonkDecoderManager {
public:
typedef TrackInfo::TrackType TrackType;
virtual ~GonkDecoderManager() {}
// Creates and initializs the GonkDecoder.
@ -42,6 +44,8 @@ public:
// True if sample is queued.
virtual bool HasQueuedSample() = 0;
virtual TrackType GetTrackType() = 0;
protected:
nsRefPtr<MediaByteBuffer> mCodecSpecificData;
@ -61,9 +65,9 @@ public:
~GonkMediaDataDecoder();
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample);
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Flush() override;

Просмотреть файл

@ -55,6 +55,8 @@ public:
virtual bool HasQueuedSample() override;
virtual TrackType GetTrackType() override { return TrackType::kVideoTrack; }
static void RecycleCallback(TextureClient* aClient, void* aClosure);
private:

Просмотреть файл

@ -31,7 +31,7 @@ const GUID MF_XVP_PLAYBACK_MODE =
{ 0xaf, 0x12, 0xcf, 0x3e, 0x23, 0x8a, 0xcc, 0xe9 }
};
DEFINE_GUID(MF_LOW_LATENCY,
DEFINE_GUID(MF_LOW_LATENCY,
0x9c27891a, 0xed7a, 0x40e1, 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee);
namespace mozilla {

Просмотреть файл

@ -31,6 +31,10 @@ public:
virtual void Shutdown() override;
virtual TrackInfo::TrackType GetType() override {
return TrackInfo::kAudioTrack;
}
private:
HRESULT UpdateOutputType();

Просмотреть файл

@ -8,6 +8,7 @@
#include "WMFDecoderModule.h"
#include "WMFVideoMFTManager.h"
#include "WMFAudioMFTManager.h"
#include "MFTDecoder.h"
#include "mozilla/Preferences.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Services.h"
@ -97,13 +98,21 @@ WMFDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
FlushableTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback)
{
nsAutoPtr<WMFVideoMFTManager> manager =
new WMFVideoMFTManager(aConfig,
aLayersBackend,
aImageContainer,
sDXVAEnabled && ShouldUseDXVA(aConfig));
nsRefPtr<MFTDecoder> mft = manager->Init();
if (!mft) {
return nullptr;
}
nsRefPtr<MediaDataDecoder> decoder =
new WMFMediaDataDecoder(new WMFVideoMFTManager(aConfig,
aLayersBackend,
aImageContainer,
sDXVAEnabled && ShouldUseDXVA(aConfig)),
aVideoTaskQueue,
aCallback);
new WMFMediaDataDecoder(manager.forget(), mft, aVideoTaskQueue, aCallback);
return decoder.forget();
}
@ -112,10 +121,15 @@ WMFDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
{
nsAutoPtr<WMFAudioMFTManager> manager = new WMFAudioMFTManager(aConfig);
nsRefPtr<MFTDecoder> mft = manager->Init();
if (!mft) {
return nullptr;
}
nsRefPtr<MediaDataDecoder> decoder =
new WMFMediaDataDecoder(new WMFAudioMFTManager(aConfig),
aAudioTaskQueue,
aCallback);
new WMFMediaDataDecoder(manager.forget(), mft, aAudioTaskQueue, aCallback);
return decoder.forget();
}

Просмотреть файл

@ -18,10 +18,12 @@ PRLogModuleInfo* GetDemuxerLog();
namespace mozilla {
WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager,
MFTDecoder* aDecoder,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
: mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mDecoder(aDecoder)
, mMFTManager(aMFTManager)
, mMonitor("WMFMediaDataDecoder")
, mIsFlushing(false)
@ -33,16 +35,14 @@ WMFMediaDataDecoder::~WMFMediaDataDecoder()
{
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
WMFMediaDataDecoder::Init()
{
MOZ_ASSERT(!mDecoder);
MOZ_ASSERT(!mIsShutDown);
mDecoder = mMFTManager->Init();
NS_ENSURE_TRUE(mDecoder, NS_ERROR_FAILURE);
return NS_OK;
return mDecoder ?
InitPromise::CreateAndResolve(mMFTManager->GetType(), __func__) :
InitPromise::CreateAndReject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
}
// A single telemetry sample is reported for each MediaDataDecoder object

Просмотреть файл

@ -45,6 +45,8 @@ public:
virtual bool IsHardwareAccelerated() const { return false; }
virtual TrackInfo::TrackType GetType() = 0;
};
// Decodes audio and video using Windows Media Foundation. Samples are decoded
@ -55,11 +57,12 @@ public:
class WMFMediaDataDecoder : public MediaDataDecoder {
public:
WMFMediaDataDecoder(MFTManager* aOutputSource,
MFTDecoder* aDecoder,
FlushableTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback);
~WMFMediaDataDecoder();
virtual nsresult Init() override;
virtual nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample);

Просмотреть файл

@ -36,6 +36,10 @@ public:
virtual bool IsHardwareAccelerated() const override;
virtual TrackInfo::TrackType GetType() override {
return TrackInfo::kVideoTrack;
}
private:
bool InitializeDXVA(bool aForceD3D9);

Просмотреть файл

@ -29,6 +29,7 @@ H264Converter::H264Converter(PlatformDecoderModule* aPDM,
, mCallback(aCallback)
, mDecoder(nullptr)
, mNeedAVCC(aPDM->DecoderNeedsConversion(aConfig) == PlatformDecoderModule::kNeedAVCC)
, mDecoderInitializing(false)
, mLastError(NS_OK)
{
CreateDecoder();
@ -38,13 +39,15 @@ H264Converter::~H264Converter()
{
}
nsresult
nsRefPtr<MediaDataDecoder::InitPromise>
H264Converter::Init()
{
if (mDecoder) {
return mDecoder->Init();
}
return mLastError;
return MediaDataDecoder::InitPromise::CreateAndReject(
MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
}
nsresult
@ -59,6 +62,12 @@ H264Converter::Input(MediaRawData* aSample)
return NS_ERROR_FAILURE;
}
}
if (mDecoderInitializing) {
mMediaRawSamples.AppendElement(aSample);
return NS_OK;
}
nsresult rv;
if (!mDecoder) {
// It is not possible to create an AVCC H264 decoder without SPS.
@ -104,6 +113,7 @@ H264Converter::Shutdown()
{
if (mDecoder) {
nsresult rv = mDecoder->Shutdown();
mInitPromiseRequest.DisconnectIfExists();
mDecoder = nullptr;
return rv;
}
@ -151,8 +161,40 @@ H264Converter::CreateDecoderAndInit(MediaRawData* aSample)
UpdateConfigFromExtraData(extra_data);
nsresult rv = CreateDecoder();
NS_ENSURE_SUCCESS(rv, rv);
return Init();
if (NS_SUCCEEDED(rv)) {
mDecoderInitializing = true;
nsRefPtr<H264Converter> self = this;
// The mVideoTaskQueue is flushable which can't be used in MediaPromise. So
// we get the current AbstractThread instead of it. The MOZ_ASSERT above
// ensures we are running in AbstractThread so we won't get a nullptr.
mInitPromiseRequest.Begin(mDecoder->Init()
->Then(AbstractThread::GetCurrent(), __func__, this,
&H264Converter::OnDecoderInitDone,
&H264Converter::OnDecoderInitFailed));
}
return rv;
}
void
H264Converter::OnDecoderInitDone(const TrackType aTrackType)
{
mInitPromiseRequest.Complete();
for (uint32_t i = 0 ; i < mMediaRawSamples.Length(); i++) {
if (NS_FAILED(mDecoder->Input(mMediaRawSamples[i]))) {
mCallback->Error();
}
}
mMediaRawSamples.Clear();
mDecoderInitializing = false;
}
void
H264Converter::OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason)
{
mInitPromiseRequest.Complete();
mCallback->Error();
}
nsresult

Просмотреть файл

@ -29,7 +29,7 @@ public:
MediaDataDecoderCallback* aCallback);
virtual ~H264Converter();
virtual nsresult Init() override;
virtual nsRefPtr<InitPromise> Init() override;
virtual nsresult Input(MediaRawData* aSample) override;
virtual nsresult Flush() override;
virtual nsresult Drain() override;
@ -48,14 +48,20 @@ private:
nsresult CheckForSPSChange(MediaRawData* aSample);
void UpdateConfigFromExtraData(MediaByteBuffer* aExtraData);
void OnDecoderInitDone(const TrackType aTrackType);
void OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason);
nsRefPtr<PlatformDecoderModule> mPDM;
VideoInfo mCurrentConfig;
layers::LayersBackend mLayersBackend;
nsRefPtr<layers::ImageContainer> mImageContainer;
nsRefPtr<FlushableTaskQueue> mVideoTaskQueue;
nsTArray<nsRefPtr<MediaRawData>> mMediaRawSamples;
MediaDataDecoderCallback* mCallback;
nsRefPtr<MediaDataDecoder> mDecoder;
MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
bool mNeedAVCC;
bool mDecoderInitializing;
nsresult mLastError;
};

Просмотреть файл

@ -173,6 +173,8 @@ AudioNode::DisconnectFromGraph()
// It doesn't matter which one we remove, since we're going to remove all
// entries for this node anyway.
output->mInputNodes.RemoveElementAt(inputIndex);
// This effects of this connection will remain.
output->NotifyHasPhantomInput();
}
while (!mOutputParams.IsEmpty()) {
@ -227,6 +229,7 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
static_cast<uint16_t>(aInput),
static_cast<uint16_t>(aOutput));
}
aDestination.NotifyInputsChanged();
// This connection may have connected a panner and a source.
Context()->UpdatePannerSource();
@ -351,6 +354,7 @@ AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
// could be for different output ports.
nsRefPtr<AudioNode> output = mOutputNodes[i].forget();
mOutputNodes.RemoveElementAt(i);
output->NotifyInputsChanged();
if (mStream) {
nsRefPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
mStream->RunAfterPendingUpdates(runnable.forget());

Просмотреть файл

@ -97,6 +97,15 @@ public:
virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv);
// Called after input nodes have been explicitly added or removed through
// the Connect() or Disconnect() methods.
virtual void NotifyInputsChanged() {}
// Indicate that the node should continue indefinitely to behave as if an
// input is connected, even though there is no longer a corresponding entry
// in mInputNodes. Called after an input node has been removed because it
// is being garbage collected.
virtual void NotifyHasPhantomInput() {}
// The following two virtual methods must be implemented by each node type
// to provide their number of input and output ports. These numbers are
// constant for the lifetime of the node. Both default to 1.

Просмотреть файл

@ -268,6 +268,21 @@ public:
return mSharedBuffers;
}
enum {
IS_CONNECTED,
};
virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
{
switch (aIndex) {
case IS_CONNECTED:
mIsConnected = aParam;
break;
default:
NS_ERROR("Bad Int32Parameter");
} // End index switch.
}
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
@ -276,8 +291,7 @@ public:
// This node is not connected to anything. Per spec, we don't fire the
// onaudioprocess event. We also want to clear out the input and output
// buffer queue, and output a null buffer.
if (!(aStream->ConsumerCount() ||
aStream->AsProcessedStream()->InputPortCount())) {
if (!mIsConnected) {
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
mSharedBuffers->Reset();
mSeenNonSilenceInput = false;
@ -379,19 +393,42 @@ private:
NS_IMETHOD Run() override
{
nsRefPtr<ScriptProcessorNode> node = static_cast<ScriptProcessorNode*>
(mStream->Engine()->NodeMainThread());
if (!node) {
return NS_OK;
nsRefPtr<ThreadSharedFloatArrayBufferList> output;
auto engine =
static_cast<ScriptProcessorNodeEngine*>(mStream->Engine());
{
auto node = static_cast<ScriptProcessorNode*>
(engine->NodeMainThread());
if (!node) {
return NS_OK;
}
if (node->HasListenersFor(nsGkAtoms::onaudioprocess)) {
output = DispatchAudioProcessEvent(node);
}
// The node may have been destroyed during event dispatch.
}
AudioContext* context = node->Context();
// Append it to our output buffer queue
engine->GetSharedBuffers()->
FinishProducingOutputBuffer(output, engine->mBufferSize);
return NS_OK;
}
// Returns the output buffers if set in event handlers.
ThreadSharedFloatArrayBufferList*
DispatchAudioProcessEvent(ScriptProcessorNode* aNode)
{
AudioContext* context = aNode->Context();
if (!context) {
return NS_OK;
return nullptr;
}
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(node->GetOwner()))) {
return NS_OK;
if (NS_WARN_IF(!jsapi.Init(aNode->GetOwner()))) {
return nullptr;
}
JSContext* cx = jsapi.cx();
@ -401,10 +438,10 @@ private:
ErrorResult rv;
inputBuffer =
AudioBuffer::Create(context, mInputChannels.Length(),
node->BufferSize(),
aNode->BufferSize(),
context->SampleRate(), cx, rv);
if (rv.Failed()) {
return NS_OK;
return nullptr;
}
// Put the channel data inside it
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
@ -417,33 +454,27 @@ private:
// avoid creating the input buffer as well. The AudioProcessingEvent class
// knows how to lazily create them if needed once the script tries to access
// them. Otherwise, we may be able to get away without creating them!
nsRefPtr<AudioProcessingEvent> event = new AudioProcessingEvent(node, nullptr, nullptr);
nsRefPtr<AudioProcessingEvent> event =
new AudioProcessingEvent(aNode, nullptr, nullptr);
event->InitEvent(inputBuffer,
mInputChannels.Length(),
context->StreamTimeToDOMTime(mPlaybackTime));
node->DispatchTrustedEvent(event);
aNode->DispatchTrustedEvent(event);
// Steal the output buffers if they have been set.
// Don't create a buffer if it hasn't been used to return output;
// FinishProducingOutputBuffer() will optimize output = null.
// GetThreadSharedChannelsForRate() may also return null after OOM.
nsRefPtr<ThreadSharedFloatArrayBufferList> output;
if (event->HasOutputBuffer()) {
ErrorResult rv;
AudioBuffer* buffer = event->GetOutputBuffer(rv);
// HasOutputBuffer() returning true means that GetOutputBuffer()
// will not fail.
MOZ_ASSERT(!rv.Failed());
output = buffer->GetThreadSharedChannelsForRate(cx);
return buffer->GetThreadSharedChannelsForRate(cx);
}
// Append it to our output buffer queue
auto engine =
static_cast<ScriptProcessorNodeEngine*>(mStream->Engine());
engine->GetSharedBuffers()->
FinishProducingOutputBuffer(output, node->BufferSize());
return NS_OK;
return nullptr;
}
private:
nsRefPtr<AudioNodeStream> mStream;
@ -466,6 +497,7 @@ private:
const uint32_t mBufferSize;
// The write index into the current input buffer
uint32_t mInputWriteIndex;
bool mIsConnected = false;
bool mSeenNonSilenceInput;
};
@ -509,12 +541,49 @@ ScriptProcessorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
void
ScriptProcessorNode::EventListenerAdded(nsIAtom* aType)
{
AudioNode::EventListenerAdded(aType);
if (aType == nsGkAtoms::onaudioprocess) {
UpdateConnectedStatus();
}
}
void
ScriptProcessorNode::EventListenerRemoved(nsIAtom* aType)
{
AudioNode::EventListenerRemoved(aType);
if (aType == nsGkAtoms::onaudioprocess) {
UpdateConnectedStatus();
}
}
JSObject*
ScriptProcessorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return ScriptProcessorNodeBinding::Wrap(aCx, this, aGivenProto);
}
void
ScriptProcessorNode::UpdateConnectedStatus()
{
bool isConnected = mHasPhantomInput ||
!(OutputNodes().IsEmpty() && OutputParams().IsEmpty()
&& InputNodes().IsEmpty());
// Events are queued even when there is no listener because a listener
// may be added while events are in the queue.
SendInt32ParameterToStream(ScriptProcessorNodeEngine::IS_CONNECTED,
isConnected);
if (isConnected && HasListenersFor(nsGkAtoms::onaudioprocess)) {
MarkActive();
} else {
MarkInactive();
}
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -28,6 +28,9 @@ public:
IMPL_EVENT_HANDLER(audioprocess)
virtual void EventListenerAdded(nsIAtom* aType) override;
virtual void EventListenerRemoved(nsIAtom* aType) override;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual void Connect(AudioNode& aDestination, uint32_t aOutput,
@ -35,7 +38,7 @@ public:
{
AudioNode::Connect(aDestination, aOutput, aInput, aRv);
if (!aRv.Failed()) {
MarkActive();
UpdateConnectedStatus();
}
}
@ -44,17 +47,27 @@ public:
{
AudioNode::Connect(aDestination, aOutput, aRv);
if (!aRv.Failed()) {
MarkActive();
UpdateConnectedStatus();
}
}
virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv) override
{
AudioNode::Disconnect(aOutput, aRv);
if (!aRv.Failed() && OutputNodes().IsEmpty() && OutputParams().IsEmpty()) {
MarkInactive();
if (!aRv.Failed()) {
UpdateConnectedStatus();
}
}
virtual void NotifyInputsChanged() override
{
UpdateConnectedStatus();
}
virtual void NotifyHasPhantomInput() override
{
mHasPhantomInput = true;
// No need to UpdateConnectedStatus() because there was previously an
// input in InputNodes().
}
virtual void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv) override
{
@ -91,12 +104,14 @@ public:
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
protected:
private:
virtual ~ScriptProcessorNode();
private:
void UpdateConnectedStatus();
const uint32_t mBufferSize;
const uint32_t mNumberOfOutputChannels;
bool mHasPhantomInput = false;
};
} // namespace dom

Просмотреть файл

@ -15,7 +15,7 @@ addLoadEvent(function() {
var source = ctx.createBufferSource();
var b0 = ctx.createBuffer(32,798,22050);
var b1 = ctx.createBuffer(32,28,22050);
var sp = ctx.createScriptProcessor();
var sp = ctx.createScriptProcessor(0, 2, 0);
source.buffer = b0;
source.connect(sp);
source.start(0);
@ -25,7 +25,6 @@ addLoadEvent(function() {
sp.onaudioprocess = null;
SimpleTest.finish();
};
sp.connect(ctx.destination); // work around bug 916387
});

Просмотреть файл

@ -63,8 +63,6 @@ function startTest() {
var delayProcessor = ctx.createScriptProcessor(bufferSize, 1, 0);
delayProcessor.onaudioprocess = onDelayOutput;
// Work around bug 916387.
delayProcessor.connect(ctx.destination);
var delayDuration = delayLength / ctx.sampleRate;
for (var i = 0; i < sourceCount; ++i) {

Просмотреть файл

@ -111,7 +111,6 @@ function startTest() {
var processor1 = ctx.createScriptProcessor(bufferSize, 1, 0);
delay.connect(processor1);
processor1.onaudioprocess = onDelayOutput;
processor1.connect(ctx.destination); // work around bug 916387
// Signal to trigger initial tail time reference
oscillator = ctx.createOscillator();
@ -128,7 +127,6 @@ function startTest() {
var processor2 = ctx.createScriptProcessor(bufferSize, 1, 0);
source.connect(processor2);
processor2.onaudioprocess = onSourceOutput;
processor2.connect(ctx.destination); // guard against bug 916387
};
startTest();

Просмотреть файл

@ -15,9 +15,8 @@ function test() {
var audio = new Audio("small-shot.ogg");
var context = new AudioContext();
var node = context.createMediaStreamSource(audio.mozCaptureStreamUntilEnded());
var sp = context.createScriptProcessor(2048, 1);
var sp = context.createScriptProcessor(2048, 1, 0);
node.connect(sp);
sp.connect(context.destination); // work around bug 916387
var expectedMinNonzeroSampleCount;
var expectedMaxNonzeroSampleCount;
var nonzeroSampleCount = 0;

Просмотреть файл

@ -14,11 +14,9 @@ addLoadEvent(function() {
var context = new AudioContext();
var osc = context.createOscillator();
var sp = context.createScriptProcessor();
var sp = context.createScriptProcessor(0, 1, 0);
osc.connect(sp);
// Work around bug 916387.
sp.connect(context.destination);
sp.onaudioprocess = function (e) {
var input = e.inputBuffer.getChannelData(0);

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше