mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 11:55:49 +00:00
Merge m-c to fx-team
This commit is contained in:
commit
3f9402bca3
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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 =
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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"/>
|
||||
|
@ -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 -->
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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 -->
|
||||
|
@ -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"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "ecfb8305745716f534e782c7b9df11aaa78b3823",
|
||||
"revision": "670b42b547f817727fc98f2983c606e8cc8766af",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -6,8 +6,8 @@
|
||||
"filename": "setup.sh"
|
||||
},
|
||||
{
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"size": 166407,
|
||||
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
"filename": "setup.sh"
|
||||
},
|
||||
{
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"size": 166407,
|
||||
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -15,8 +15,8 @@
|
||||
"filename": "clang.tar.bz2"
|
||||
},
|
||||
{
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"size": 166407,
|
||||
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -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"/>
|
||||
|
@ -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')
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -129,8 +129,6 @@ chatBrowserAccess.prototype = {
|
||||
},
|
||||
|
||||
isTabContentWindow: function (aWindow) this.contentWindow == aWindow,
|
||||
|
||||
get contentWindow() document.getElementById("chatter").contentWindow
|
||||
};
|
||||
|
||||
</script>
|
||||
|
@ -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();
|
||||
|
@ -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[
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -12,8 +12,8 @@
|
||||
"filename": "gcc.tar.xz"
|
||||
},
|
||||
{
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"size": 166407,
|
||||
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -12,8 +12,8 @@
|
||||
"filename": "gcc.tar.xz"
|
||||
},
|
||||
{
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"size": 166407,
|
||||
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -15,8 +15,8 @@
|
||||
"filename": "clang.tar.bz2"
|
||||
},
|
||||
{
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"size": 166407,
|
||||
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -12,8 +12,8 @@
|
||||
"filename": "setup.sh"
|
||||
},
|
||||
{
|
||||
"size": 165226,
|
||||
"digest": "79280f7595bc9e1613e05f8b2f0db3798ac739b96191e0f133e8ccd8ad149fedc84a1046e59863574189db28363a01712ae7b368ad1714e30ff88e7ebd5dad23",
|
||||
"size": 166407,
|
||||
"digest": "88fcc94f21818621e9e10107db913a3c787c6a68219c1e3e5fb26ebdf0864efdca4f05bd168d4851fee35c6b8d9ca4f9eb3ec229f565b7e6ce55ff6e7e899c24",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2"
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -1087,10 +1087,6 @@ nsBrowserAccess.prototype = {
|
||||
isTabContentWindow: function(aWindow) {
|
||||
return Browser.browsers.some(function (browser) browser.contentWindow == aWindow);
|
||||
},
|
||||
|
||||
get contentWindow() {
|
||||
return Browser.selectedBrowser.contentWindow;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
||||
|
251
browser/modules/FormSubmitObserver.jsm
Normal file
251
browser/modules/FormSubmitObserver.jsm
Normal 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])
|
||||
};
|
157
browser/modules/FormValidationHandler.jsm
Normal file
157
browser/modules/FormValidationHandler.jsm
Normal 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();
|
||||
}
|
||||
}
|
||||
};
|
@ -17,6 +17,8 @@ EXTRA_JS_MODULES += [
|
||||
'ContentSearch.jsm',
|
||||
'CustomizationTabPreloader.jsm',
|
||||
'Feeds.jsm',
|
||||
'FormSubmitObserver.jsm',
|
||||
'FormValidationHandler.jsm',
|
||||
'NetworkPrioritizer.jsm',
|
||||
'offlineAppCache.jsm',
|
||||
'PanelFrame.jsm',
|
||||
|
@ -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
|
||||
|
||||
|
||||
###
|
||||
|
@ -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)
|
||||
|
@ -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++
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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) {}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -188,6 +188,12 @@ BufferDecoder::NotifyWaitingForResourcesStatusChanged()
|
||||
// ignore
|
||||
}
|
||||
|
||||
void
|
||||
BufferDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
MediaDecoderOwner*
|
||||
BufferDecoder::GetOwner()
|
||||
{
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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; }
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
{
|
||||
|
30
content/media/ThreadPoolCOMListener.cpp
Normal file
30
content/media/ThreadPoolCOMListener.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
28
content/media/ThreadPoolCOMListener.h
Normal file
28
content/media/ThreadPoolCOMListener.h
Normal 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_
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.");
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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(); }
|
||||
|
36
content/media/mediasource/MediaSourceUtils.cpp
Normal file
36
content/media/mediasource/MediaSourceUtils.cpp
Normal 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
|
22
content/media/mediasource/MediaSourceUtils.h
Normal file
22
content/media/mediasource/MediaSourceUtils.h
Normal 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_ */
|
178
content/media/mediasource/ResourceQueue.h
Normal file
178
content/media/mediasource/ResourceQueue.h
Normal 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_ */
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "nsCycleCollectionNoteChild.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsString.h"
|
||||
#include "nscore.h"
|
||||
|
||||
class JSObject;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -20,6 +20,7 @@ UNIFIED_SOURCES += [
|
||||
'MediaSource.cpp',
|
||||
'MediaSourceDecoder.cpp',
|
||||
'MediaSourceReader.cpp',
|
||||
'MediaSourceUtils.cpp',
|
||||
'SourceBuffer.cpp',
|
||||
'SourceBufferList.cpp',
|
||||
'SourceBufferResource.cpp',
|
||||
|
@ -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 += [
|
||||
|
@ -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(); }
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user