Merge m-c to fx-team

This commit is contained in:
Nigel Babu 2014-08-22 07:17:47 +05:30
commit d590551451
604 changed files with 9688 additions and 5266 deletions

View File

@ -889,6 +889,10 @@ pref("network.sntp.timeout", 30); // In seconds.
// Enable dataStore
pref("dom.datastore.enabled", true);
// When an entry is changed, use two timers to fire system messages in a more
// moderate pattern.
pref("dom.datastore.sysMsgOnChangeShortTimeoutSec", 10);
pref("dom.datastore.sysMsgOnChangeLongTimeoutSec", 60);
// DOM Inter-App Communication API.
pref("dom.inter-app-communication-api.enabled", true);

View File

@ -10,6 +10,13 @@ xul|window xul|scrollbar {
display: none;
}
/* Bug 1041576 - Scrollable with scrollgrab should not have scrollbars */
@-moz-document domain(system.gaiamobile.org) {
.browser-container > xul|scrollbar {
display: none;
}
}
html xul|scrollbar[root="true"] {
position: relative;
z-index: 2147483647;

View File

@ -304,12 +304,16 @@ setUpdateTrackingId();
})();
// ================ Accessibility ============
SettingsListener.observe("accessibility.screenreader", false, function(value) {
if (value && !("AccessFu" in this)) {
Cu.import('resource://gre/modules/accessibility/AccessFu.jsm');
AccessFu.attach(window);
}
});
(function setupAccessibility() {
let accessibilityScope = {};
SettingsListener.observe("accessibility.screenreader", false, function(value) {
if (!('AccessFu' in accessibilityScope)) {
Cu.import('resource://gre/modules/accessibility/AccessFu.jsm',
accessibilityScope);
accessibilityScope.AccessFu.attach(window);
}
});
})();
// ================ Theming ============
(function themingSettingsListener() {

View File

@ -862,11 +862,15 @@ window.addEventListener('ContentStart', function ss_onContentStart() {
try {
var canvas = document.createElementNS('http://www.w3.org/1999/xhtml',
'canvas');
var width = window.innerWidth;
var height = window.innerHeight;
var docRect = document.body.getBoundingClientRect();
var width = docRect.width;
var height = docRect.height;
// Convert width and height from CSS pixels (potentially fractional)
// to device pixels (integer).
var scale = window.devicePixelRatio;
canvas.setAttribute('width', width * scale);
canvas.setAttribute('height', height * scale);
canvas.setAttribute('width', Math.round(width * scale));
canvas.setAttribute('height', Math.round(height * scale));
var context = canvas.getContext('2d');
var flags =

View File

@ -149,7 +149,8 @@ let AlertsHelper = {
dir: listener.dir,
id: listener.id,
tag: listener.tag,
timestamp: listener.timestamp
timestamp: listener.timestamp,
data: listener.dataObj
},
Services.io.newURI(listener.target, null, null),
Services.io.newURI(listener.manifestURL, null, null)
@ -199,8 +200,32 @@ let AlertsHelper = {
});
},
deserializeStructuredClone: function(dataString) {
if (!dataString) {
return null;
}
let scContainer = Cc["@mozilla.org/docshell/structured-clone-container;1"].
createInstance(Ci.nsIStructuredCloneContainer);
// The maximum supported structured-clone serialization format version
// as defined in "js/public/StructuredClone.h"
let JS_STRUCTURED_CLONE_VERSION = 4;
scContainer.initFromBase64(dataString, JS_STRUCTURED_CLONE_VERSION);
let dataObj = scContainer.deserializeToVariant();
// We have to check whether dataObj contains DOM objects (supported by
// nsIStructuredCloneContainer, but not by Cu.cloneInto), e.g. ImageData.
// After the structured clone callback systems will be unified, we'll not
// have to perform this check anymore.
try {
let data = Cu.cloneInto(dataObj, {});
} catch(e) { dataObj = null; }
return dataObj;
},
showNotification: function(imageURL, title, text, textClickable, cookie,
uid, bidi, lang, manifestURL, timestamp) {
uid, bidi, lang, dataObj, manifestURL, timestamp) {
function send(appName, appIcon) {
SystemAppProxy._sendCustomEvent(kMozChromeNotificationEvent, {
type: kDesktopNotification,
@ -213,7 +238,8 @@ let AlertsHelper = {
appName: appName,
appIcon: appIcon,
manifestURL: manifestURL,
timestamp: timestamp
timestamp: timestamp,
data: dataObj
});
}
@ -238,15 +264,17 @@ let AlertsHelper = {
currentListener.observer.observe(null, kTopicAlertFinished, currentListener.cookie);
}
let dataObj = this.deserializeStructuredClone(data.dataStr);
this.registerListener(data.name, data.cookie, data.alertListener);
this.showNotification(data.imageURL, data.title, data.text,
data.textClickable, data.cookie, data.name, data.bidi,
data.lang, null);
data.lang, dataObj, null);
},
showAppNotification: function(aMessage) {
let data = aMessage.data;
let details = data.details;
let dataObject = this.deserializeStructuredClone(details.data);
let listener = {
mm: aMessage.target,
title: data.title,
@ -257,12 +285,14 @@ let AlertsHelper = {
id: details.id || undefined,
dir: details.dir || undefined,
tag: details.tag || undefined,
timestamp: details.timestamp || undefined
timestamp: details.timestamp || undefined,
dataObj: dataObject || undefined
};
this.registerAppListener(data.uid, listener);
this.showNotification(data.imageURL, data.title, data.text,
details.textClickable, null, data.uid, details.dir,
details.lang, details.manifestURL, details.timestamp);
details.lang, dataObject, details.manifestURL,
details.timestamp);
},
closeAlert: function(name) {

View File

@ -69,7 +69,7 @@ AlertsService.prototype = {
// nsIAlertsService
showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable,
aCookie, aAlertListener, aName, aBidi,
aLang) {
aLang, aDataStr) {
cpmm.sendAsyncMessage(kMessageAlertNotificationSend, {
imageURL: aImageUrl,
title: aTitle,
@ -79,7 +79,8 @@ AlertsService.prototype = {
listener: aAlertListener,
id: aName,
dir: aBidi,
lang: aLang
lang: aLang,
dataStr: aDataStr
});
},
@ -95,6 +96,7 @@ AlertsService.prototype = {
let uid = (aDetails.id == "") ?
"app-notif-" + uuidGenerator.generateUUID() : aDetails.id;
let dataObj = this.deserializeStructuredClone(aDetails.data);
this._listeners[uid] = {
observer: aAlertListener,
title: aTitle,
@ -106,7 +108,8 @@ AlertsService.prototype = {
dbId: aDetails.dbId || undefined,
dir: aDetails.dir || undefined,
tag: aDetails.tag || undefined,
timestamp: aDetails.timestamp || undefined
timestamp: aDetails.timestamp || undefined,
dataObj: dataObj || undefined
};
cpmm.sendAsyncMessage(kMessageAppNotificationSend, {
@ -151,7 +154,8 @@ AlertsService.prototype = {
id: listener.id,
tag: listener.tag,
dbId: listener.dbId,
timestamp: listener.timestamp
timestamp: listener.timestamp,
data: listener.dataObj || undefined,
},
Services.io.newURI(data.target, null, null),
Services.io.newURI(listener.manifestURL, null, null)
@ -167,6 +171,30 @@ AlertsService.prototype = {
}
delete this._listeners[data.uid];
}
},
deserializeStructuredClone: function(dataString) {
if (!dataString) {
return null;
}
let scContainer = Cc["@mozilla.org/docshell/structured-clone-container;1"].
createInstance(Ci.nsIStructuredCloneContainer);
// The maximum supported structured-clone serialization format version
// as defined in "js/public/StructuredClone.h"
let JS_STRUCTURED_CLONE_VERSION = 4;
scContainer.initFromBase64(dataString, JS_STRUCTURED_CLONE_VERSION);
let dataObj = scContainer.deserializeToVariant();
// We have to check whether dataObj contains DOM objects (supported by
// nsIStructuredCloneContainer, but not by Cu.cloneInto), e.g. ImageData.
// After the structured clone callback systems will be unified, we'll not
// have to perform this check anymore.
try {
let data = Cu.cloneInto(dataObj, {});
} catch(e) { dataObj = null; }
return dataObj;
}
};

View File

@ -312,6 +312,9 @@ UpdatePrompt.prototype = {
}
switch (aDetail.result) {
// Battery not okay, do not wait for idle to re-prompt
case "low-battery":
break;
case "wait":
// Wait until the user is idle before prompting to apply the update
this.waitForIdle();

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="1865c6639c51f0290d5778adef146147d5d6a5f0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -127,7 +127,7 @@
<!-- Stock Android things -->
<project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
<!-- dolphin specific things -->
<project name="device/sprd" path="device/sprd" revision="054d217fc6efdeff296742b58b5bda427d9d4384"/>
<project name="device/sprd" path="device/sprd" revision="66f858de575b95e334f32f6c7ac9d1cd85e9f0d8"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="4e58336019b5cbcfd134caf55b142236cf986618"/>
<project name="platform/frameworks/av" path="frameworks/av" revision="cbd80d8c03fc639dd810b17c4b682c67abc06ee8"/>
<project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>

View File

@ -19,11 +19,11 @@
<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="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="f0592d4814d738e3f8d840915ef799c13601bdef"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a567a787b5ac3e0cb663aa6464b18a24ec764409"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<!-- Stock Android things -->

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="1865c6639c51f0290d5778adef146147d5d6a5f0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,11 +19,11 @@
<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="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="f0592d4814d738e3f8d840915ef799c13601bdef"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a567a787b5ac3e0cb663aa6464b18a24ec764409"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<!-- Stock Android things -->

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
@ -135,7 +135,7 @@
<project name="platform/frameworks/av" path="frameworks/av" revision="0f7829661cd7125de9dc2c90eca2fa1dbc68dfbf"/>
<project name="platform/frameworks/base" path="frameworks/base" revision="f9309b4463abd80e0876cd113c892e31d62113b1"/>
<project name="platform/frameworks/native" path="frameworks/native" revision="268d569074237b53617db8211400d4e3c947ae73"/>
<project name="platform/hardware/libhardware" path="hardware/libhardware" revision="484802559ed106bac4811bd01c024ca64f741e60"/>
<project name="platform/hardware/libhardware" path="hardware/libhardware" revision="8b39ee0db3203247e983db773799f7f4ff2f69ce"/>
<project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="de4ade568b273781416638fbbce13ff31b636ada"/>
<project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="5e110615212302c5d798a3c223dcee458817651c"/>
<project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="fa9ffd47948eb24466de227e48fe9c4a7c5e7711"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "ecfb8305745716f534e782c7b9df11aaa78b3823",
"revision": "670b42b547f817727fc98f2983c606e8cc8766af",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,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="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,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="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>

View File

@ -6,8 +6,8 @@
"filename": "setup.sh"
},
{
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"size": 166407,
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -6,8 +6,8 @@
"filename": "setup.sh"
},
{
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"size": 166407,
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -15,8 +15,8 @@
"filename": "clang.tar.bz2"
},
{
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"size": 166407,
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -17,7 +17,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="3584b2723412ed3299c6761f465885d80651c87e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -1,3 +1,6 @@
# 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/.
DIST_SUBDIR = 'browser'
export('DIST_SUBDIR')

View File

@ -53,9 +53,9 @@ pref("extensions.blocklist.interval", 86400);
// Controls what level the blocklist switches from warning about items to forcibly
// blocking them.
pref("extensions.blocklist.level", 2);
pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
pref("extensions.blocklist.url", "https://blocklist.addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
pref("extensions.blocklist.detailsURL", "https://www.mozilla.org/%LOCALE%/blocklist/");
pref("extensions.blocklist.itemURL", "https://addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%");
pref("extensions.blocklist.itemURL", "https://blocklist.addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%");
pref("extensions.update.autoUpdateDefault", true);

View File

@ -38,7 +38,7 @@
<commandset id="editMenuCommands"/>
<command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(content.document);" observes="isImage"/>
<command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(window.gBrowser.selectedBrowser.contentDocumentAsCPOW);" observes="isImage"/>
<command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
<command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
<command id="cmd_find"

View File

@ -58,6 +58,10 @@ var tabPreviews = {
thumbnail.height = this.height;
thumbnail.width = this.width;
// drawWindow doesn't yet work with e10s (bug 698371)
if (gMultiProcessBrowser)
return thumbnail;
var ctx = thumbnail.getContext("2d");
var win = aTab.linkedBrowser.contentWindow;
var snippetWidth = win.innerWidth * .6;

View File

@ -683,6 +683,13 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
max-width: 280px;
}
.form-validation-anchor {
/* should occupy space but not be visible */
opacity: 0;
visibility: hidden;
pointer-events: none;
}
#addon-progress-notification {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#addon-progress-notification");
}

View File

@ -173,6 +173,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter",
"resource:///modules/TabCrashReporter.jsm");
#endif
XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
"resource:///modules/FormValidationHandler.jsm");
let gInitialPages = [
"about:blank",
"about:newtab",
@ -665,104 +668,6 @@ var gPopupBlockerObserver = {
}
};
const gFormSubmitObserver = {
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
panel: null,
init: function()
{
this.panel = document.getElementById('invalid-form-popup');
},
notifyInvalidSubmit : function (aFormElement, aInvalidElements)
{
// We are going to handle invalid form submission attempt by focusing the
// first invalid element and show the corresponding validation message in a
// panel attached to the element.
if (!aInvalidElements.length) {
return;
}
// Don't show the popup if the current tab doesn't contain the invalid form.
if (gBrowser.contentDocument !=
aFormElement.ownerDocument.defaultView.top.document) {
return;
}
let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
if (!(element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement ||
element instanceof HTMLSelectElement ||
element instanceof HTMLButtonElement)) {
return;
}
this.panel.firstChild.textContent = element.validationMessage;
element.focus();
// If the user interacts with the element and makes it valid or leaves it,
// we want to remove the popup.
// We could check for clicks but a click is already removing the popup.
function blurHandler() {
gFormSubmitObserver.panel.hidePopup();
};
function inputHandler(e) {
if (e.originalTarget.validity.valid) {
gFormSubmitObserver.panel.hidePopup();
} else {
// If the element is now invalid for a new reason, we should update the
// error message.
if (gFormSubmitObserver.panel.firstChild.textContent !=
e.originalTarget.validationMessage) {
gFormSubmitObserver.panel.firstChild.textContent =
e.originalTarget.validationMessage;
}
}
};
element.addEventListener("input", inputHandler, false);
element.addEventListener("blur", blurHandler, false);
// One event to bring them all and in the darkness bind them.
this.panel.addEventListener("popuphiding", function onPopupHiding(aEvent) {
aEvent.target.removeEventListener("popuphiding", onPopupHiding, false);
element.removeEventListener("input", inputHandler, false);
element.removeEventListener("blur", blurHandler, false);
}, false);
this.panel.hidden = false;
// We want to show the popup at the middle of checkbox and radio buttons
// and where the content begin for the other elements.
let offset = 0;
let position = "";
if (element.tagName == 'INPUT' &&
(element.type == 'radio' || element.type == 'checkbox')) {
position = "bottomcenter topleft";
} else {
let win = element.ownerDocument.defaultView;
let style = win.getComputedStyle(element, null);
let utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
if (style.direction == 'rtl') {
offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
} else {
offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
}
offset = Math.round(offset * utils.fullZoom);
position = "after_start";
}
this.panel.openPopup(element, position, offset, 0);
}
};
function gKeywordURIFixup(fixupInfo, topic, data) {
fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
@ -1122,11 +1027,15 @@ var gBrowserInit = {
// This pageshow listener needs to be registered before we may call
// swapBrowsersAndCloseOther() to receive pageshow events fired by that.
gBrowser.addEventListener("pageshow", function(event) {
// Filter out events that are not about the document load we are interested in
if (content && event.target == content.document)
setTimeout(pageShowEventHandlers, 0, event.persisted);
}, true);
if (!gMultiProcessBrowser) {
// pageshow handlers are being migrated to
// content.js. Eventually this code should be removed.
gBrowser.addEventListener("pageshow", function(event) {
// Filter out events that are not about the document load we are interested in
if (content && event.target == content.document)
setTimeout(pageShowEventHandlers, 0, event.persisted);
}, true);
}
if (uriToLoad && uriToLoad != "about:blank") {
if (uriToLoad instanceof Ci.nsISupportsArray) {
@ -1180,13 +1089,11 @@ var gBrowserInit = {
Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup", false);
BrowserOffline.init();
OfflineApps.init();
IndexedDBPromptHelper.init();
gFormSubmitObserver.init();
gRemoteTabsUI.init();
// Initialize the full zoom setting.
@ -1481,7 +1388,6 @@ var gBrowserInit = {
Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
try {
@ -2258,7 +2164,7 @@ function BrowserPageInfo(doc, initialTab, imageElement) {
var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
var windows = Services.wm.getEnumerator("Browser:page-info");
var documentURL = doc ? doc.location : window.content.document.location;
var documentURL = doc ? doc.location : window.gBrowser.selectedBrowser.contentDocumentAsCPOW.location;
// Check for windows matching the url
while (windows.hasMoreElements()) {
@ -3776,10 +3682,8 @@ var XULBrowserWindow = {
onLocationChange: function (aWebProgress, aRequest, aLocationURI, aFlags) {
var location = aLocationURI ? aLocationURI.spec : "";
// Hide the form invalid popup.
if (gFormSubmitObserver.panel) {
gFormSubmitObserver.panel.hidePopup();
}
// If displayed, hide the form validation popup.
FormValidationHandler.hidePopup();
let pageTooltip = document.getElementById("aHTMLTooltip");
let tooltipNode = pageTooltip.triggerNode;
@ -4361,10 +4265,6 @@ nsBrowserAccess.prototype = {
isTabContentWindow: function (aWindow) {
return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow);
},
get contentWindow() {
return gBrowser.contentWindow;
}
}
function getTogglableToolbars() {

View File

@ -129,8 +129,6 @@ chatBrowserAccess.prototype = {
},
isTabContentWindow: function (aWindow) this.contentWindow == aWindow,
get contentWindow() document.getElementById("chatter").contentWindow
};
</script>

View File

@ -8,6 +8,8 @@ let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler",
"resource:///modules/ContentLinkHandler.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
@ -18,11 +20,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
"resource:///modules/UITour.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver",
"resource:///modules/FormSubmitObserver.jsm");
// Creates a new nsIURI object.
function makeURI(uri, originCharset, baseURI) {
return Services.io.newURI(uri, originCharset, baseURI);
}
// TabChildGlobal
var global = this;
// Load the form validation popup handler
var formSubmitObserver = new FormSubmitObserver(content, this);
addMessageListener("Browser:HideSessionRestoreButton", function (message) {
// Hide session restore button on about:home
@ -328,9 +333,6 @@ let ContentSearchMediator = {
};
ContentSearchMediator.init(this);
var global = this;
// Lazily load the finder code
addMessageListener("Finder:Initialize", function () {
let {RemoteFinderListener} = Cu.import("resource://gre/modules/RemoteFinder.jsm", {});
@ -465,7 +467,7 @@ let ClickEventHandler = {
// In case of XLink, we don't return the node we got href from since
// callers expect <a>-like elements.
// Note: makeURI() will throw if aUri is not a valid URI.
return [href ? makeURI(href, null, baseURI).spec : null, null];
return [href ? BrowserUtils.makeURI(href, null, baseURI).spec : null, null];
}
};
ClickEventHandler.init();

View File

@ -151,6 +151,22 @@
]]></getter>
</property>
<property name="formValidationAnchor" readonly="true">
<getter><![CDATA[
if (this.mCurrentTab._formValidationAnchor) {
return this.mCurrentTab._formValidationAnchor;
}
let stack = this.mCurrentBrowser.parentNode;
// Create an anchor for the form validation popup
const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let formValidationAnchor = document.createElementNS(NS_XUL, "hbox");
formValidationAnchor.className = "form-validation-anchor";
formValidationAnchor.hidden = true;
stack.appendChild(formValidationAnchor);
return this.mCurrentTab._formValidationAnchor = formValidationAnchor;
]]></getter>
</property>
<method name="isFindBarInitialized">
<parameter name="aTab"/>
<body><![CDATA[

View File

@ -29,6 +29,22 @@ let gObserver = {
}
};
function getDocHeader()
{
return "data:text/html,<html><head><meta charset='utf-8'></head><body>" + getEmptyFrame();
}
function getDocFooter()
{
return "</body></html>";
}
function getEmptyFrame()
{
return "<iframe style='width:100px; height:30px; margin:3px; border:1px solid lightgray;' " +
"name='t' srcdoc=\"<html><head><meta charset='utf-8'></head><body>form target</body></html>\"></iframe>";
}
var testId = -1;
function nextTest()
@ -54,7 +70,7 @@ var tests = [
*/
function()
{
let uri = "data:text/html,<html><body><iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='s' type='submit'></form></body></html>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
@ -82,7 +98,7 @@ function()
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
@ -102,8 +118,9 @@ function()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;
@ -116,7 +133,7 @@ function()
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='i' required><input required><input id='s' type='submit'></form>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input><input id='i' required><input required><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
@ -136,8 +153,9 @@ function()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;
@ -150,7 +168,7 @@ function()
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
@ -176,8 +194,9 @@ function()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;
@ -190,7 +209,7 @@ function()
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' id='i' required><input id='s' type='submit'></form>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input type='email' id='i' required><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
@ -216,8 +235,9 @@ function()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;
@ -230,7 +250,7 @@ function()
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
@ -256,8 +276,9 @@ function()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;
@ -269,7 +290,7 @@ function()
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
@ -295,8 +316,9 @@ function()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;
@ -308,7 +330,7 @@ function()
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
@ -336,8 +358,9 @@ function()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;
@ -345,21 +368,24 @@ function()
},
/**
* In this test, we check that nothing happen (no focus nor popup) if the
* invalid form is submitted in another tab than the current focused one
* (submitted in background).
* In this test, we check that nothing happen if the invalid form is
* submitted in a background tab.
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
// Observers don't propagate currently across processes. We may add support for this in the
// future via the addon compat layer.
if (gBrowser.isRemoteBrowser) {
nextTest();
return;
}
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gObserver.notifyInvalidSubmit = function() {
executeSoon(function() {
let doc = tab.linkedBrowser.contentDocument;
isnot(doc.activeElement, doc.getElementById('i'),
"We should not focus the invalid element when the form is submitted in background");
checkPopupHide();
// Clean-up
@ -381,7 +407,9 @@ function()
isnot(gBrowser.selectedTab.linkedBrowser, browser,
"This tab should have been loaded in background");
browser.contentDocument.getElementById('s').click();
executeSoon(function() {
browser.contentDocument.getElementById('s').click();
});
}
}, true);
@ -393,7 +421,7 @@ function()
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input x-moz-errormessage='foo' required id='i'><input id='s' type='submit'></form>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input x-moz-errormessage='foo' required id='i'><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
@ -415,8 +443,9 @@ function()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;
@ -428,7 +457,7 @@ function()
*/
function()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input type='email' required id='i'><input id='s' type='submit'></form>";
let uri = getDocHeader() + "<form target='t' action='data:text/html,'><input type='email' required id='i'><input id='s' type='submit'></form>" + getDocFooter();
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
@ -463,8 +492,9 @@ function()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;

View File

@ -29,9 +29,10 @@ function test()
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument
.getElementById('s').click();
executeSoon(function() {
gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument
.getElementById('s').click();
});
}, true);
gBrowser.selectedTab = tab;

View File

@ -117,6 +117,9 @@ XPCOMUtils.defineLazyGetter(this, "ShellService", function() {
}
});
XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
"resource:///modules/FormValidationHandler.jsm");
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
@ -519,6 +522,7 @@ BrowserGlue.prototype = {
SessionStore.init();
BrowserUITelemetry.init();
ContentSearch.init();
FormValidationHandler.init();
ContentClick.init();
RemotePrompt.init();
@ -724,6 +728,7 @@ BrowserGlue.prototype = {
}
#endif
webrtcUI.uninit();
FormValidationHandler.uninit();
},
// All initial windows have opened.

View File

@ -12,8 +12,8 @@
"filename": "gcc.tar.xz"
},
{
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"size": 166407,
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -12,8 +12,8 @@
"filename": "gcc.tar.xz"
},
{
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"size": 166407,
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -15,8 +15,8 @@
"filename": "clang.tar.bz2"
},
{
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"size": 166407,
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -12,8 +12,8 @@
"filename": "setup.sh"
},
{
"size": 165226,
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
"size": 166407,
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
"algorithm": "sha512",
"filename": "sccache.tar.bz2"
}

View File

@ -229,12 +229,13 @@ TabTarget.prototype = {
},
get name() {
return this._tab ? this._tab.linkedBrowser.contentDocument.title :
this._form.title;
return this._tab && this._tab.linkedBrowser.contentDocument ?
this._tab.linkedBrowser.contentDocument.title :
this._form.title;
},
get url() {
return this._tab ? this._tab.linkedBrowser.contentDocument.location.href :
return this._tab ? this._tab.linkedBrowser.currentURI.spec :
this._form.url;
},
@ -298,17 +299,20 @@ TabTarget.prototype = {
this._client.listTabs(aResponse => {
this._root = aResponse;
let windowUtils = this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let outerWindow = windowUtils.outerWindowID;
aResponse.tabs.some((tab) => {
if (tab.outerWindowID === outerWindow) {
this._form = tab;
return true;
}
return false;
});
if (this.window) {
let windowUtils = this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let outerWindow = windowUtils.outerWindowID;
aResponse.tabs.some((tab) => {
if (tab.outerWindowID === outerWindow) {
this._form = tab;
return true;
}
return false;
});
}
if (!this._form) {
this._form = aResponse.tabs[aResponse.selected];
}

View File

@ -906,9 +906,6 @@ PdfStreamConverter.prototype = {
aRequest.setResponseHeader('Content-Security-Policy', '', false);
aRequest.setResponseHeader('Content-Security-Policy-Report-Only', '',
false);
aRequest.setResponseHeader('X-Content-Security-Policy', '', false);
aRequest.setResponseHeader('X-Content-Security-Policy-Report-Only', '',
false);
}
PdfJsTelemetry.onViewerIsUsed();

View File

@ -1087,10 +1087,6 @@ nsBrowserAccess.prototype = {
isTabContentWindow: function(aWindow) {
return Browser.browsers.some(function (browser) browser.contentWindow == aWindow);
},
get contentWindow() {
return Browser.selectedBrowser.contentWindow;
}
};
/**

View File

@ -237,7 +237,7 @@ pref("extensions.update.enabled", false);
/* blocklist preferences */
pref("extensions.blocklist.enabled", true);
pref("extensions.blocklist.interval", 86400);
pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
pref("extensions.blocklist.url", "https://blocklist.addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
pref("extensions.blocklist.detailsURL", "https://www.mozilla.org/%LOCALE%/blocklist/");
pref("extensions.showMismatchUI", false);

View File

@ -0,0 +1,251 @@
/* 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/. */
/*
* Handles the validation callback from nsIFormFillController and
* the display of the help panel on invalid elements.
*/
"use strict";
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
let HTMLInputElement = Ci.nsIDOMHTMLInputElement;
let HTMLTextAreaElement = Ci.nsIDOMHTMLTextAreaElement;
let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
let HTMLButtonElement = Ci.nsIDOMHTMLButtonElement;
this.EXPORTED_SYMBOLS = [ "FormSubmitObserver" ];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/BrowserUtils.jsm");
function FormSubmitObserver(aWindow, aTabChildGlobal) {
this.init(aWindow, aTabChildGlobal);
}
FormSubmitObserver.prototype =
{
_validationMessage: "",
_content: null,
_element: null,
/*
* Public apis
*/
init: function(aWindow, aTabChildGlobal)
{
this._content = aWindow;
this._tab = aTabChildGlobal;
this._mm =
this._content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
.sameTypeRootTreeItem
.QueryInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
// nsIFormSubmitObserver callback about invalid forms. See HTMLFormElement
// for details.
Services.obs.addObserver(this, "invalidformsubmit", false);
this._tab.addEventListener("pageshow", this, false);
this._tab.addEventListener("unload", this, false);
},
uninit: function()
{
Services.obs.removeObserver(this, "invalidformsubmit");
this._content.removeEventListener("pageshow", this, false);
this._content.removeEventListener("unload", this, false);
this._mm = null;
this._element = null;
this._content = null;
this._tab = null;
},
/*
* Events
*/
handleEvent: function (aEvent) {
switch (aEvent.type) {
case "pageshow":
if (this._isRootDocumentEvent(aEvent)) {
this._hidePopup();
}
break;
case "unload":
this.uninit();
break;
case "input":
this._onInput(aEvent);
break;
case "blur":
this._onBlur(aEvent);
break;
}
},
/*
* nsIFormSubmitObserver
*/
notifyInvalidSubmit : function (aFormElement, aInvalidElements)
{
// We are going to handle invalid form submission attempt by focusing the
// first invalid element and show the corresponding validation message in a
// panel attached to the element.
if (!aInvalidElements.length) {
return;
}
// Insure that this is the FormSubmitObserver associated with the form
// element / window this notification is about.
if (this._content != aFormElement.ownerDocument.defaultView.top.document.defaultView) {
return;
}
let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
if (!(element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement ||
element instanceof HTMLSelectElement ||
element instanceof HTMLButtonElement)) {
return;
}
// Don't connect up to the same element more than once.
if (this._element == element) {
this._showPopup(element);
return;
}
this._element = element;
element.focus();
this._validationMessage = element.validationMessage;
// Watch for input changes which may change the validation message.
element.addEventListener("input", this, false);
// Watch for focus changes so we can disconnect our listeners and
// hide the popup.
element.addEventListener("blur", this, false);
this._showPopup(element);
},
/*
* Internal
*/
/*
* Handles input changes on the form element we've associated a popup
* with. Updates the validation message or closes the popup if form data
* becomes valid.
*/
_onInput: function (aEvent) {
let element = aEvent.originalTarget;
// If the form input is now valid, hide the popup.
if (element.validity.valid) {
this._hidePopup();
return;
}
// If the element is still invalid for a new reason, we should update
// the popup error message.
if (this._validationMessage != element.validationMessage) {
this._validationMessage = element.validationMessage;
this._showPopup(element);
}
},
/*
* Blur event handler in which we disconnect from the form element and
* hide the popup.
*/
_onBlur: function (aEvent) {
aEvent.originalTarget.removeEventListener("input", this, false);
aEvent.originalTarget.removeEventListener("blur", this, false);
this._element = null;
this._hidePopup();
},
/*
* Send the show popup message to chrome with appropriate position
* information. Can be called repetitively to update the currently
* displayed popup position and text.
*/
_showPopup: function (aElement) {
// Collect positional information and show the popup
let panelData = {};
panelData.message = this._validationMessage;
// Note, this is relative to the browser and needs to be translated
// in chrome.
panelData.contentRect = this._msgRect(aElement);
// We want to show the popup at the middle of checkbox and radio buttons
// and where the content begin for the other elements.
let offset = 0;
let position = "";
if (aElement.tagName == 'INPUT' &&
(aElement.type == 'radio' || aElement.type == 'checkbox')) {
panelData.position = "bottomcenter topleft";
} else {
let win = aElement.ownerDocument.defaultView;
let style = win.getComputedStyle(aElement, null);
if (style.direction == 'rtl') {
offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
} else {
offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
}
let zoomFactor = this._getWindowUtils().fullZoom;
panelData.offset = Math.round(offset * zoomFactor);
panelData.position = "after_start";
}
this._mm.sendAsyncMessage("FormValidation:ShowPopup", panelData);
},
_hidePopup: function () {
this._mm.sendAsyncMessage("FormValidation:HidePopup", {});
},
_getWindowUtils: function () {
return this._content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
},
_isRootDocumentEvent: function (aEvent) {
if (this._content == null) {
return true;
}
let target = aEvent.originalTarget;
return (target == this._content.document ||
(target.ownerDocument && target.ownerDocument == this._content.document));
},
/*
* Return a message manager rect for the element's bounding client rect
* in top level browser coords.
*/
_msgRect: function (aElement) {
let domRect = aElement.getBoundingClientRect();
let zoomFactor = this._getWindowUtils().fullZoom;
let { offsetX, offsetY } = BrowserUtils.offsetToTopLevelWindow(this._content, aElement);
return {
left: (domRect.left + offsetX) * zoomFactor,
top: (domRect.top + offsetY) * zoomFactor,
width: domRect.width * zoomFactor,
height: domRect.height * zoomFactor
};
},
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver])
};

View File

@ -0,0 +1,157 @@
/* 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/. */
/*
* Chrome side handling of form validation popup.
*/
"use strict";
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
this.EXPORTED_SYMBOLS = [ "FormValidationHandler" ];
Cu.import("resource://gre/modules/Services.jsm");
let FormValidationHandler =
{
_panel: null,
_anchor: null,
/*
* Public apis
*/
init: function () {
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
mm.addMessageListener("FormValidation:ShowPopup", this);
mm.addMessageListener("FormValidation:HidePopup", this);
},
uninit: function () {
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
mm.removeMessageListener("FormValidation:ShowPopup", this);
mm.removeMessageListener("FormValidation:HidePopup", this);
this._panel = null;
this._anchor = null;
},
hidePopup: function () {
this._hidePopup();
},
/*
* Events
*/
receiveMessage: function (aMessage) {
let window = aMessage.target.ownerDocument.defaultView;
let json = aMessage.json;
let tabBrowser = window.gBrowser;
switch (aMessage.name) {
case "FormValidation:ShowPopup":
// target is the <browser>, make sure we're receiving a message
// from the foreground tab.
if (tabBrowser && aMessage.target != tabBrowser.selectedBrowser) {
return;
}
this._showPopup(window, json);
break;
case "FormValidation:HidePopup":
this._hidePopup();
break;
}
},
observe: function (aSubject, aTopic, aData) {
this._hidePopup();
},
handleEvent: function (aEvent) {
switch (aEvent.type) {
case "FullZoomChange":
case "TextZoomChange":
case "ZoomChangeUsingMouseWheel":
case "scroll":
this._hidePopup();
break;
case "popuphiding":
this._onPopupHiding(aEvent);
break;
}
},
/*
* Internal
*/
_onPopupHiding: function (aEvent) {
aEvent.originalTarget.removeEventListener("popuphiding", this, true);
let tabBrowser = aEvent.originalTarget.ownerDocument.getElementById("content");
tabBrowser.selectedBrowser.removeEventListener("scroll", this, true);
tabBrowser.selectedBrowser.removeEventListener("FullZoomChange", this, false);
tabBrowser.selectedBrowser.removeEventListener("TextZoomChange", this, false);
tabBrowser.selectedBrowser.removeEventListener("ZoomChangeUsingMouseWheel", this, false);
this._panel.hidden = true;
this._panel = null;
this._anchor.hidden = true;
this._anchor = null;
},
/*
* Shows the form validation popup at a specified position or updates the
* messaging and position if the popup is already displayed.
*
* @aWindow - the chrome window
* @aPanelData - Object that contains popup information
* aPanelData stucture detail:
* contentRect - the bounding client rect of the target element. If
* content is remote, this is relative to the browser, otherwise its
* relative to the window.
* position - popup positional string constants.
* message - the form element validation message text.
*/
_showPopup: function (aWindow, aPanelData) {
let previouslyShown = !!this._panel;
this._panel = aWindow.document.getElementById("invalid-form-popup");
this._panel.firstChild.textContent = aPanelData.message;
this._panel.hidden = false;
let tabBrowser = aWindow.gBrowser;
this._anchor = tabBrowser.formValidationAnchor;
this._anchor.left = aPanelData.contentRect.left;
this._anchor.top = aPanelData.contentRect.top;
this._anchor.width = aPanelData.contentRect.width;
this._anchor.height = aPanelData.contentRect.height;
this._anchor.hidden = false;
// Display the panel if it isn't already visible.
if (!previouslyShown) {
// Cleanup after the popup is hidden
this._panel.addEventListener("popuphiding", this, true);
// Hide if the user scrolls the page
tabBrowser.selectedBrowser.addEventListener("scroll", this, true);
tabBrowser.selectedBrowser.addEventListener("FullZoomChange", this, false);
tabBrowser.selectedBrowser.addEventListener("TextZoomChange", this, false);
tabBrowser.selectedBrowser.addEventListener("ZoomChangeUsingMouseWheel", this, false);
// Open the popup
this._panel.openPopup(this._anchor, aPanelData.position, 0, 0, false);
}
},
/*
* Hide the popup if currently displayed. Will fire an event to onPopupHiding
* above if visible.
*/
_hidePopup: function () {
if (this._panel) {
this._panel.hidePopup();
}
}
};

View File

@ -17,6 +17,8 @@ EXTRA_JS_MODULES += [
'ContentSearch.jsm',
'CustomizationTabPreloader.jsm',
'Feeds.jsm',
'FormSubmitObserver.jsm',
'FormValidationHandler.jsm',
'NetworkPrioritizer.jsm',
'offlineAppCache.jsm',
'PanelFrame.jsm',

View File

@ -62,8 +62,9 @@ leak:gsmsdp_add_default_video_formats_to_local_sdp
leak:CCAPI_CallInfo_getMediaStreams
# Intermittent Mochitest 3 WebRTC leaks, as seen in bug 1055910.
leak:sdp_build_attr_ice_attr
leak:sdp_build_attr_
leak:VcmSIPCCBinding::CandidateReady
leak:fsmdef_ev_foundcandidate
###

View File

@ -1371,8 +1371,6 @@ if test "$GNU_CC"; then
AC_MSG_RESULT("$result")
if test "$result" = "yes"; then
HAVE_X86_AVX2=1
AC_DEFINE(HAVE_X86_AVX2)
AC_SUBST(HAVE_X86_AVX2)
fi
esac
@ -2168,6 +2166,10 @@ ia64*-hpux*)
dnl both SSSE3 and SSE4.1.
HAVE_TOOLCHAIN_SUPPORT_MSSSE3=1
HAVE_TOOLCHAIN_SUPPORT_MSSE4_1=1
if test "$_CC_SUITE" -ge "11"; then
dnl allow AVX2 code from VS2012
HAVE_X86_AVX2=1
fi
MOZ_MEMORY=1
fi
AC_DEFINE(HAVE_SNPRINTF)
@ -8703,6 +8705,7 @@ AC_SUBST(CPU_ARCH)
AC_SUBST(INTEL_ARCHITECTURE)
AC_SUBST(HAVE_TOOLCHAIN_SUPPORT_MSSSE3)
AC_SUBST(HAVE_TOOLCHAIN_SUPPORT_MSSE4_1)
AC_SUBST(HAVE_X86_AVX2)
AC_SUBST(GCC_USE_GNU_LD)
AC_SUBST(MOZ_CHROME_FILE_FORMAT)

View File

@ -9,7 +9,7 @@ interface nsIInputStream;
interface nsIDOMDocument;
interface nsIURI;
interface nsIPrincipal;
interface nsIScriptGlobalObject;
interface nsIGlobalObject;
/**
* The nsIDOMParser interface is a non-SAX interface that can be used
@ -89,7 +89,7 @@ interface nsIDOMParser : nsISupports
[noscript] void init(in nsIPrincipal principal,
in nsIURI documentURI,
in nsIURI baseURI,
in nsIScriptGlobalObject scriptObject);
in nsIGlobalObject scriptObject);
};
%{ C++

View File

@ -17,6 +17,7 @@
#include "nsPIDOMWindow.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ScriptSettings.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -315,7 +316,7 @@ DOMParser::ParseFromStream(nsIInputStream *stream,
NS_IMETHODIMP
DOMParser::Init(nsIPrincipal* principal, nsIURI* documentURI,
nsIURI* baseURI, nsIScriptGlobalObject* aScriptObject)
nsIURI* baseURI, nsIGlobalObject* aScriptObject)
{
NS_ENSURE_STATE(!mAttemptedInit);
mAttemptedInit = true;
@ -432,7 +433,7 @@ DOMParser::InitInternal(nsISupports* aOwner, nsIPrincipal* prin,
}
}
nsCOMPtr<nsIScriptGlobalObject> scriptglobal = do_QueryInterface(aOwner);
nsCOMPtr<nsIGlobalObject> scriptglobal = do_QueryInterface(aOwner);
return Init(prin, documentURI, baseURI, scriptglobal);
}
@ -442,26 +443,22 @@ DOMParser::Init(nsIPrincipal* aPrincipal, nsIURI* aDocumentURI,
{
AttemptedInitMarker marker(&mAttemptedInit);
JSContext *cx = nsContentUtils::GetCurrentJSContext();
if (!cx) {
rv.Throw(NS_ERROR_UNEXPECTED);
return;
}
nsIScriptContext* scriptContext = GetScriptContextFromJSContext(cx);
nsCOMPtr<nsIPrincipal> principal = aPrincipal;
if (!principal && !aDocumentURI) {
principal = nsContentUtils::SubjectPrincipal();
}
rv = Init(principal, aDocumentURI, aBaseURI,
scriptContext ? scriptContext->GetGlobalObject() : nullptr);
rv = Init(principal, aDocumentURI, aBaseURI, GetEntryGlobal());
}
nsresult
DOMParser::SetUpDocument(DocumentFlavor aFlavor, nsIDOMDocument** aResult)
{
// We should really QI to nsIGlobalObject here, but nsDocument gets confused
// if we pass it a scriptHandlingObject that doesn't QI to
// nsIScriptGlobalObject, and test_isequalnode.js (an xpcshell test without
// a window global) breaks. The correct solution is just to wean nsDocument
// off of nsIScriptGlobalObject, but that's a yak to shave another day.
nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
do_QueryReferent(mScriptHandlingObject);
nsresult rv;

View File

@ -601,7 +601,7 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
nsCOMPtr<nsIURI> uri = do_QueryInterface(aBlockedContentSource);
// could be a string or URI
if (uri) {
uri->GetSpec(reportBlockedURI);
uri->GetSpecIgnoringRef(reportBlockedURI);
} else {
nsCOMPtr<nsISupportsCString> cstr = do_QueryInterface(aBlockedContentSource);
if (cstr) {
@ -619,7 +619,7 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
// document-uri
if (aOriginalURI) {
nsAutoCString reportDocumentURI;
aOriginalURI->GetSpec(reportDocumentURI);
aOriginalURI->GetSpecIgnoringRef(reportDocumentURI);
report.mCsp_report.mDocument_uri = NS_ConvertUTF8toUTF16(reportDocumentURI);
}
@ -641,6 +641,14 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
// source-file
if (!aSourceFile.IsEmpty()) {
// if aSourceFile is a URI, we have to make sure to strip fragments
nsCOMPtr<nsIURI> sourceURI;
NS_NewURI(getter_AddRefs(sourceURI), aSourceFile);
if (sourceURI) {
nsAutoCString spec;
sourceURI->GetSpecIgnoringRef(spec);
aSourceFile = NS_ConvertUTF8toUTF16(spec);
}
report.mCsp_report.mSource_file.Construct();
report.mCsp_report.mSource_file.Value() = aSourceFile;
}

View File

@ -1214,8 +1214,6 @@ GK_ATOM(withParam, "with-param")
GK_ATOM(wizard, "wizard")
GK_ATOM(wrap, "wrap")
GK_ATOM(headerDNSPrefetchControl,"x-dns-prefetch-control")
GK_ATOM(headerOldCSP, "x-content-security-policy")
GK_ATOM(headerOldCSPReportOnly, "x-content-security-policy-report-only")
GK_ATOM(headerCSP, "content-security-policy")
GK_ATOM(headerCSPReportOnly, "content-security-policy-report-only")
GK_ATOM(headerXFO, "x-frame-options")

View File

@ -166,7 +166,7 @@
let func = message.objects.func;
let result = func(n => 2*n);
ok(result == 20, "result == 20");
let obj = {a:1};
let obj = {a:1, __exposedProps__: {"a": "r"}};
savedMM.sendAsyncMessage("cpows:from_parent", {}, {obj: obj});
}
@ -191,7 +191,7 @@
}
let savedWilldieObj;
let wontDie = {f:2};
let wontDie = {f:2, __exposedProps__: {"f": "r"}};
function recvLifetimeTest1(message) {
let obj = message.objects.obj;
savedWilldieObj = obj.will_die;

View File

@ -11,15 +11,15 @@ var thisSite = "http://mochi.test:8888";
var otherSite = "http://example.com";
var page = "/tests/content/base/test/csp/file_csp_redirects_page.sjs";
var tests = { "font-src": thisSite+page+"?testid=font-src&csp=1&spec=1",
"frame-src": thisSite+page+"?testid=frame-src&csp=1&spec=1",
"img-src": thisSite+page+"?testid=img-src&csp=1&spec=1",
"media-src": thisSite+page+"?testid=media-src&csp=1&spec=1",
"object-src": thisSite+page+"?testid=object-src&csp=1&spec=1",
"script-src": thisSite+page+"?testid=script-src&csp=1&spec=1",
"style-src": thisSite+page+"?testid=style-src&csp=1&spec=1",
"worker": thisSite+page+"?testid=worker&csp=1&spec=1",
"xhr-src": thisSite+page+"?testid=xhr-src&csp=1&spec=1",
var tests = { "font-src": thisSite+page+"?testid=font-src&csp=1",
"frame-src": thisSite+page+"?testid=frame-src&csp=1",
"img-src": thisSite+page+"?testid=img-src&csp=1",
"media-src": thisSite+page+"?testid=media-src&csp=1",
"object-src": thisSite+page+"?testid=object-src&csp=1",
"script-src": thisSite+page+"?testid=script-src&csp=1",
"style-src": thisSite+page+"?testid=style-src&csp=1",
"worker": thisSite+page+"?testid=worker&csp=1",
"xhr-src": thisSite+page+"?testid=xhr-src&csp=1",
};
var container = document.getElementById("container");

View File

@ -15,11 +15,7 @@ function handleRequest(request, response)
// CSP header value
if (query["csp"] == 1) {
if (query["spec"] == 1) {
response.setHeader("Content-Security-Policy", "default-src 'self' ; style-src 'self' 'unsafe-inline'", false);
} else {
response.setHeader("X-Content-Security-Policy", "allow 'self'", false);
}
response.setHeader("Content-Security-Policy", "default-src 'self' ; style-src 'self' 'unsafe-inline'", false);
}
// downloadable font that redirects to another site

View File

@ -37,6 +37,11 @@ const docUri = "http://mochi.test:8888/tests/content/base/test/csp/file_csp_test
window.checkResults = function(reportObj) {
var cspReport = reportObj["csp-report"];
// The following uris' fragments should be stripped before reporting:
// * document-uri
// * blocked-uri
// * source-file
// see http://www.w3.org/TR/CSP11/#violation-reports
is(cspReport["document-uri"], docUri, "Incorrect document-uri");
// we can not test for the whole referrer since it includes platform specific information
@ -130,6 +135,9 @@ var src = "file_csp_testserver.sjs";
src += "?file=" + escape(testfile);
// append the CSP that should be used to serve the file
src += "&csp=" + escape(policy);
// appending a fragment so we can test that it's correctly stripped
// for document-uri and source-file.
src += "#foo";
document.getElementById("cspframe").src = src;
</script>

View File

@ -119,6 +119,10 @@ public:
// required to begin playback have been acquired. Can be called on any thread.
virtual void NotifyWaitingForResourcesStatusChanged() = 0;
// Called by the reader's MediaResource as data arrives over the network.
// Must be called on the main thread.
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) = 0;
// Set by Reader if the current audio track can be offloaded
virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {}

View File

@ -302,7 +302,7 @@ AudioSink::PlayFromAudioQueue()
GetReentrantMonitor().NotifyAll();
}
SINK_LOG_V("playing %u frames of audio at time %lld",
this, audio->mFrames, audio->mTime);
audio->mFrames, audio->mTime);
mAudioStream->Write(audio->mAudioData, audio->mFrames);
StartAudioStreamPlaybackIfNeeded();

View File

@ -575,13 +575,13 @@ void AudioStream::PanOutputIfNeeded(bool aMicrophoneActive)
}
if (!strncmp(name, "MacBookPro", 10)) {
if (cubeb_stream_get_current_device(mCubebStream, &device) == CUBEB_OK) {
if (cubeb_stream_get_current_device(mCubebStream.get(), &device) == CUBEB_OK) {
// Check if we are currently outputing sound on external speakers.
if (!strcmp(device->output_name, "ispk")) {
// Pan everything to the right speaker.
if (aMicrophoneActive) {
LOG(("%p Panning audio output to the right.", this));
if (cubeb_stream_set_panning(mCubebStream, 1.0) != CUBEB_OK) {
if (cubeb_stream_set_panning(mCubebStream.get(), 1.0) != CUBEB_OK) {
NS_WARNING("Could not pan audio output to the right.");
}
} else {
@ -592,11 +592,11 @@ void AudioStream::PanOutputIfNeeded(bool aMicrophoneActive)
}
if (panCenter) {
LOG(("%p Panning audio output to the center.", this));
if (cubeb_stream_set_panning(mCubebStream, 0.0) != CUBEB_OK) {
if (cubeb_stream_set_panning(mCubebStream.get(), 0.0) != CUBEB_OK) {
NS_WARNING("Could not pan audio output to the center.");
}
}
cubeb_stream_device_destroy(mCubebStream, device);
cubeb_stream_device_destroy(mCubebStream.get(), device);
}
}
#endif
@ -609,14 +609,14 @@ void AudioStream::ResetStreamIfNeeded()
if (!mMicrophoneActive || mLatencyRequest != LowLatency) {
return;
}
if (cubeb_stream_get_current_device(mCubebStream, &device) == CUBEB_OK) {
if (cubeb_stream_get_current_device(mCubebStream.get(), &device) == CUBEB_OK) {
// This a microphone that goes through the headphone plug, reset the
// output to prevent echo building up.
if (strcmp(device->input_name, "emic") == 0) {
LOG(("Resetting audio output"));
Reset();
}
cubeb_stream_device_destroy(mCubebStream, device);
cubeb_stream_device_destroy(mCubebStream.get(), device);
}
}
@ -660,7 +660,7 @@ AudioStream::OpenCubeb(cubeb_stream_params &aParams,
latency, DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
MonitorAutoLock mon(mMonitor);
MOZ_ASSERT(mState != SHUTDOWN);
mCubebStream.own(stream);
mCubebStream.reset(stream);
// We can't cubeb_stream_start() the thread from a transient thread due to
// cubeb API requirements (init can be called from another thread, but
// not start/stop/destroy/etc)
@ -672,7 +672,7 @@ AudioStream::OpenCubeb(cubeb_stream_params &aParams,
}
}
cubeb_stream_register_device_changed_callback(mCubebStream,
cubeb_stream_register_device_changed_callback(mCubebStream.get(),
AudioStream::DeviceChangedCallback_s);
mState = INITIALIZED;
@ -844,7 +844,7 @@ AudioStream::SetVolume(double aVolume)
{
NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
if (cubeb_stream_set_volume(mCubebStream, aVolume * GetVolumeScale()) != CUBEB_OK) {
if (cubeb_stream_set_volume(mCubebStream.get(), aVolume * GetVolumeScale()) != CUBEB_OK) {
NS_WARNING("Could not change volume on cubeb stream.");
}
}
@ -902,7 +902,7 @@ AudioStream::StartUnlocked()
int r;
{
MonitorAutoUnlock mon(mMonitor);
r = cubeb_stream_start(mCubebStream);
r = cubeb_stream_start(mCubebStream.get());
PanOutputIfNeeded(mMicrophoneActive);
}
@ -924,7 +924,7 @@ AudioStream::Pause()
int r;
{
MonitorAutoUnlock mon(mMonitor);
r = cubeb_stream_stop(mCubebStream);
r = cubeb_stream_stop(mCubebStream.get());
}
if (mState != ERRORED && r == CUBEB_OK) {
mState = STOPPED;
@ -942,7 +942,7 @@ AudioStream::Resume()
int r;
{
MonitorAutoUnlock mon(mMonitor);
r = cubeb_stream_start(mCubebStream);
r = cubeb_stream_start(mCubebStream.get());
}
if (mState != ERRORED && r == CUBEB_OK) {
mState = STARTED;
@ -962,7 +962,7 @@ AudioStream::Shutdown()
if (mCubebStream) {
MonitorAutoUnlock mon(mMonitor);
// Force stop to put the cubeb stream in a stable state before deletion.
cubeb_stream_stop(mCubebStream);
cubeb_stream_stop(mCubebStream.get());
// Must not try to shut down cubeb from within the lock! wasapi may still
// call our callback after Pause()/stop()!?! Bug 996162
mCubebStream.reset();
@ -1004,7 +1004,7 @@ AudioStream::GetPositionInFramesUnlocked()
uint64_t position = 0;
{
MonitorAutoUnlock mon(mMonitor);
if (cubeb_stream_get_position(mCubebStream, &position) != CUBEB_OK) {
if (cubeb_stream_get_position(mCubebStream.get(), &position) != CUBEB_OK) {
return -1;
}
}
@ -1016,7 +1016,7 @@ int64_t
AudioStream::GetLatencyInFrames()
{
uint32_t latency;
if (cubeb_stream_get_latency(mCubebStream, &latency)) {
if (cubeb_stream_get_latency(mCubebStream.get(), &latency)) {
NS_WARNING("Could not get cubeb latency.");
return 0;
}
@ -1291,7 +1291,7 @@ AudioStream::DataCallback(void* aBuffer, long aFrames)
mState != SHUTDOWN &&
insertTime != INT64_MAX && servicedFrames > underrunFrames) {
uint32_t latency = UINT32_MAX;
if (cubeb_stream_get_latency(mCubebStream, &latency)) {
if (cubeb_stream_get_latency(mCubebStream.get(), &latency)) {
NS_WARNING("Could not get latency from cubeb.");
}
TimeStamp now = TimeStamp::Now();

View File

@ -8,29 +8,31 @@
#include "AudioSampleFormat.h"
#include "nsAutoPtr.h"
#include "nsAutoRef.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
#include "Latency.h"
#include "mozilla/dom/AudioChannelBinding.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/UniquePtr.h"
#include "cubeb/cubeb.h"
template <>
class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
{
public:
static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
};
namespace soundtouch {
class SoundTouch;
}
namespace mozilla {
template<>
struct DefaultDelete<cubeb_stream>
{
void operator()(cubeb_stream* aStream) const
{
cubeb_stream_destroy(aStream);
}
};
class AudioStream;
class FrameHistory;
@ -387,9 +389,8 @@ private:
// frames.
CircularByteBuffer mBuffer;
// Owning reference to a cubeb_stream. cubeb_stream_destroy is called by
// nsAutoRef's destructor.
nsAutoRef<cubeb_stream> mCubebStream;
// Owning reference to a cubeb_stream.
UniquePtr<cubeb_stream> mCubebStream;
uint32_t mBytesPerFrame;

View File

@ -188,6 +188,12 @@ BufferDecoder::NotifyWaitingForResourcesStatusChanged()
// ignore
}
void
BufferDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
{
// ignore
}
MediaDecoderOwner*
BufferDecoder::GetOwner()
{

View File

@ -75,6 +75,8 @@ public:
virtual void NotifyWaitingForResourcesStatusChanged() MOZ_OVERRIDE;
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) MOZ_OVERRIDE;
protected:
virtual ~BufferDecoder();

View File

@ -106,7 +106,7 @@ private:
virtual void Pin() {}
virtual void Unpin() {}
virtual double GetDownloadRate(bool* aIsReliable) { return 0.; }
virtual double GetDownloadRate(bool* aIsReliable) { *aIsReliable = false; return 0.; }
virtual int64_t GetLength() { return mLength; }
virtual int64_t GetNextCachedData(int64_t aOffset) { return aOffset; }
virtual int64_t GetCachedDataEnd(int64_t aOffset) { return mLength; }

View File

@ -58,12 +58,22 @@ static const int64_t CAN_PLAY_THROUGH_MARGIN = 1;
#ifdef PR_LOGGING
PRLogModuleInfo* gMediaDecoderLog;
#define DECODER_LOG(type, msg, ...) \
PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, this, ##__VA_ARGS__))
#define DECODER_LOG(x, ...) \
PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, ("Decoder=%p " x, this, ##__VA_ARGS__))
#else
#define DECODER_LOG(type, msg, ...)
#define DECODER_LOG(x, ...)
#endif
static const char* const gPlayStateStr[] = {
"START",
"LOADING",
"PAUSED",
"PLAYING",
"SEEKING",
"ENDED",
"SHUTDOWN"
};
class MediaMemoryTracker : public nsIMemoryReporter
{
virtual ~MediaMemoryTracker();
@ -331,7 +341,7 @@ void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
{
MOZ_ASSERT(NS_IsMainThread());
GetReentrantMonitor().AssertCurrentThreadIn();
DECODER_LOG(PR_LOG_DEBUG, "RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
DECODER_LOG("RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
DestroyDecodedStream();
@ -363,7 +373,7 @@ void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
bool aFinishWhenEnded)
{
MOZ_ASSERT(NS_IsMainThread());
DECODER_LOG(PR_LOG_DEBUG, "AddOutputStream aStream=%p!", aStream);
DECODER_LOG("AddOutputStream aStream=%p!", aStream);
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
@ -524,10 +534,7 @@ nsresult MediaDecoder::OpenResource(nsIStreamListener** aStreamListener)
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
nsresult rv = mResource->Open(aStreamListener);
if (NS_FAILED(rv)) {
DECODER_LOG(PR_LOG_WARNING, "Failed to open stream!");
return rv;
}
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
@ -541,10 +548,7 @@ nsresult MediaDecoder::Load(nsIStreamListener** aStreamListener,
NS_ENSURE_SUCCESS(rv, rv);
mDecoderStateMachine = CreateStateMachine();
if (!mDecoderStateMachine) {
DECODER_LOG(PR_LOG_WARNING, "Failed to create state machine!");
return NS_ERROR_FAILURE;
}
NS_ENSURE_TRUE(mDecoderStateMachine, NS_ERROR_FAILURE);
return InitializeStateMachine(aCloneDonor);
}
@ -555,11 +559,9 @@ nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
MediaDecoder* cloneDonor = static_cast<MediaDecoder*>(aCloneDonor);
if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ?
cloneDonor->mDecoderStateMachine : nullptr))) {
DECODER_LOG(PR_LOG_WARNING, "Failed to init state machine!");
return NS_ERROR_FAILURE;
}
nsresult rv = mDecoderStateMachine->Init(
cloneDonor ? cloneDonor->mDecoderStateMachine : nullptr);
NS_ENSURE_SUCCESS(rv, rv);
// If some parameters got set before the state machine got created,
// set them now
@ -693,6 +695,10 @@ void MediaDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags)
return;
}
DECODER_LOG("MetadataLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d",
aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
aInfo->HasAudio(), aInfo->HasVideo());
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mPlayState == PLAY_STATE_LOADING && mIsDormant && !mIsExitingDormant) {
@ -1006,6 +1012,8 @@ void MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
{
MOZ_ASSERT(NS_IsMainThread());
DECODER_LOG("NotifyDownloadEnded, status=%x", aStatus);
if (aStatus == NS_BINDING_ABORTED) {
// Download has been cancelled by user.
if (mOwner) {
@ -1168,6 +1176,9 @@ void MediaDecoder::ChangeState(PlayState aState)
mDecodedStream->mHaveBlockedForPlayState = blockForPlayState;
}
}
DECODER_LOG("ChangeState %s => %s",
gPlayStateStr[mPlayState], gPlayStateStr[aState]);
mPlayState = aState;
if (mPlayState == PLAY_STATE_PLAYING) {
@ -1265,7 +1276,7 @@ void MediaDecoder::DurationChanged()
SetInfinite(mDuration == -1);
if (mOwner && oldDuration != mDuration && !IsInfinite()) {
DECODER_LOG(PR_LOG_DEBUG, "Duration changed to %lld", mDuration);
DECODER_LOG("Duration changed to %lld", mDuration);
mOwner->DispatchEvent(NS_LITERAL_STRING("durationchange"));
}
}
@ -1486,10 +1497,8 @@ void MediaDecoder::Invalidate()
// Constructs the time ranges representing what segments of the media
// are buffered and playable.
nsresult MediaDecoder::GetBuffered(dom::TimeRanges* aBuffered) {
if (mDecoderStateMachine) {
return mDecoderStateMachine->GetBuffered(aBuffered);
}
return NS_ERROR_FAILURE;
NS_ENSURE_TRUE(mDecoderStateMachine, NS_ERROR_FAILURE);
return mDecoderStateMachine->GetBuffered(aBuffered);
}
size_t MediaDecoder::SizeOfVideoQueue() {

View File

@ -570,7 +570,7 @@ public:
// Called as data arrives on the stream and is read into the cache. Called
// on the main thread only.
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) MOZ_OVERRIDE;
// Called by MediaResource when the principal of the resource has
// changed. Called on main thread only.

View File

@ -2594,20 +2594,20 @@ void MediaDecoderStateMachine::AdvanceFrame()
int64_t remainingTime = AUDIO_DURATION_USECS;
NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
nsAutoPtr<VideoData> currentFrame;
#ifdef PR_LOGGING
int32_t droppedFrames = 0;
#endif
if (VideoQueue().GetSize() > 0) {
VideoData* frame = VideoQueue().PeekFront();
#ifdef PR_LOGGING
int32_t droppedFrames = 0;
#endif
while (mScheduler->IsRealTime() || clock_time >= frame->mTime) {
mVideoFrameEndTime = frame->GetEndTime();
currentFrame = frame;
#ifdef PR_LOGGING
VERBOSE_LOG("discarding video frame %lld", frame->mTime);
if (droppedFrames++) {
VERBOSE_LOG("discarding video frame %lld (%d so far)", frame->mTime, droppedFrames-1);
if (currentFrame) {
VERBOSE_LOG("discarding video frame mTime=%lld clock_time=%lld (%d so far)",
currentFrame->mTime, ++droppedFrames);
}
#endif
currentFrame = frame;
VideoQueue().PopFront();
// Notify the decode thread that the video queue's buffers may have
// free'd up space for more frames.

View File

@ -146,7 +146,7 @@ public:
virtual bool IsSuspended() MOZ_OVERRIDE { return false; }
virtual bool IsTransportSeekable() MOZ_OVERRIDE { return true; }
// dummy
virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { return 0; }
virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { *aIsReliable = false; return 0; }
virtual int64_t GetLength() MOZ_OVERRIDE {
if (mRealTime) {

View File

@ -11,10 +11,8 @@
#include "VideoUtils.h"
#include "nsXPCOMCIDInternal.h"
#include "nsComponentManagerUtils.h"
#ifdef XP_WIN
// Required to init MSCOM by MSCOMInitThreadPoolListener.
#include <objbase.h>
#include "ThreadPoolCOMListener.h"
#endif
namespace mozilla {
@ -187,40 +185,6 @@ SharedThreadPool::~SharedThreadPool()
MOZ_COUNT_DTOR(SharedThreadPool);
}
#ifdef XP_WIN
// Thread pool listener which ensures that MSCOM is initialized and
// deinitialized on the thread pool thread. We may call into WMF or
// DirectShow on this thread, so we need MSCOM working.
class MSCOMInitThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
~MSCOMInitThreadPoolListener() {}
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITHREADPOOLLISTENER
};
NS_IMPL_ISUPPORTS(MSCOMInitThreadPoolListener, nsIThreadPoolListener)
NS_IMETHODIMP
MSCOMInitThreadPoolListener::OnThreadCreated()
{
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr)) {
NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread.");
}
return NS_OK;
}
NS_IMETHODIMP
MSCOMInitThreadPoolListener::OnThreadShuttingDown()
{
CoUninitialize();
return NS_OK;
}
#endif // XP_WIN
nsresult
SharedThreadPool::EnsureThreadLimitIsAtLeast(uint32_t aLimit)
{

View File

@ -0,0 +1,30 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "ThreadPoolCOMListener.h"
namespace mozilla {
NS_IMPL_ISUPPORTS(MSCOMInitThreadPoolListener, nsIThreadPoolListener)
NS_IMETHODIMP
MSCOMInitThreadPoolListener::OnThreadCreated()
{
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr)) {
NS_WARNING("Failed to initialize MSCOM on decoder thread.");
}
return NS_OK;
}
NS_IMETHODIMP
MSCOMInitThreadPoolListener::OnThreadShuttingDown()
{
CoUninitialize();
return NS_OK;
}
}

View File

@ -0,0 +1,28 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#ifndef MSCOMInitThreadPoolListener_h_
#define MSCOMInitThreadPoolListener_h_
#include "nsIThreadPool.h"
#include <objbase.h>
namespace mozilla {
// Thread pool listener which ensures that MSCOM is initialized and
// deinitialized on the thread pool thread. We may call into WMF or
// DirectShow on this thread, so we need MSCOM working.
class MSCOMInitThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
~MSCOMInitThreadPoolListener() {}
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITHREADPOOLLISTENER
};
} // namespace mozilla
#endif // MSCOMInitThreadPoolListener_h_

View File

@ -311,6 +311,14 @@ void
CDMProxy::gmp_Shutdown()
{
MOZ_ASSERT(IsOnGMPThread());
// Abort any pending decrypt jobs, to awaken any clients waiting on a job.
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
DecryptJob* job = mDecryptionJobs[i];
job->mClient->Decrypted(NS_ERROR_ABORT, nullptr);
}
mDecryptionJobs.Clear();
if (mCDM) {
mCDM->Close();
mCDM = nullptr;
@ -487,11 +495,14 @@ CDMProxy::gmp_Decrypted(uint32_t aId,
if (aDecryptedData.Length() != job->mSample->size) {
NS_WARNING("CDM returned incorrect number of decrypted bytes");
}
PodCopy(job->mSample->data,
aDecryptedData.Elements(),
std::min<size_t>(aDecryptedData.Length(), job->mSample->size));
nsresult rv = GMP_SUCCEEDED(aResult) ? NS_OK : NS_ERROR_FAILURE;
job->mClient->Decrypted(rv, job->mSample.forget());
if (GMP_SUCCEEDED(aResult)) {
PodCopy(job->mSample->data,
aDecryptedData.Elements(),
std::min<size_t>(aDecryptedData.Length(), job->mSample->size));
job->mClient->Decrypted(NS_OK, job->mSample.forget());
} else {
job->mClient->Decrypted(NS_ERROR_FAILURE, nullptr);
}
mDecryptionJobs.RemoveElementAt(i);
return;
} else {

View File

@ -317,6 +317,8 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
return NS_ERROR_FAILURE;
}
mInfo.mAudio.mHasAudio = mAudio.mActive = mDemuxer->HasValidAudio();
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mIsEncrypted = mDemuxer->Crypto().valid;
@ -377,9 +379,8 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
}
if (mDemuxer->HasValidAudio()) {
if (HasAudio()) {
const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
mInfo.mAudio.mHasAudio = mAudio.mActive = true;
if (mInfo.mAudio.mHasAudio && !IsSupportedAudioMimeType(audio.mime_type)) {
return NS_ERROR_FAILURE;
}
@ -595,6 +596,8 @@ MP4Reader::Output(TrackType aTrack, MediaData* aSample)
// Don't accept output while we're flushing.
MonitorAutoLock mon(data.mMonitor);
if (data.mIsFlushing) {
delete aSample;
LOG("MP4Reader produced output while flushing, discarding.");
mon.NotifyAll();
return;
}

View File

@ -55,7 +55,7 @@ AppleATDecoder::AppleATDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
AppleATDecoder::~AppleATDecoder()
{
MOZ_COUNT_DTOR(AppleATDecoer);
MOZ_COUNT_DTOR(AppleATDecoder);
MOZ_ASSERT(!mConverter);
MOZ_ASSERT(!mStream);
}
@ -129,6 +129,7 @@ nsresult
AppleATDecoder::Flush()
{
LOG("Flushing AudioToolbox AAC decoder");
mTaskQueue->Flush();
OSStatus rv = AudioConverterReset(mConverter);
if (rv) {
LOG("Error %d resetting AudioConverter", rv);

View File

@ -111,6 +111,7 @@ AppleVTDecoder::Input(mp4_demuxer::MP4Sample* aSample)
nsresult
AppleVTDecoder::Flush()
{
mTaskQueue->Flush();
nsresult rv = WaitForAsynchronousFrames();
if (NS_FAILED(rv)) {
LOG("AppleVTDecoder::Drain failed waiting for platform decoder.");

View File

@ -83,7 +83,7 @@ public:
mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
if (NS_FAILED(aResult)) {
mDecryptor->mCallback->Error();
delete aSample;
MOZ_ASSERT(!aSample);
} else {
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,

View File

@ -100,9 +100,22 @@ GonkAudioDecoderManager::CreateAudioData(int64_t aStreamOffset, AudioData **v) {
size_t size;
int64_t timeUs;
if (!(mAudioBuffer != nullptr && mAudioBuffer->data() != nullptr)) {
ALOG("Audio Buffer is not valid!");
return NS_ERROR_UNEXPECTED;
}
if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
return NS_ERROR_UNEXPECTED;
}
if (mAudioBuffer->range_length() == 0) {
// Some decoders may return spurious empty buffers that we just want to ignore
// quoted from Android's AwesomePlayer.cpp
ReleaseAudioBuffer();
return NS_ERROR_NOT_AVAILABLE;
}
data = mAudioBuffer->data();
dataOffset = mAudioBuffer->range_offset();
size = mAudioBuffer->range_length();
@ -137,16 +150,12 @@ GonkAudioDecoderManager::Output(int64_t aStreamOffset,
switch (err) {
case OK:
{
if (mAudioBuffer && mAudioBuffer->range_length() != 0) {
int64_t timeUs;
if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
return NS_ERROR_UNEXPECTED;
}
}
AudioData* data = nullptr;
nsresult rv = CreateAudioData(aStreamOffset, &data);
// Frame should be non null only when we succeeded.
if (rv != NS_OK) {
if (rv == NS_ERROR_NOT_AVAILABLE) {
// Decoder outputs a empty video buffer, try again
return NS_ERROR_NOT_AVAILABLE;
} else if (rv != NS_OK || data == nullptr) {
return NS_ERROR_UNEXPECTED;
}
aOutData = data;
@ -165,7 +174,17 @@ GonkAudioDecoderManager::Output(int64_t aStreamOffset,
}
case android::ERROR_END_OF_STREAM:
{
ALOG("End of Stream");
ALOG("Got EOS frame!");
AudioData* data = nullptr;
nsresult rv = CreateAudioData(aStreamOffset, &data);
if (rv == NS_ERROR_NOT_AVAILABLE) {
// For EOS, no need to do any thing.
return NS_ERROR_ABORT;
} else if (rv != NS_OK || data == nullptr) {
ALOG("Failed to create audio data!");
return NS_ERROR_UNEXPECTED;
}
aOutData = data;
return NS_ERROR_ABORT;
}
case -ETIMEDOUT:
@ -197,9 +216,20 @@ void GonkAudioDecoderManager::ReleaseAudioBuffer() {
nsresult
GonkAudioDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
{
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
uint32_t length = aSample->size;
status_t rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
if (mDecoder == nullptr) {
ALOG("Decoder is not inited");
return NS_ERROR_UNEXPECTED;
}
status_t rv;
if (aSample) {
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
uint32_t length = aSample->size;
rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
} else {
// Inputted data is null, so it is going to notify decoder EOS
rv = mDecoder->Input(0, 0, 0ll, 0);
}
return rv == OK ? NS_OK : NS_ERROR_UNEXPECTED;
}

View File

@ -31,6 +31,7 @@ GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
: mTaskQueue(aTaskQueue)
, mCallback(aCallback)
, mManager(aManager)
, mSignaledEOS(false)
{
MOZ_COUNT_CTOR(GonkMediaDataDecoder);
}
@ -77,8 +78,9 @@ GonkMediaDataDecoder::ProcessDecode(mp4_demuxer::MP4Sample* aSample)
mCallback->Error();
return;
}
mLastStreamOffset = aSample->byte_offset;
if (aSample) {
mLastStreamOffset = aSample->byte_offset;
}
ProcessOutput();
}
@ -92,6 +94,9 @@ GonkMediaDataDecoder::ProcessOutput()
if (rv == NS_OK) {
mCallback->Output(output.forget());
continue;
} else if (rv == NS_ERROR_NOT_AVAILABLE && mSignaledEOS) {
// Try to get more frames before getting EOS frame
continue;
}
else {
break;
@ -105,6 +110,15 @@ GonkMediaDataDecoder::ProcessOutput()
if (rv != NS_OK) {
NS_WARNING("GonkMediaDataDecoder failed to output data");
ALOG("Failed to output data");
// GonkDecoderManangers report NS_ERROR_ABORT when EOS is reached.
if (rv == NS_ERROR_ABORT) {
if (output.get() != nullptr) {
mCallback->Output(output.forget());
}
mCallback->DrainComplete();
mSignaledEOS = false;
return;
}
mCallback->Error();
}
}
@ -125,9 +139,10 @@ GonkMediaDataDecoder::Flush()
void
GonkMediaDataDecoder::ProcessDrain()
{
// Then extract all available output.
// Notify decoder input EOS by sending a null data.
ProcessDecode(nullptr);
mSignaledEOS = true;
ProcessOutput();
mCallback->DrainComplete();
}
nsresult

View File

@ -68,7 +68,9 @@ public:
private:
// Called on the task queue. Inserts the sample into the decoder, and
// extracts output if available.
// extracts output if available, if aSample is null, it means there is
// no data from source, it will notify the decoder EOS and flush all the
// decoded frames.
void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
// Called on the task queue. Extracts output if available, and delivers
@ -88,6 +90,8 @@ private:
// The last offset into the media resource that was passed into Input().
// This is used to approximate the decoder's position in the media resource.
int64_t mLastStreamOffset;
// Set it ture when there is no input data
bool mSignaledEOS;
};
} // namespace mozilla

View File

@ -115,11 +115,24 @@ GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v)
*v = nullptr;
int64_t timeUs;
int32_t keyFrame;
if (!(mVideoBuffer != nullptr && mVideoBuffer->data() != nullptr)) {
ALOG("Video Buffer is not valid!");
return NS_ERROR_UNEXPECTED;
}
if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
ALOG("Decoder did not return frame time");
return NS_ERROR_UNEXPECTED;
}
if (mVideoBuffer->range_length() == 0) {
// Some decoders may return spurious empty buffers that we just want to ignore
// quoted from Android's AwesomePlayer.cpp
ReleaseVideoBuffer();
return NS_ERROR_NOT_AVAILABLE;
}
if (!mVideoBuffer->meta_data()->findInt32(kKeyIsSyncFrame, &keyFrame)) {
keyFrame = 0;
}
@ -137,11 +150,6 @@ GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v)
picture.height = (mFrameInfo.mHeight * mPicture.height) / mInitialFrame.height;
}
if (!(mVideoBuffer != nullptr && mVideoBuffer->size() > 0 && mVideoBuffer->data() != nullptr)) {
ALOG("mVideoBuffer is not valid!");
return NS_ERROR_UNEXPECTED;
}
uint8_t *yuv420p_buffer = (uint8_t *)mVideoBuffer->data();
int32_t stride = mFrameInfo.mStride;
int32_t slice_height = mFrameInfo.mSliceHeight;
@ -268,9 +276,11 @@ GonkVideoDecoderManager::Output(int64_t aStreamOffset,
{
VideoData* data = nullptr;
nsresult rv = CreateVideoData(aStreamOffset, &data);
// Frame should be non null only when we succeeded.
if (rv != NS_OK || data == nullptr){
ALOG("Error unexpected in CreateVideoData");
if (rv == NS_ERROR_NOT_AVAILABLE) {
// Decoder outputs a empty video buffer, try again
return NS_ERROR_NOT_AVAILABLE;
} else if (rv != NS_OK || data == nullptr) {
ALOG("Failed to create VideoData");
return NS_ERROR_UNEXPECTED;
}
aOutData = data;
@ -293,7 +303,18 @@ GonkVideoDecoderManager::Output(int64_t aStreamOffset,
}
case android::ERROR_END_OF_STREAM:
{
ALOG("End of Stream");
ALOG("Got the EOS frame!");
VideoData* data = nullptr;
nsresult rv = CreateVideoData(aStreamOffset, &data);
if (rv == NS_ERROR_NOT_AVAILABLE) {
// For EOS, no need to do any thing.
return NS_ERROR_ABORT;
}
if (rv != NS_OK || data == nullptr) {
ALOG("Failed to create video data");
return NS_ERROR_UNEXPECTED;
}
aOutData = data;
return NS_ERROR_ABORT;
}
case -ETIMEDOUT:
@ -325,17 +346,24 @@ void GonkVideoDecoderManager::ReleaseVideoBuffer() {
nsresult
GonkVideoDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
{
// We must prepare samples in AVC Annex B.
mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
// Forward sample data to the decoder.
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
uint32_t length = aSample->size;
if (mDecoder == nullptr) {
ALOG("Decoder is not inited");
return NS_ERROR_UNEXPECTED;
}
status_t rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
status_t rv;
if (aSample != nullptr) {
// We must prepare samples in AVC Annex B.
mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
// Forward sample data to the decoder.
const uint8_t* data = reinterpret_cast<const uint8_t*>(aSample->data);
uint32_t length = aSample->size;
rv = mDecoder->Input(data, length, aSample->composition_timestamp, 0);
}
else {
// Inputted data is null, so it is going to notify decoder EOS
rv = mDecoder->Input(nullptr, 0, 0ll, 0);
}
return (rv == OK) ? NS_OK : NS_ERROR_FAILURE;
}

View File

@ -49,8 +49,11 @@ GMPChild::~GMPChild()
}
static bool
GetPluginBinaryFile(const std::string& aPluginPath,
nsCOMPtr<nsIFile>& aLibFile)
GetPluginFile(const std::string& aPluginPath,
#if defined(XP_MACOSX)
nsCOMPtr<nsIFile>& aLibDirectory,
#endif
nsCOMPtr<nsIFile>& aLibFile)
{
nsDependentCString pluginPath(aPluginPath.c_str());
@ -60,6 +63,12 @@ GetPluginBinaryFile(const std::string& aPluginPath,
return false;
}
#if defined(XP_MACOSX)
if (NS_FAILED(aLibFile->Clone(getter_AddRefs(aLibDirectory)))) {
return false;
}
#endif
nsAutoString leafName;
if (NS_FAILED(aLibFile->GetLeafName(leafName))) {
return false;
@ -81,32 +90,48 @@ GetPluginBinaryFile(const std::string& aPluginPath,
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
static bool
GetPluginBinaryPath(const std::string& aPluginPath,
nsCString &aPluginBinaryPath)
GetPluginPaths(const std::string& aPluginPath,
nsCString &aPluginDirectoryPath,
nsCString &aPluginFilePath)
{
nsCOMPtr<nsIFile> libFile;
if (!GetPluginBinaryFile(aPluginPath, libFile)) {
nsCOMPtr<nsIFile> libDirectory, libFile;
if (!GetPluginFile(aPluginPath, libDirectory, libFile)) {
return false;
}
libFile->GetNativePath(aPluginBinaryPath);
// Mac sandbox rules expect paths to actual files and directories -- not
// soft links.
bool isLink;
libDirectory->IsSymlink(&isLink);
if (isLink) {
libDirectory->GetNativeTarget(aPluginDirectoryPath);
} else {
libDirectory->GetNativePath(aPluginDirectoryPath);
}
libFile->IsSymlink(&isLink);
if (isLink) {
libFile->GetNativeTarget(aPluginFilePath);
} else {
libFile->GetNativePath(aPluginFilePath);
}
return true;
}
void
GMPChild::OnChannelConnected(int32_t aPid)
{
nsAutoCString pluginDirectoryPath, pluginFilePath;
if (!GetPluginPaths(mPluginPath, pluginDirectoryPath, pluginFilePath)) {
MOZ_CRASH("Error scanning plugin path");
}
MacSandboxInfo info;
info.type = MacSandboxType_Plugin;
info.pluginInfo.type = MacSandboxPluginType_GMPlugin_Default;
info.pluginInfo.pluginPath.Assign(mPluginPath.c_str());
nsAutoCString pluginBinaryPath;
if (!GetPluginBinaryPath(mPluginPath, pluginBinaryPath)) {
MOZ_CRASH("Error scanning plugin path");
}
mPluginBinaryPath.Assign(pluginBinaryPath);
info.pluginInfo.pluginBinaryPath.Assign(pluginBinaryPath);
info.pluginInfo.pluginPath.Assign(pluginDirectoryPath);
mPluginBinaryPath.Assign(pluginFilePath);
info.pluginInfo.pluginBinaryPath.Assign(pluginFilePath);
nsAutoCString err;
if (!mozilla::StartMacSandbox(info, err)) {
@ -165,7 +190,7 @@ GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
mLib = PR_LoadLibrary(nativePath.get());
#else
nsCOMPtr<nsIFile> libFile;
if (!GetPluginBinaryFile(aPluginPath, libFile)) {
if (!GetPluginFile(aPluginPath, libFile)) {
return false;
}
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)

View File

@ -8,6 +8,7 @@
#include "AsyncEventRunner.h"
#include "DecoderTraits.h"
#include "MediaSourceUtils.h"
#include "SourceBuffer.h"
#include "SourceBufferList.h"
#include "mozilla/ErrorResult.h"
@ -23,7 +24,7 @@
#include "nsIEventTarget.h"
#include "nsIRunnable.h"
#include "nsPIDOMWindow.h"
#include "nsStringGlue.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "prlog.h"
@ -333,37 +334,42 @@ MediaSource::Detach()
void
MediaSource::GetBuffered(TimeRanges* aBuffered)
{
MOZ_ASSERT(aBuffered->Length() == 0);
if (mActiveSourceBuffers->IsEmpty()) {
return;
}
nsTArray<nsRefPtr<TimeRanges>> ranges;
double highestEndTime = 0;
nsTArray<nsRefPtr<TimeRanges>> activeRanges;
for (uint32_t i = 0; i < mActiveSourceBuffers->Length(); ++i) {
bool found;
SourceBuffer* sourceBuffer = mActiveSourceBuffers->IndexedGetter(i, found);
ErrorResult dummy;
*ranges.AppendElement() = sourceBuffer->GetBuffered(dummy);
*activeRanges.AppendElement() = sourceBuffer->GetBuffered(dummy);
highestEndTime = std::max(highestEndTime, activeRanges.LastElement()->GetEndTime());
}
double highestEndTime = mActiveSourceBuffers->GetHighestBufferedEndTime();
if (highestEndTime <= 0) {
return;
}
TimeRanges* intersectionRanges = aBuffered;
intersectionRanges->Add(0, highestEndTime);
MOZ_ASSERT(aBuffered->Length() == 0);
aBuffered->Add(0, highestEndTime);
for (uint32_t i = 0; i < activeRanges.Length(); ++i) {
TimeRanges* sourceRanges = activeRanges[i];
for (uint32_t i = 0; i < ranges.Length(); ++i) {
if (mReadyState == MediaSourceReadyState::Ended) {
ranges[i]->Add(ranges[i]->GetEndTime(), highestEndTime);
// Set the end time on the last range to highestEndTime by adding a
// new range spanning the current end time to highestEndTime, which
// Normalize() will then merge with the old last range.
sourceRanges->Add(sourceRanges->GetEndTime(), highestEndTime);
sourceRanges->Normalize();
}
aBuffered->Intersection(ranges[i]);
intersectionRanges->Intersection(sourceRanges);
}
MSE_DEBUG("MediaSource(%p)::GetBuffered start=%f end=%f length=%u",
this, aBuffered->GetStartTime(), aBuffered->GetEndTime(), aBuffered->Length());
MSE_DEBUG("MediaSource(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(intersectionRanges).get());
}
MediaSource::MediaSource(nsPIDOMWindow* aWindow)

View File

@ -11,6 +11,7 @@
#include "MediaSource.h"
#include "MediaSourceReader.h"
#include "MediaSourceResource.h"
#include "MediaSourceUtils.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* GetMediaSourceLog();
@ -59,14 +60,12 @@ MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
return NS_ERROR_FAILURE;
}
nsresult rv = mDecoderStateMachine->Init(nullptr);
NS_ENSURE_SUCCESS(rv, rv);
SetStateMachineParameters();
return rv;
return NS_OK;
}
nsresult
@ -82,8 +81,7 @@ MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable)
} else {
aSeekable->Add(0, duration);
}
MSE_DEBUG("MediaSourceDecoder(%p)::GetSeekable startTime=%f endTime=%f",
this, aSeekable->GetStartTime(), aSeekable->GetEndTime());
MSE_DEBUG("MediaSourceDecoder(%p)::GetSeekable ranges=%s", this, DumpTimeRanges(aSeekable).get());
return NS_OK;
}
@ -110,7 +108,8 @@ MediaSourceDecoder::DetachMediaSource()
already_AddRefed<SubBufferDecoder>
MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
{
return mReader->CreateSubDecoder(aType, this);
MOZ_ASSERT(mReader);
return mReader->CreateSubDecoder(aType);
}
} // namespace mozilla

View File

@ -11,6 +11,7 @@
#include "MediaDecoderOwner.h"
#include "MediaSource.h"
#include "MediaSourceDecoder.h"
#include "MediaSourceUtils.h"
#include "SubBufferDecoder.h"
#ifdef MOZ_FMP4
@ -36,9 +37,8 @@ namespace mozilla {
MediaSourceReader::MediaSourceReader(MediaSourceDecoder* aDecoder, dom::MediaSource* aSource)
: MediaDecoderReader(aDecoder)
, mTimeThreshold(-1)
, mDropAudioBeforeThreshold(false)
, mDropVideoBeforeThreshold(false)
, mActiveVideoDecoder(-1)
, mActiveAudioDecoder(-1)
, mMediaSource(aSource)
{
}
@ -52,41 +52,60 @@ MediaSourceReader::IsWaitingMediaResources()
void
MediaSourceReader::RequestAudioData()
{
if (!GetAudioReader()) {
if (!mAudioReader) {
MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
MOZ_ASSERT(mPendingDecoders.IsEmpty());
GetCallback()->OnDecodeError();
return;
}
GetAudioReader()->RequestAudioData();
SwitchReaders(SWITCH_OPTIONAL);
mAudioReader->RequestAudioData();
}
void
MediaSourceReader::OnAudioDecoded(AudioData* aSample)
{
if (mDropAudioBeforeThreshold) {
if (aSample->mTime < mTimeThreshold) {
MSE_DEBUG("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld < mTimeThreshold=%lld",
this, aSample->mTime, mTimeThreshold);
delete aSample;
mAudioReader->RequestAudioData();
return;
}
mDropAudioBeforeThreshold = false;
}
GetCallback()->OnAudioDecoded(aSample);
}
void
MediaSourceReader::OnAudioEOS()
{
MSE_DEBUG("MediaSourceReader(%p)::OnAudioEOS %d (%p) EOS (readers=%u)",
this, mActiveAudioDecoder, mDecoders[mActiveAudioDecoder].get(), mDecoders.Length());
GetCallback()->OnAudioEOS();
MSE_DEBUG("MediaSourceReader(%p)::OnAudioEOS reader=%p (readers=%u)",
this, mAudioReader.get(), mDecoders.Length());
if (SwitchReaders(SWITCH_FORCED)) {
// Success! Resume decoding with next audio decoder.
RequestAudioData();
} else {
// End of stream.
MSE_DEBUG("MediaSourceReader(%p)::OnAudioEOS reader=%p EOS (readers=%u)",
this, mAudioReader.get(), mDecoders.Length());
GetCallback()->OnAudioEOS();
}
}
void
MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold)
{
if (!GetVideoReader()) {
if (!mVideoReader) {
MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called with no video reader", this);
MOZ_ASSERT(mPendingDecoders.IsEmpty());
GetCallback()->OnDecodeError();
return;
}
mTimeThreshold = aTimeThreshold;
SwitchVideoReaders(SWITCH_OPTIONAL);
GetVideoReader()->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
SwitchReaders(SWITCH_OPTIONAL);
mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold);
}
void
@ -97,7 +116,7 @@ MediaSourceReader::OnVideoDecoded(VideoData* aSample)
MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld",
this, aSample->mTime, mTimeThreshold);
delete aSample;
GetVideoReader()->RequestVideoData(false, mTimeThreshold);
mVideoReader->RequestVideoData(false, mTimeThreshold);
return;
}
mDropVideoBeforeThreshold = false;
@ -109,15 +128,15 @@ void
MediaSourceReader::OnVideoEOS()
{
// End of stream. See if we can switch to another video decoder.
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS %d (%p) (readers=%u)",
this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
if (SwitchVideoReaders(SWITCH_FORCED)) {
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS reader=%p (readers=%u)",
this, mVideoReader.get(), mDecoders.Length());
if (SwitchReaders(SWITCH_FORCED)) {
// Success! Resume decoding with next video decoder.
RequestVideoData(false, mTimeThreshold);
} else {
// End of stream.
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS %d (%p) EOS (readers=%u)",
this, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get(), mDecoders.Length());
MSE_DEBUG("MediaSourceReader(%p)::OnVideoEOS reader=%p EOS (readers=%u)",
this, mVideoReader.get(), mDecoders.Length());
GetCallback()->OnVideoEOS();
}
}
@ -147,63 +166,86 @@ MediaSourceReader::BreakCycles()
}
bool
MediaSourceReader::SwitchVideoReaders(SwitchType aType)
MediaSourceReader::SwitchAudioReader(MediaDecoderReader* aTargetReader)
{
if (aTargetReader == mAudioReader) {
return false;
}
if (mAudioReader) {
AudioInfo targetInfo = aTargetReader->GetMediaInfo().mAudio;
AudioInfo currentInfo = mAudioReader->GetMediaInfo().mAudio;
// TODO: We can't handle switching audio formats yet.
if (currentInfo.mRate != targetInfo.mRate ||
currentInfo.mChannels != targetInfo.mChannels) {
return false;
}
mAudioReader->SetIdle();
}
mAudioReader = aTargetReader;
mDropAudioBeforeThreshold = true;
MSE_DEBUG("MediaDecoderReader(%p)::SwitchReaders(%p) switching audio reader",
this, mAudioReader.get());
return true;
}
bool
MediaSourceReader::SwitchVideoReader(MediaDecoderReader* aTargetReader)
{
if (aTargetReader == mVideoReader) {
return false;
}
if (mVideoReader) {
mVideoReader->SetIdle();
}
mVideoReader = aTargetReader;
mDropVideoBeforeThreshold = true;
MSE_DEBUG("MediaDecoderReader(%p)::SwitchVideoReader(%p) switching video reader",
this, mVideoReader.get());
return true;
}
bool
MediaSourceReader::SwitchReaders(SwitchType aType)
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MOZ_ASSERT(mActiveVideoDecoder != -1);
InitializePendingDecoders();
for (uint32_t i = mActiveVideoDecoder + 1; i < mDecoders.Length(); ++i) {
bool didSwitch = false;
double decodeTarget = double(mTimeThreshold) / USECS_PER_S;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
SubBufferDecoder* decoder = mDecoders[i];
const MediaInfo& info = decoder->GetReader()->GetMediaInfo();
nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges();
decoder->GetBuffered(ranges);
MSE_DEBUGV("MediaDecoderReader(%p)::SwitchVideoReaders(%d) decoder=%u (%p) discarded=%d"
" hasVideo=%d timeThreshold=%f startTime=%f endTime=%f length=%u",
MSE_DEBUGV("MediaDecoderReader(%p)::SwitchReaders(%d) decoder=%u (%p) discarded=%d"
" reader=%p audioReader=%p videoReader=%p"
" hasAudio=%d hasVideo=%d decodeTarget=%f ranges=%s",
this, aType, i, decoder, decoder->IsDiscarded(),
decoder->GetReader()->GetMediaInfo().HasVideo(),
double(mTimeThreshold) / USECS_PER_S,
ranges->GetStartTime(), ranges->GetEndTime(), ranges->Length());
decoder->GetReader(), mAudioReader.get(), mVideoReader.get(),
info.HasAudio(), info.HasVideo(), decodeTarget,
DumpTimeRanges(ranges).get());
if (decoder->IsDiscarded() ||
!decoder->GetReader()->GetMediaInfo().HasVideo() ||
ranges->Length() == 0) {
if (decoder->IsDiscarded()) {
continue;
}
if (aType == SWITCH_FORCED ||
ranges->Find(double(mTimeThreshold) / USECS_PER_S) != dom::TimeRanges::NoIndex) {
GetVideoReader()->SetIdle();
mActiveVideoDecoder = i;
mDropVideoBeforeThreshold = true;
MSE_DEBUG("MediaDecoderReader(%p)::SwitchVideoReaders(%d) switching to %d (%p)",
this, aType, mActiveVideoDecoder, mDecoders[mActiveVideoDecoder].get());
return true;
if (aType == SWITCH_FORCED || ranges->Find(decodeTarget) != dom::TimeRanges::NoIndex) {
if (info.HasAudio()) {
didSwitch |= SwitchAudioReader(mDecoders[i]->GetReader());
}
if (info.HasVideo()) {
didSwitch |= SwitchVideoReader(mDecoders[i]->GetReader());
}
}
}
return false;
}
MediaDecoderReader*
MediaSourceReader::GetAudioReader()
{
if (mActiveAudioDecoder == -1) {
return nullptr;
}
return mDecoders[mActiveAudioDecoder]->GetReader();
}
MediaDecoderReader*
MediaSourceReader::GetVideoReader()
{
if (mActiveVideoDecoder == -1) {
return nullptr;
}
return mDecoders[mActiveVideoDecoder]->GetReader();
return didSwitch;
}
void
@ -298,12 +340,11 @@ CreateReaderForType(const nsACString& aType, AbstractMediaDecoder* aDecoder)
}
already_AddRefed<SubBufferDecoder>
MediaSourceReader::CreateSubDecoder(const nsACString& aType,
MediaSourceDecoder* aParentDecoder)
MediaSourceReader::CreateSubDecoder(const nsACString& aType)
{
// XXX: Why/when is mDecoder null here, since it should be equal to aParentDecoder?!
MOZ_ASSERT(GetTaskQueue());
nsRefPtr<SubBufferDecoder> decoder =
new SubBufferDecoder(new SourceBufferResource(nullptr, aType), aParentDecoder);
new SubBufferDecoder(new SourceBufferResource(nullptr, aType), mDecoder);
nsRefPtr<MediaDecoderReader> reader(CreateReaderForType(aType, decoder));
if (!reader) {
return nullptr;
@ -316,7 +357,7 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType,
reader->SetCallback(callback);
reader->SetTaskQueue(GetTaskQueue());
reader->Init(nullptr);
ReentrantMonitorAutoEnter mon(aParentDecoder->GetReentrantMonitor());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MSE_DEBUG("MediaSourceReader(%p)::CreateSubDecoder subdecoder %p subreader %p",
this, decoder.get(), reader.get());
decoder->SetReader(reader);
@ -379,11 +420,10 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
// This is a workaround for our lack of async functionality in the
// MediaDecoderStateMachine. Bug 979104 implements what we need and
// we'll remove this for an async approach based on that in bug XXXXXXX.
while (!DecodersContainTime(target)
&& !IsShutdown()) {
while (!DecodersContainTime(target) && !IsShutdown()) {
MSE_DEBUG("MediaSourceReader(%p)::Seek waiting for target=%f", this, target);
mMediaSource->WaitForData();
SwitchVideoReaders(SWITCH_FORCED);
SwitchReaders(SWITCH_FORCED);
}
if (IsShutdown()) {
@ -391,14 +431,14 @@ MediaSourceReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
}
ResetDecode();
if (GetAudioReader()) {
nsresult rv = GetAudioReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
if (mAudioReader) {
nsresult rv = mAudioReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
if (NS_FAILED(rv)) {
return rv;
}
}
if (GetVideoReader()) {
nsresult rv = GetVideoReader()->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
if (mVideoReader) {
nsresult rv = mVideoReader->Seek(aTime, aStartTime, aEndTime, aCurrentTime);
if (NS_FAILED(rv)) {
return rv;
}
@ -427,20 +467,20 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
MediaInfo mi = reader->GetMediaInfo();
if (mi.HasVideo() && !mInfo.HasVideo()) {
MOZ_ASSERT(mActiveVideoDecoder == -1);
mActiveVideoDecoder = i;
MOZ_ASSERT(!mVideoReader);
mVideoReader = reader;
mInfo.mVideo = mi.mVideo;
maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video decoder=%u maxDuration=%lld",
this, i, maxDuration);
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p maxDuration=%lld",
this, reader, maxDuration);
}
if (mi.HasAudio() && !mInfo.HasAudio()) {
MOZ_ASSERT(mActiveAudioDecoder == -1);
mActiveAudioDecoder = i;
MOZ_ASSERT(!mAudioReader);
mAudioReader = reader;
mInfo.mAudio = mi.mAudio;
maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio decoder=%u maxDuration=%lld",
this, i, maxDuration);
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p maxDuration=%lld",
this, reader, maxDuration);
}
}

View File

@ -11,7 +11,7 @@
#include "mozilla/ReentrantMonitor.h"
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsStringGlue.h"
#include "nsString.h"
#include "nsTArray.h"
#include "MediaDecoderReader.h"
@ -70,8 +70,7 @@ public:
nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
int64_t aCurrentTime) MOZ_OVERRIDE;
already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType,
MediaSourceDecoder* aParentDecoder);
already_AddRefed<SubBufferDecoder> CreateSubDecoder(const nsACString& aType);
void Shutdown();
@ -94,22 +93,24 @@ private:
SWITCH_FORCED
};
bool SwitchVideoReaders(SwitchType aType);
bool SwitchReaders(SwitchType aType);
MediaDecoderReader* GetAudioReader();
MediaDecoderReader* GetVideoReader();
bool SwitchAudioReader(MediaDecoderReader* aTargetReader);
bool SwitchVideoReader(MediaDecoderReader* aTargetReader);
void SetMediaSourceDuration(double aDuration) ;
// These are read and written on the decode task queue threads.
int64_t mTimeThreshold;
bool mDropAudioBeforeThreshold;
bool mDropVideoBeforeThreshold;
nsTArray<nsRefPtr<SubBufferDecoder>> mPendingDecoders;
nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
int32_t mActiveVideoDecoder;
int32_t mActiveAudioDecoder;
nsRefPtr<MediaDecoderReader> mAudioReader;
nsRefPtr<MediaDecoderReader> mVideoReader;
dom::MediaSource* mMediaSource;
};

View File

@ -32,7 +32,7 @@ public:
virtual int64_t Tell() MOZ_OVERRIDE { return -1; }
virtual void Pin() MOZ_OVERRIDE {}
virtual void Unpin() MOZ_OVERRIDE {}
virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { return 0; }
virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { *aIsReliable = false; return 0; }
virtual int64_t GetLength() MOZ_OVERRIDE { return -1; }
virtual int64_t GetNextCachedData(int64_t aOffset) MOZ_OVERRIDE { return aOffset; }
virtual int64_t GetCachedDataEnd(int64_t aOffset) MOZ_OVERRIDE { return GetLength(); }

View File

@ -0,0 +1,36 @@
/* -*- 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 "MediaSourceUtils.h"
#include "prlog.h"
#include "mozilla/dom/TimeRanges.h"
#include "nsPrintfCString.h"
namespace mozilla {
#if defined(PR_LOGGING)
nsCString
DumpTimeRanges(dom::TimeRanges* aRanges)
{
nsCString dump;
dump = "[";
for (uint32_t i = 0; i < aRanges->Length(); ++i) {
if (i > 0) {
dump += ", ";
}
ErrorResult dummy;
dump += nsPrintfCString("(%f, %f)", aRanges->Start(i, dummy), aRanges->End(i, dummy));
}
dump += "]";
return dump;
}
#endif
} // namespace mozilla

View File

@ -0,0 +1,22 @@
/* -*- 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/. */
#ifndef MOZILLA_MEDIASOURCEUTILS_H_
#define MOZILLA_MEDIASOURCEUTILS_H_
#include "nsString.h"
namespace mozilla {
namespace dom {
class TimeRanges;
} // namespace dom
nsCString DumpTimeRanges(dom::TimeRanges* aRanges);
} // namespace mozilla
#endif /* MOZILLA_MEDIASOURCEUTILS_H_ */

View File

@ -0,0 +1,178 @@
/* -*- 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/. */
#ifndef MOZILLA_RESOURCEQUEUE_H_
#define MOZILLA_RESOURCEQUEUE_H_
#include <algorithm>
#include "nsDeque.h"
#include "nsTArray.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* GetSourceBufferResourceLog();
#define SBR_DEBUG(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
#define SBR_DEBUGV(...) PR_LOG(GetSourceBufferResourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
#else
#define SBR_DEBUG(...)
#define SBR_DEBUGV(...)
#endif
namespace mozilla {
// A SourceBufferResource has a queue containing the data that is appended
// to it. The queue holds instances of ResourceItem which is an array of the
// bytes. Appending data to the SourceBufferResource pushes this onto the
// queue.
// Data is evicted once it reaches a size threshold. This pops the items off
// the front of the queue and deletes it. If an eviction happens then the
// MediaSource is notified (done in SourceBuffer::AppendData) which then
// requests all SourceBuffers to evict data up to approximately the same
// timepoint.
struct ResourceItem {
ResourceItem(const uint8_t* aData, uint32_t aSize) {
mData.AppendElements(aData, aSize);
}
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
// size including this
size_t size = aMallocSizeOf(this);
// size excluding this
size += mData.SizeOfExcludingThis(aMallocSizeOf);
return size;
}
nsTArray<uint8_t> mData;
};
class ResourceQueueDeallocator : public nsDequeFunctor {
virtual void* operator() (void* aObject) {
delete static_cast<ResourceItem*>(aObject);
return nullptr;
}
};
class ResourceQueue : private nsDeque {
public:
ResourceQueue()
: nsDeque(new ResourceQueueDeallocator())
, mLogicalLength(0)
, mOffset(0)
{
}
// Returns the logical byte offset of the start of the data.
uint64_t GetOffset() {
return mOffset;
}
// Returns the length of all items in the queue plus the offset.
// This is the logical length of the resource.
uint64_t GetLength() {
return mLogicalLength;
}
// Copies aCount bytes from aOffset in the queue into aDest.
void CopyData(uint64_t aOffset, uint32_t aCount, char* aDest) {
uint32_t offset = 0;
uint32_t start = GetAtOffset(aOffset, &offset);
uint32_t end = std::min(GetAtOffset(aOffset + aCount, nullptr) + 1, uint32_t(GetSize()));
for (uint32_t i = start; i < end; ++i) {
ResourceItem* item = ResourceAt(i);
uint32_t bytes = std::min(aCount, uint32_t(item->mData.Length() - offset));
if (bytes != 0) {
memcpy(aDest, &item->mData[offset], bytes);
offset = 0;
aCount -= bytes;
aDest += bytes;
}
}
}
void AppendItem(const uint8_t* aData, uint32_t aLength) {
mLogicalLength += aLength;
Push(new ResourceItem(aData, aLength));
}
// Evict data in queue if the total queue size is greater than
// aThreshold past the offset. Returns true if some data was
// actually evicted.
bool Evict(uint64_t aOffset, uint32_t aThreshold) {
bool evicted = false;
while (GetLength() - mOffset > aThreshold) {
ResourceItem* item = ResourceAt(0);
if (item->mData.Length() + mOffset > aOffset) {
break;
}
mOffset += item->mData.Length();
SBR_DEBUGV("ResourceQueue(%p)::Evict(%llu, %u) removed chunk length=%u",
this, aOffset, aThreshold, item->mData.Length());
delete PopFront();
evicted = true;
}
return evicted;
}
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
// Calculate the size of the internal deque.
size_t size = nsDeque::SizeOfExcludingThis(aMallocSizeOf);
// Sum the ResourceItems.
for (uint32_t i = 0; i < uint32_t(GetSize()); ++i) {
const ResourceItem* item = ResourceAt(i);
size += item->SizeOfIncludingThis(aMallocSizeOf);
}
return size;
}
private:
ResourceItem* ResourceAt(uint32_t aIndex) const {
return static_cast<ResourceItem*>(ObjectAt(aIndex));
}
// Returns the index of the resource that contains the given
// logical offset. aResourceOffset will contain the offset into
// the resource at the given index returned if it is not null. If
// no such resource exists, returns GetSize() and aOffset is
// untouched.
uint32_t GetAtOffset(uint64_t aOffset, uint32_t *aResourceOffset) {
MOZ_ASSERT(aOffset >= mOffset);
uint64_t offset = mOffset;
for (uint32_t i = 0; i < uint32_t(GetSize()); ++i) {
ResourceItem* item = ResourceAt(i);
// If the item contains the start of the offset we want to
// break out of the loop.
if (item->mData.Length() + offset > aOffset) {
if (aResourceOffset) {
*aResourceOffset = aOffset - offset;
}
return i;
}
offset += item->mData.Length();
}
return GetSize();
}
ResourceItem* PopFront() {
return static_cast<ResourceItem*>(nsDeque::PopFront());
}
// Logical length of the resource.
uint64_t mLogicalLength;
// Logical offset into the resource of the first element in the queue.
uint64_t mOffset;
};
} // namespace mozilla
#endif /* MOZILLA_RESOURCEQUEUE_H_ */

View File

@ -9,6 +9,7 @@
#include "DecoderTraits.h"
#include "MediaDecoder.h"
#include "MediaSourceDecoder.h"
#include "MediaSourceUtils.h"
#include "SourceBufferResource.h"
#include "mozilla/Endian.h"
#include "mozilla/ErrorResult.h"
@ -172,6 +173,7 @@ SourceBuffer::GetBuffered(ErrorResult& aRv)
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
double highestEndTime = 0;
nsRefPtr<TimeRanges> ranges = new TimeRanges();
// TODO: Need to adjust mDecoders so it only tracks active decoders.
// Once we have an abstraction for track buffers, this needs to report the
@ -179,11 +181,19 @@ SourceBuffer::GetBuffered(ErrorResult& aRv)
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
nsRefPtr<TimeRanges> r = new TimeRanges();
mDecoders[i]->GetBuffered(r);
ranges->Union(r);
if (r->Length() > 0) {
highestEndTime = std::max(highestEndTime, r->GetEndTime());
ranges->Union(r);
}
}
ranges->Normalize();
MSE_DEBUGV("SourceBuffer(%p)::GetBuffered startTime=%f endTime=%f length=%u",
this, ranges->GetStartTime(), ranges->GetEndTime(), ranges->Length());
if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
// Set the end time on the last range to highestEndTime by adding a
// new range spanning the current end time to highestEndTime, which
// Normalize() will then merge with the old last range.
ranges->Add(ranges->GetEndTime(), highestEndTime);
ranges->Normalize();
}
MSE_DEBUGV("SourceBuffer(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(ranges).get());
return ranges.forget();
}

View File

@ -21,7 +21,7 @@
#include "nsCycleCollectionNoteChild.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupports.h"
#include "nsStringGlue.h"
#include "nsString.h"
#include "nscore.h"
class JSObject;

View File

@ -13,7 +13,7 @@
#include "nsCOMPtr.h"
#include "nsIEventTarget.h"
#include "nsIRunnable.h"
#include "nsStringGlue.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "prlog.h"
@ -144,11 +144,11 @@ double
SourceBufferList::GetHighestBufferedEndTime()
{
MOZ_ASSERT(NS_IsMainThread());
double highestEnd = 0;
double highestEndTime = 0;
for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
highestEnd = std::max(highestEnd, mSourceBuffers[i]->GetBufferedEnd());
highestEndTime = std::max(highestEndTime, mSourceBuffers[i]->GetBufferedEnd());
}
return highestEnd;
return highestEndTime;
}
void

View File

@ -11,7 +11,6 @@
#include "nsISeekableStream.h"
#include "nsISupportsImpl.h"
#include "prenv.h"
#include "prlog.h"
#ifdef PR_LOGGING
@ -172,7 +171,7 @@ SourceBufferResource::AppendData(const uint8_t* aData, uint32_t aLength)
{
SBR_DEBUG("SourceBufferResource(%p)::AppendData(aData=%p, aLength=%u)", this, aData, aLength);
ReentrantMonitorAutoEnter mon(mMonitor);
mInputBuffer.PushBack(new ResourceItem(aData, aLength));
mInputBuffer.AppendItem(aData, aLength);
mon.NotifyAll();
}

View File

@ -7,17 +7,16 @@
#ifndef MOZILLA_SOURCEBUFFERRESOURCE_H_
#define MOZILLA_SOURCEBUFFERRESOURCE_H_
#include <algorithm>
#include "MediaCache.h"
#include "MediaResource.h"
#include "ResourceQueue.h"
#include "mozilla/Attributes.h"
#include "mozilla/ReentrantMonitor.h"
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsIPrincipal.h"
#include "nsStringGlue.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsDeque.h"
#include "nscore.h"
class nsIStreamListener;
@ -34,167 +33,9 @@ class SourceBuffer;
class SourceBufferResource MOZ_FINAL : public MediaResource
{
private:
// A SourceBufferResource has a queue containing the data
// that is appended to it. The queue holds instances of
// ResourceItem which is an array of the bytes. Appending
// data to the SourceBufferResource pushes this onto the
// queue. As items are played they are taken off the front
// of the queue.
// Data is evicted once it reaches a size threshold. This
// pops the items off the front of the queue and deletes it.
// If an eviction happens then the MediaSource is notified
// (done in SourceBuffer::AppendData) which then requests
// all SourceBuffers to evict data up to approximately
// the same timepoint.
struct ResourceItem {
ResourceItem(uint8_t const* aData, uint32_t aSize) {
mData.AppendElements(aData, aSize);
}
nsTArray<uint8_t> mData;
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
// size including this
size_t size = aMallocSizeOf(this);
// size excluding this
size += mData.SizeOfExcludingThis(aMallocSizeOf);
return size;
}
};
class ResourceQueueDeallocator : public nsDequeFunctor {
virtual void* operator() (void* aObject) {
delete static_cast<ResourceItem*>(aObject);
return nullptr;
}
};
class ResourceQueue : private nsDeque {
public:
ResourceQueue() :
nsDeque(new ResourceQueueDeallocator()),
mLogicalLength(0),
mOffset(0)
{
}
// Returns the logical byte offset of the start of the data.
inline uint64_t GetOffset() {
return mOffset;
}
// Returns the length of all items in the queue plus the offset.
// This is the logical length of the resource.
inline uint64_t GetLength() {
return mLogicalLength;
}
// Copies aCount bytes from aOffset in the queue into aDest.
inline void CopyData(uint64_t aOffset, uint32_t aCount, char* aDest) {
uint32_t offset = 0;
uint32_t start = GetAtOffset(aOffset, &offset);
uint32_t end = std::min(GetAtOffset(aOffset + aCount, nullptr) + 1, GetSize());
for (uint32_t i = start; i < end; ++i) {
ResourceItem* item = ResourceAt(i);
uint32_t bytes = std::min(aCount, uint32_t(item->mData.Length() - offset));
if (bytes != 0) {
memcpy(aDest, &item->mData[offset], bytes);
offset = 0;
aCount -= bytes;
aDest += bytes;
}
}
}
inline void PushBack(ResourceItem* aItem) {
mLogicalLength += aItem->mData.Length();
nsDeque::Push(aItem);
}
// Evict data in queue if the total queue size is greater than
// aThreshold past the offset. Returns true if some data was
// actually evicted.
inline bool Evict(uint64_t aOffset, uint32_t aThreshold) {
bool evicted = false;
while (GetLength() - mOffset > aThreshold) {
ResourceItem* item = ResourceAt(0);
if (item->mData.Length() + mOffset > aOffset) {
break;
}
mOffset += item->mData.Length();
delete PopFront();
evicted = true;
}
return evicted;
}
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
// Calculate the size of the internal deque.
size_t size = nsDeque::SizeOfExcludingThis(aMallocSizeOf);
// Sum the ResourceItems.
for (int32_t i = 0; i < nsDeque::GetSize(); ++i) {
const ResourceItem* item =
static_cast<const ResourceItem*>(nsDeque::ObjectAt(i));
size += item->SizeOfIncludingThis(aMallocSizeOf);
}
return size;
}
private:
// Returns the number of items in the queue
inline uint32_t GetSize() {
return nsDeque::GetSize();
}
inline ResourceItem* ResourceAt(uint32_t aIndex) {
return static_cast<ResourceItem*>(nsDeque::ObjectAt(aIndex));
}
// Returns the index of the resource that contains the given
// logical offset. aResourceOffset will contain the offset into
// the resource at the given index returned if it is not null. If
// no such resource exists, returns GetSize() and aOffset is
// untouched.
inline uint32_t GetAtOffset(uint64_t aOffset, uint32_t *aResourceOffset) {
MOZ_ASSERT(aOffset >= mOffset);
uint64_t offset = mOffset;
for (uint32_t i = 0; i < GetSize(); ++i) {
ResourceItem* item = ResourceAt(i);
// If the item contains the start of the offset we want to
// break out of the loop.
if (item->mData.Length() + offset > aOffset) {
if (aResourceOffset) {
*aResourceOffset = aOffset - offset;
}
return i;
}
offset += item->mData.Length();
}
return GetSize();
}
inline ResourceItem* PopFront() {
return static_cast<ResourceItem*>(nsDeque::PopFront());
}
// Logical length of the resource.
uint64_t mLogicalLength;
// Logical offset into the resource of the first element in the queue.
uint64_t mOffset;
};
public:
SourceBufferResource(nsIPrincipal* aPrincipal,
const nsACString& aType);
protected:
~SourceBufferResource();
public:
virtual nsresult Close() MOZ_OVERRIDE;
virtual void Suspend(bool aCloseImmediately) MOZ_OVERRIDE {}
virtual void Resume() MOZ_OVERRIDE {}
@ -219,7 +60,7 @@ public:
virtual int64_t Tell() MOZ_OVERRIDE { return mOffset; }
virtual void Pin() MOZ_OVERRIDE {}
virtual void Unpin() MOZ_OVERRIDE {}
virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { return 0; }
virtual double GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { *aIsReliable = false; return 0; }
virtual int64_t GetLength() MOZ_OVERRIDE { return mInputBuffer.GetLength(); }
virtual int64_t GetNextCachedData(int64_t aOffset) MOZ_OVERRIDE { return GetLength() == aOffset ? -1 : aOffset; }
virtual int64_t GetCachedDataEnd(int64_t aOffset) MOZ_OVERRIDE { return GetLength(); }
@ -272,6 +113,7 @@ public:
void EvictBefore(uint64_t aOffset);
private:
~SourceBufferResource();
nsresult SeekInternal(int64_t aOffset);
nsCOMPtr<nsIPrincipal> mPrincipal;
@ -283,7 +125,7 @@ private:
// data is available in mData.
mutable ReentrantMonitor mMonitor;
// The buffer holding resource data is a queue of ResourceItem's.
// The buffer holding resource data.
ResourceQueue mInputBuffer;
uint64_t mOffset;

View File

@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SubBufferDecoder.h"
#include "MediaSourceDecoder.h"
#include "AbstractMediaDecoder.h"
#include "MediaDecoderReader.h"
#include "mozilla/dom/TimeRanges.h"

View File

@ -27,7 +27,7 @@ class SubBufferDecoder : public BufferDecoder
public:
// This class holds a weak pointer to MediaResource. It's the responsibility
// of the caller to manage the memory of the MediaResource object.
SubBufferDecoder(MediaResource* aResource, MediaSourceDecoder* aParentDecoder)
SubBufferDecoder(MediaResource* aResource, AbstractMediaDecoder* aParentDecoder)
: BufferDecoder(aResource), mParentDecoder(aParentDecoder), mReader(nullptr)
, mMediaDuration(-1), mDiscarded(false)
{
@ -84,7 +84,7 @@ public:
bool ContainsTime(double aTime);
private:
MediaSourceDecoder* mParentDecoder;
AbstractMediaDecoder* mParentDecoder;
nsRefPtr<MediaDecoderReader> mReader;
int64_t mMediaDuration;
bool mDiscarded;

View File

@ -20,6 +20,7 @@ UNIFIED_SOURCES += [
'MediaSource.cpp',
'MediaSourceDecoder.cpp',
'MediaSourceReader.cpp',
'MediaSourceUtils.cpp',
'SourceBuffer.cpp',
'SourceBufferList.cpp',
'SourceBufferResource.cpp',

View File

@ -65,7 +65,6 @@ EXPORTS += [
'AbstractMediaDecoder.h',
'AudioChannelFormat.h',
'AudioCompactor.h',
'AudioEventTimeline.h',
'AudioMixer.h',
'AudioSampleFormat.h',
'AudioSegment.h',
@ -100,6 +99,7 @@ EXPORTS += [
'SharedBuffer.h',
'SharedThreadPool.h',
'StreamBuffer.h',
'ThreadPoolCOMListener.h',
'TimeVarying.h',
'TrackUnionStream.h',
'VideoFrameContainer.h',
@ -170,6 +170,9 @@ UNIFIED_SOURCES += [
'WebVTTListener.cpp',
]
if CONFIG['OS_TARGET'] == 'WINNT':
SOURCES += [ 'ThreadPoolCOMListener.cpp' ]
# DecoderTraits.cpp needs to be built separately because of Mac OS X headers.
# Latency.cpp needs to be built separately because it forces NSPR logging.
SOURCES += [

View File

@ -69,7 +69,6 @@ public:
ogg_packet* PopFront() { return static_cast<ogg_packet*>(nsDeque::PopFront()); }
ogg_packet* PeekFront() { return static_cast<ogg_packet*>(nsDeque::PeekFront()); }
void PushFront(ogg_packet* aPacket) { nsDeque::PushFront(aPacket); }
void PushBack(ogg_packet* aPacket) { nsDeque::PushFront(aPacket); }
void Erase() { nsDeque::Erase(); }
};

View File

@ -10,6 +10,7 @@
#include <stagefright/foundation/ABuffer.h>
#include <stagefright/foundation/ADebug.h>
#include <stagefright/MetaData.h>
#include "stagefright/MediaErrors.h"
#define LOG_TAG "MediaCodecProxy"
#include <android/log.h>
@ -428,13 +429,19 @@ status_t MediaCodecProxy::Input(const uint8_t* aData, uint32_t aDataSize,
ALOG("dequeueInputBuffer returned %d", err);
return err;
}
const sp<ABuffer> &dstBuffer = mInputBuffers.itemAt(index);
CHECK_LE(aDataSize, dstBuffer->capacity());
dstBuffer->setRange(0, aDataSize);
if (aData) {
const sp<ABuffer> &dstBuffer = mInputBuffers.itemAt(index);
CHECK_LE(aDataSize, dstBuffer->capacity());
dstBuffer->setRange(0, aDataSize);
memcpy(dstBuffer->data(), aData, aDataSize);
err = queueInputBuffer(index, 0, dstBuffer->size(), aTimestampUsecs, aflags);
} else {
err = queueInputBuffer(index, 0, 0, 0ll, MediaCodec::BUFFER_FLAG_EOS);
}
memcpy(dstBuffer->data(), aData, aDataSize);
err = queueInputBuffer(index, 0, dstBuffer->size(), aTimestampUsecs, aflags);
if (err != OK) {
ALOG("queueInputBuffer returned %d", err);
return err;
@ -473,6 +480,9 @@ status_t MediaCodecProxy::Output(MediaBuffer** aBuffer, int64_t aTimeoutUs)
metaData->setInt64(kKeyTime, timeUs);
buffer->set_range(buffer->range_offset(), size);
*aBuffer = buffer;
if (flags & MediaCodec::BUFFER_FLAG_EOS) {
return ERROR_END_OF_STREAM;
}
return err;
}

View File

@ -112,7 +112,7 @@ public:
// an input/output buffer has become available, a format change is
// pending, an error is pending.
void requestActivityNotification(const sp<AMessage> &aNotify);
// If aData is null, will notify decoder input EOS
status_t Input(const uint8_t* aData, uint32_t aDataSize,
int64_t aTimestampUsecs, uint64_t flags);
status_t Output(MediaBuffer** aBuffer, int64_t aTimeoutUs);

View File

@ -40,7 +40,8 @@ function getPref(name) {
var haveMp4 = (getPref("media.windows-media-foundation.enabled") && IsWindowsVistaOrLater()) ||
getPref("media.omx.enabled") ||
getPref("media.gstreamer.enabled");
getPref("media.gstreamer.enabled") ||
getPref("media.fragmented-mp4.exposed");
// TODO: Add "getPref("media.plugins.enabled")" once MP4 works on Gingerbread.
check_mp4(document.getElementById('v'), haveMp4);

Some files were not shown because too many files have changed in this diff Show More