mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 08:12:05 +00:00
Merge m-c to fx-team
This commit is contained in:
commit
c6d3789b06
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 1210154 - Update the clang toolchain
|
||||
Bug 1215696 - Update mp4parse to v0.1.1
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="4ace9aaee0e048dfda11bb787646c59982a3dc80"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="4ace9aaee0e048dfda11bb787646c59982a3dc80"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"git_revision": "a87f947366c2e044bd6336e1982419ac45378969",
|
||||
"git_revision": "3491a49da4d4ce45b8a682e95d0ce68892c2b8ea",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "6878d4020e3c1a3b020031cce83c4b437ad42669",
|
||||
"revision": "6458b8861154bf7e28971ce85d49e404ac96da29",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0c28789b9957913be975eb002a22323f93585d4c"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a87f947366c2e044bd6336e1982419ac45378969"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="3491a49da4d4ce45b8a682e95d0ce68892c2b8ea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c1bbb66f52f9e2d76ce97e7b3aa0cb29957cd7d8"/>
|
||||
|
@ -95,6 +95,18 @@ function handlePCRequest(aSubject, aTopic, aData) {
|
||||
let { windowID, innerWindowID, callID, isSecure } = aSubject;
|
||||
let contentWindow = Services.wm.getOuterWindowWithId(windowID);
|
||||
|
||||
let mm = getMessageManagerForWindow(contentWindow);
|
||||
if (!mm) {
|
||||
// Workaround for Bug 1207784. To use WebRTC, add-ons right now use
|
||||
// hiddenWindow.mozRTCPeerConnection which is only privileged on OSX. Other
|
||||
// platforms end up here without a message manager.
|
||||
// TODO: Remove once there's a better way (1215591).
|
||||
|
||||
// Skip permission check in the absence of a message manager.
|
||||
Services.obs.notifyObservers(null, "PeerConnection:response:allow", callID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!contentWindow.pendingPeerConnectionRequests) {
|
||||
setupPendingListsInitially(contentWindow);
|
||||
}
|
||||
@ -107,8 +119,6 @@ function handlePCRequest(aSubject, aTopic, aData) {
|
||||
documentURI: contentWindow.document.documentURI,
|
||||
secure: isSecure,
|
||||
};
|
||||
|
||||
let mm = getMessageManagerForWindow(contentWindow);
|
||||
mm.sendAsyncMessage("rtcpeer:Request", request);
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ browser.jar:
|
||||
skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
|
||||
#endif
|
||||
|
||||
../extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon16.png chrome://browser/skin/feeds/feedIcon16.png
|
||||
% override chrome://browser/skin/feeds/videoFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
|
@ -275,7 +275,7 @@ browser.jar:
|
||||
skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
|
||||
#endif
|
||||
|
||||
../extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon16.png chrome://browser/skin/feeds/feedIcon16.png
|
||||
% override chrome://browser/skin/feeds/videoFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
|
@ -285,7 +285,7 @@ browser.jar:
|
||||
skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
|
||||
#endif
|
||||
|
||||
../extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
|
||||
% override chrome://browser/skin/page-livemarks.png chrome://browser/skin/feeds/feedIcon16.png
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon16.png chrome://browser/skin/feeds/feedIcon16.png
|
||||
|
@ -31,6 +31,13 @@ To ship chrome files in a JAR, an indented line indicates a file to be packaged:
|
||||
<jarfile>.jar:
|
||||
path/in/jar/file_name.xul (source/tree/location/file_name.xul)
|
||||
|
||||
The JAR location may be preceded with a base path between square brackets::
|
||||
[base/path] <jarfile>.jar:
|
||||
path/in/jar/file_name.xul (source/tree/location/file_name.xul)
|
||||
|
||||
In this case, the jar will be directly located under the given ``base/bath``,
|
||||
while without a base path, it will be under a ``chrome`` directory.
|
||||
|
||||
If the JAR manifest and packaged file live in the same directory, the path and
|
||||
parenthesis can be omitted. In other words, the following two lines are
|
||||
equivalent::
|
||||
|
@ -70,4 +70,5 @@ if CONFIG['GNU_CXX']:
|
||||
# installing it in dist/lib.
|
||||
NO_EXPAND_LIBS = True
|
||||
|
||||
# We allow warnings for third-party code that can be updated from upstream.
|
||||
ALLOW_COMPILER_WARNINGS = True
|
||||
|
@ -1256,7 +1256,7 @@ endif
|
||||
|
||||
libs realchrome:: $(FINAL_TARGET)/chrome
|
||||
$(call py_action,jar_maker,\
|
||||
$(QUIET) -j $(FINAL_TARGET)/chrome \
|
||||
$(QUIET) -d $(FINAL_TARGET) \
|
||||
$(MAKE_JARS_FLAGS) $(DEFINES) $(ACDEFINES) $(MOZ_DEBUG_DEFINES) \
|
||||
$(JAR_MANIFEST))
|
||||
|
||||
|
@ -9,6 +9,7 @@ EXPORTS += [
|
||||
'sqlite3.h',
|
||||
]
|
||||
|
||||
# We allow warnings for third-party code that can be updated from upstream.
|
||||
ALLOW_COMPILER_WARNINGS = True
|
||||
|
||||
if CONFIG['MOZ_FOLD_LIBS']:
|
||||
|
@ -787,7 +787,7 @@ NetworkMonitor.prototype = {
|
||||
// associated with a load group. Bug 1160837 will hopefully introduce a
|
||||
// platform fix that will render the following code entirely useless.
|
||||
if (aChannel.loadInfo &&
|
||||
aChannel.loadInfo.contentPolicyType == Ci.nsIContentPolicy.TYPE_BEACON) {
|
||||
aChannel.loadInfo.externalContentPolicyType == Ci.nsIContentPolicy.TYPE_BEACON) {
|
||||
let nonE10sMatch = this.window &&
|
||||
aChannel.loadInfo.loadingDocument === this.window.document;
|
||||
let e10sMatch = this.topFrame &&
|
||||
@ -838,7 +838,7 @@ NetworkMonitor.prototype = {
|
||||
|
||||
// Determine if this is an XHR request.
|
||||
httpActivity.isXHR = event.isXHR =
|
||||
(aChannel.loadInfo.contentPolicyType === Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST);
|
||||
(aChannel.loadInfo.externalContentPolicyType === Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST);
|
||||
|
||||
// Determine the HTTP version.
|
||||
let httpVersionMaj = {};
|
||||
|
@ -14,13 +14,11 @@ function run_test() {
|
||||
}
|
||||
|
||||
// Make sure the queue has items in it...
|
||||
var queue = prefetch.enumerateQueue();
|
||||
do_check_true(queue.hasMoreElements());
|
||||
do_check_true(prefetch.hasMoreElements());
|
||||
|
||||
// Now disable the pref to force the queue to empty...
|
||||
prefs.setBoolPref("network.prefetch-next", false);
|
||||
queue = prefetch.enumerateQueue();
|
||||
do_check_false(queue.hasMoreElements());
|
||||
do_check_false(prefetch.hasMoreElements());
|
||||
|
||||
// Now reenable the pref, and add more items to the queue.
|
||||
prefs.setBoolPref("network.prefetch-next", true);
|
||||
@ -28,7 +26,5 @@ function run_test() {
|
||||
var uri = ios.newURI("http://localhost/" + i, null, null);
|
||||
prefetch.prefetchURI(uri, uri, null, true);
|
||||
}
|
||||
queue = prefetch.enumerateQueue();
|
||||
do_check_true(queue.hasMoreElements());
|
||||
do_check_true(prefetch.hasMoreElements());
|
||||
}
|
||||
|
||||
|
@ -1720,6 +1720,9 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
nsIDocument* document =
|
||||
HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc();
|
||||
|
||||
if (HasPointerLock()) {
|
||||
nsIDocument::UnlockPointer();
|
||||
}
|
||||
if (aNullParent) {
|
||||
if (IsFullScreenAncestor()) {
|
||||
// The element being removed is an ancestor of the full-screen element,
|
||||
@ -1731,9 +1734,6 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
// Fully exit full-screen.
|
||||
nsIDocument::ExitFullscreenInDocTree(OwnerDoc());
|
||||
}
|
||||
if (HasPointerLock()) {
|
||||
nsIDocument::UnlockPointer();
|
||||
}
|
||||
|
||||
if (GetParent() && GetParent()->IsInUncomposedDoc()) {
|
||||
// Update the editable descendant count in the ancestors before we
|
||||
|
@ -668,11 +668,6 @@ public:
|
||||
ErrorResult& aError);
|
||||
already_AddRefed<nsIHTMLCollection>
|
||||
GetElementsByClassName(const nsAString& aClassNames);
|
||||
bool MozMatchesSelector(const nsAString& aSelector,
|
||||
ErrorResult& aError)
|
||||
{
|
||||
return Matches(aSelector, aError);
|
||||
}
|
||||
void SetPointerCapture(int32_t aPointerId, ErrorResult& aError)
|
||||
{
|
||||
bool activeState = false;
|
||||
@ -1769,8 +1764,8 @@ NS_IMETHOD MozMatchesSelector(const nsAString& selector, \
|
||||
bool* _retval) final override \
|
||||
{ \
|
||||
mozilla::ErrorResult rv; \
|
||||
*_retval = Element::MozMatchesSelector(selector, rv); \
|
||||
return rv.StealNSResult(); \
|
||||
*_retval = Element::Matches(selector, rv); \
|
||||
return rv.StealNSResult(); \
|
||||
} \
|
||||
NS_IMETHOD SetCapture(bool retargetToElement) final override \
|
||||
{ \
|
||||
|
@ -418,5 +418,23 @@ URLSearchParams::NotifyObserver()
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
URLSearchParams::GetIterableLength() const
|
||||
{
|
||||
return mParams->Length();
|
||||
}
|
||||
|
||||
const nsAString&
|
||||
URLSearchParams::GetKeyAtIndex(uint32_t aIndex) const
|
||||
{
|
||||
return mParams->GetKeyAtIndex(aIndex);
|
||||
}
|
||||
|
||||
const nsAString&
|
||||
URLSearchParams::GetValueAtIndex(uint32_t aIndex) const
|
||||
{
|
||||
return mParams->GetValueAtIndex(aIndex);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -91,6 +91,23 @@ public:
|
||||
mParams.Clear();
|
||||
}
|
||||
|
||||
uint32_t Length() const
|
||||
{
|
||||
return mParams.Length();
|
||||
}
|
||||
|
||||
const nsAString& GetKeyAtIndex(uint32_t aIndex) const
|
||||
{
|
||||
MOZ_ASSERT(aIndex < mParams.Length());
|
||||
return mParams[aIndex].mKey;
|
||||
}
|
||||
|
||||
const nsAString& GetValueAtIndex(uint32_t aIndex) const
|
||||
{
|
||||
MOZ_ASSERT(aIndex < mParams.Length());
|
||||
return mParams[aIndex].mValue;
|
||||
}
|
||||
|
||||
private:
|
||||
void DecodeString(const nsACString& aInput, nsAString& aOutput);
|
||||
void ConvertString(const nsACString& aInput, nsAString& aOutput);
|
||||
@ -153,6 +170,10 @@ public:
|
||||
|
||||
void Delete(const nsAString& aName);
|
||||
|
||||
uint32_t GetIterableLength() const;
|
||||
const nsAString& GetKeyAtIndex(uint32_t aIndex) const;
|
||||
const nsAString& GetValueAtIndex(uint32_t aIndex) const;
|
||||
|
||||
void Stringify(nsString& aRetval) const
|
||||
{
|
||||
Serialize(aRetval);
|
||||
|
@ -3196,7 +3196,7 @@ convertSheetType(uint32_t aSheetType)
|
||||
default:
|
||||
NS_ASSERTION(false, "wrong type");
|
||||
// we must return something although this should never happen
|
||||
return nsIDocument::SheetTypeCount;
|
||||
return nsIDocument::AdditionalSheetTypeCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2413,7 +2413,7 @@ nsDocument::RemoveDocStyleSheetsFromStyleSets()
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, nsStyleSet::sheetType aType)
|
||||
nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, SheetType aType)
|
||||
{
|
||||
// The stylesheets should forget us
|
||||
int32_t indx = aSheets.Count();
|
||||
@ -2440,16 +2440,17 @@ nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
|
||||
|
||||
mozAutoDocUpdate upd(this, UPDATE_STYLE, true);
|
||||
RemoveDocStyleSheetsFromStyleSets();
|
||||
RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, nsStyleSet::eAgentSheet);
|
||||
RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet);
|
||||
RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet);
|
||||
RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet);
|
||||
RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, SheetType::Agent);
|
||||
RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], SheetType::Agent);
|
||||
RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], SheetType::User);
|
||||
RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], SheetType::Doc);
|
||||
|
||||
// Release all the sheets
|
||||
mStyleSheets.Clear();
|
||||
mOnDemandBuiltInUASheets.Clear();
|
||||
for (uint32_t i = 0; i < SheetTypeCount; ++i)
|
||||
mAdditionalSheets[i].Clear();
|
||||
for (auto& sheets : mAdditionalSheets) {
|
||||
sheets.Clear();
|
||||
}
|
||||
|
||||
// NOTE: We don't release the catalog sheets. It doesn't really matter
|
||||
// now, but it could in the future -- in which case not releasing them
|
||||
@ -2483,14 +2484,14 @@ static bool
|
||||
AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData)
|
||||
{
|
||||
nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData);
|
||||
styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet);
|
||||
styleSet->AppendStyleSheet(SheetType::Doc, aSheet);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
AppendSheetsToStyleSet(nsStyleSet* aStyleSet,
|
||||
const nsCOMArray<nsIStyleSheet>& aSheets,
|
||||
nsStyleSet::sheetType aType)
|
||||
SheetType aType)
|
||||
{
|
||||
for (int32_t i = aSheets.Count() - 1; i >= 0; --i) {
|
||||
aStyleSet->AppendStyleSheet(aType, aSheets[i]);
|
||||
@ -2502,13 +2503,13 @@ void
|
||||
nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
|
||||
{
|
||||
NS_PRECONDITION(aStyleSet, "Must have a style set");
|
||||
NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0,
|
||||
NS_PRECONDITION(aStyleSet->SheetCount(SheetType::Doc) == 0,
|
||||
"Style set already has document sheets?");
|
||||
|
||||
// We could consider moving this to nsStyleSet::Init, to match its
|
||||
// handling of the eAnimationSheet and eTransitionSheet levels.
|
||||
aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet);
|
||||
aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet);
|
||||
aStyleSet->DirtyRuleProcessors(SheetType::PresHint);
|
||||
aStyleSet->DirtyRuleProcessors(SheetType::StyleAttr);
|
||||
|
||||
int32_t i;
|
||||
for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
|
||||
@ -2528,16 +2529,16 @@ nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
|
||||
for (i = mOnDemandBuiltInUASheets.Count() - 1; i >= 0; --i) {
|
||||
nsIStyleSheet* sheet = mOnDemandBuiltInUASheets[i];
|
||||
if (sheet->IsApplicable()) {
|
||||
aStyleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
|
||||
aStyleSet->PrependStyleSheet(SheetType::Agent, sheet);
|
||||
}
|
||||
}
|
||||
|
||||
AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
|
||||
nsStyleSet::eAgentSheet);
|
||||
SheetType::Agent);
|
||||
AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
|
||||
nsStyleSet::eUserSheet);
|
||||
SheetType::User);
|
||||
AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
|
||||
nsStyleSet::eDocSheet);
|
||||
SheetType::Doc);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -4141,7 +4142,7 @@ nsDocument::AddOnDemandBuiltInUASheet(CSSStyleSheet* aSheet)
|
||||
// do not override Firefox OS/Mobile's content.css sheet. Maybe we should
|
||||
// have an insertion point to match the order of
|
||||
// nsDocumentViewer::CreateStyleSet though?
|
||||
shell->StyleSet()->PrependStyleSheet(nsStyleSet::eAgentSheet, aSheet);
|
||||
shell->StyleSet()->PrependStyleSheet(SheetType::Agent, aSheet);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4373,20 +4374,20 @@ nsDocument::NotifyStyleSheetApplicableStateChanged()
|
||||
}
|
||||
}
|
||||
|
||||
static nsStyleSet::sheetType
|
||||
static SheetType
|
||||
ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType)
|
||||
{
|
||||
switch(aType) {
|
||||
case nsIDocument::eAgentSheet:
|
||||
return nsStyleSet::eAgentSheet;
|
||||
return SheetType::Agent;
|
||||
case nsIDocument::eUserSheet:
|
||||
return nsStyleSet::eUserSheet;
|
||||
return SheetType::User;
|
||||
case nsIDocument::eAuthorSheet:
|
||||
return nsStyleSet::eDocSheet;
|
||||
return SheetType::Doc;
|
||||
default:
|
||||
NS_ASSERTION(false, "wrong type");
|
||||
MOZ_ASSERT(false, "wrong type");
|
||||
// we must return something although this should never happen
|
||||
return nsStyleSet::eSheetTypeCount;
|
||||
return SheetType::Count;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4460,7 +4461,7 @@ nsDocument::AddAdditionalStyleSheet(additionalSheetType aType, nsIStyleSheet* aS
|
||||
BeginUpdate(UPDATE_STYLE);
|
||||
nsCOMPtr<nsIPresShell> shell = GetShell();
|
||||
if (shell) {
|
||||
nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
|
||||
SheetType type = ConvertAdditionalSheetType(aType);
|
||||
shell->StyleSet()->AppendStyleSheet(type, aSheet);
|
||||
}
|
||||
|
||||
@ -4488,7 +4489,7 @@ nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheet
|
||||
MOZ_ASSERT(sheetRef->IsApplicable());
|
||||
nsCOMPtr<nsIPresShell> shell = GetShell();
|
||||
if (shell) {
|
||||
nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
|
||||
SheetType type = ConvertAdditionalSheetType(aType);
|
||||
shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
|
||||
}
|
||||
}
|
||||
|
@ -1516,7 +1516,7 @@ protected:
|
||||
|
||||
void RemoveDocStyleSheetsFromStyleSets();
|
||||
void RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets,
|
||||
nsStyleSet::sheetType aType);
|
||||
mozilla::SheetType aType);
|
||||
void ResetStylesheetsToURI(nsIURI* aURI);
|
||||
void FillStyleSet(nsStyleSet* aStyleSet);
|
||||
|
||||
@ -1571,7 +1571,7 @@ protected:
|
||||
|
||||
nsCOMArray<nsIStyleSheet> mStyleSheets;
|
||||
nsCOMArray<nsIStyleSheet> mOnDemandBuiltInUASheets;
|
||||
nsCOMArray<nsIStyleSheet> mAdditionalSheets[SheetTypeCount];
|
||||
nsCOMArray<nsIStyleSheet> mAdditionalSheets[AdditionalSheetTypeCount];
|
||||
|
||||
// Array of observers
|
||||
nsTObserverArray<nsIDocumentObserver*> mObservers;
|
||||
|
@ -4961,7 +4961,7 @@ nsGlobalWindow::GetInnerWidth(int32_t* aInnerWidth)
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetInnerWidthOuter(int32_t aInnerWidth, ErrorResult& aError)
|
||||
nsGlobalWindow::SetInnerWidthOuter(int32_t aInnerWidth, ErrorResult& aError, bool aCallerIsChrome)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsOuterWindow());
|
||||
|
||||
@ -4970,7 +4970,7 @@ nsGlobalWindow::SetInnerWidthOuter(int32_t aInnerWidth, ErrorResult& aError)
|
||||
return;
|
||||
}
|
||||
|
||||
CheckSecurityWidthAndHeight(&aInnerWidth, nullptr);
|
||||
CheckSecurityWidthAndHeight(&aInnerWidth, nullptr, aCallerIsChrome);
|
||||
|
||||
RefPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
|
||||
|
||||
@ -4999,7 +4999,7 @@ nsGlobalWindow::SetInnerWidthOuter(int32_t aInnerWidth, ErrorResult& aError)
|
||||
void
|
||||
nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth, ErrorResult& aError)
|
||||
{
|
||||
FORWARD_TO_OUTER_OR_THROW(SetInnerWidthOuter, (aInnerWidth, aError), aError, );
|
||||
FORWARD_TO_OUTER_OR_THROW(SetInnerWidthOuter, (aInnerWidth, aError, nsContentUtils::IsCallerChrome()), aError, );
|
||||
}
|
||||
|
||||
void
|
||||
@ -5013,14 +5013,14 @@ nsGlobalWindow::SetInnerWidth(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::SetInnerWidth(int32_t aInnerWidth)
|
||||
{
|
||||
FORWARD_TO_INNER(SetInnerWidth, (aInnerWidth), NS_ERROR_UNEXPECTED);
|
||||
FORWARD_TO_OUTER(SetInnerWidth, (aInnerWidth), NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (IsFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
SetInnerWidth(aInnerWidth, rv);
|
||||
SetInnerWidthOuter(aInnerWidth, rv, /* aCallerIsChrome = */ true);
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
@ -5062,7 +5062,7 @@ nsGlobalWindow::GetInnerHeight(int32_t* aInnerHeight)
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetInnerHeightOuter(int32_t aInnerHeight, ErrorResult& aError)
|
||||
nsGlobalWindow::SetInnerHeightOuter(int32_t aInnerHeight, ErrorResult& aError, bool aCallerIsChrome)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsOuterWindow());
|
||||
|
||||
@ -5081,7 +5081,7 @@ nsGlobalWindow::SetInnerHeightOuter(int32_t aInnerHeight, ErrorResult& aError)
|
||||
nsRect shellArea = presContext->GetVisibleArea();
|
||||
nscoord height = aInnerHeight;
|
||||
nscoord width = shellArea.width;
|
||||
CheckSecurityWidthAndHeight(nullptr, &height);
|
||||
CheckSecurityWidthAndHeight(nullptr, &height, aCallerIsChrome);
|
||||
SetCSSViewportWidthAndHeight(width,
|
||||
nsPresContext::CSSPixelsToAppUnits(height));
|
||||
return;
|
||||
@ -5092,14 +5092,14 @@ nsGlobalWindow::SetInnerHeightOuter(int32_t aInnerHeight, ErrorResult& aError)
|
||||
|
||||
nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell));
|
||||
docShellAsWin->GetSize(&width, &height);
|
||||
CheckSecurityWidthAndHeight(nullptr, &aInnerHeight);
|
||||
CheckSecurityWidthAndHeight(nullptr, &aInnerHeight, aCallerIsChrome);
|
||||
aError = SetDocShellWidthAndHeight(width, CSSToDevIntPixels(aInnerHeight));
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight, ErrorResult& aError)
|
||||
{
|
||||
FORWARD_TO_OUTER_OR_THROW(SetInnerHeightOuter, (aInnerHeight, aError), aError, );
|
||||
FORWARD_TO_OUTER_OR_THROW(SetInnerHeightOuter, (aInnerHeight, aError, nsContentUtils::IsCallerChrome()), aError, );
|
||||
}
|
||||
|
||||
void
|
||||
@ -5113,14 +5113,14 @@ nsGlobalWindow::SetInnerHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::SetInnerHeight(int32_t aInnerHeight)
|
||||
{
|
||||
FORWARD_TO_INNER(SetInnerHeight, (aInnerHeight), NS_ERROR_UNEXPECTED);
|
||||
FORWARD_TO_OUTER(SetInnerHeight, (aInnerHeight), NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (IsFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
SetInnerHeight(aInnerHeight, rv);
|
||||
SetInnerHeightOuter(aInnerHeight, rv, /* aCallerIsChrome = */ true);
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
@ -5225,7 +5225,7 @@ nsGlobalWindow::GetOuterHeight(int32_t* aOuterHeight)
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
|
||||
ErrorResult& aError)
|
||||
ErrorResult& aError, bool aCallerIsChrome)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
@ -5236,7 +5236,8 @@ nsGlobalWindow::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
|
||||
}
|
||||
|
||||
CheckSecurityWidthAndHeight(aIsWidth ? &aLengthCSSPixels : nullptr,
|
||||
aIsWidth ? nullptr : &aLengthCSSPixels);
|
||||
aIsWidth ? nullptr : &aLengthCSSPixels,
|
||||
aCallerIsChrome);
|
||||
|
||||
int32_t width, height;
|
||||
aError = treeOwnerAsWin->GetSize(&width, &height);
|
||||
@ -5254,17 +5255,17 @@ nsGlobalWindow::SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetOuterWidthOuter(int32_t aOuterWidth, ErrorResult& aError)
|
||||
nsGlobalWindow::SetOuterWidthOuter(int32_t aOuterWidth, ErrorResult& aError, bool aCallerIsChrome)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsOuterWindow());
|
||||
|
||||
SetOuterSize(aOuterWidth, true, aError);
|
||||
SetOuterSize(aOuterWidth, true, aError, aCallerIsChrome);
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth, ErrorResult& aError)
|
||||
{
|
||||
FORWARD_TO_OUTER_OR_THROW(SetOuterWidthOuter, (aOuterWidth, aError), aError, );
|
||||
FORWARD_TO_OUTER_OR_THROW(SetOuterWidthOuter, (aOuterWidth, aError, nsContentUtils::IsCallerChrome()), aError, );
|
||||
}
|
||||
|
||||
void
|
||||
@ -5278,30 +5279,30 @@ nsGlobalWindow::SetOuterWidth(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::SetOuterWidth(int32_t aOuterWidth)
|
||||
{
|
||||
FORWARD_TO_INNER(SetOuterWidth, (aOuterWidth), NS_ERROR_UNEXPECTED);
|
||||
FORWARD_TO_OUTER(SetOuterWidth, (aOuterWidth), NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (IsFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
SetOuterWidth(aOuterWidth, rv);
|
||||
SetOuterWidthOuter(aOuterWidth, rv, /* aCallerIsChrome = */ true);
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetOuterHeightOuter(int32_t aOuterHeight, ErrorResult& aError)
|
||||
nsGlobalWindow::SetOuterHeightOuter(int32_t aOuterHeight, ErrorResult& aError, bool aCallerIsChrome)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsOuterWindow());
|
||||
|
||||
SetOuterSize(aOuterHeight, false, aError);
|
||||
SetOuterSize(aOuterHeight, false, aError, aCallerIsChrome);
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight, ErrorResult& aError)
|
||||
{
|
||||
FORWARD_TO_OUTER_OR_THROW(SetOuterHeightOuter, (aOuterHeight, aError), aError, );
|
||||
FORWARD_TO_OUTER_OR_THROW(SetOuterHeightOuter, (aOuterHeight, aError, nsContentUtils::IsCallerChrome()), aError, );
|
||||
}
|
||||
|
||||
void
|
||||
@ -5315,14 +5316,14 @@ nsGlobalWindow::SetOuterHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::SetOuterHeight(int32_t aOuterHeight)
|
||||
{
|
||||
FORWARD_TO_INNER(SetOuterHeight, (aOuterHeight), NS_ERROR_UNEXPECTED);
|
||||
FORWARD_TO_OUTER(SetOuterHeight, (aOuterHeight), NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (IsFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
SetOuterHeight(aOuterHeight, rv);
|
||||
SetOuterHeightOuter(aOuterHeight, rv, /* aCallerIsChrome = */ true);
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
@ -5644,7 +5645,7 @@ nsGlobalWindow::MatchMedia(const nsAString& aMediaQueryList,
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetScreenXOuter(int32_t aScreenX, ErrorResult& aError)
|
||||
nsGlobalWindow::SetScreenXOuter(int32_t aScreenX, ErrorResult& aError, bool aCallerIsChrome)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsOuterWindow());
|
||||
|
||||
@ -5660,7 +5661,7 @@ nsGlobalWindow::SetScreenXOuter(int32_t aScreenX, ErrorResult& aError)
|
||||
return;
|
||||
}
|
||||
|
||||
CheckSecurityLeftAndTop(&aScreenX, nullptr);
|
||||
CheckSecurityLeftAndTop(&aScreenX, nullptr, aCallerIsChrome);
|
||||
x = CSSToDevIntPixels(aScreenX);
|
||||
|
||||
aError = treeOwnerAsWin->SetPosition(x, y);
|
||||
@ -5669,7 +5670,7 @@ nsGlobalWindow::SetScreenXOuter(int32_t aScreenX, ErrorResult& aError)
|
||||
void
|
||||
nsGlobalWindow::SetScreenX(int32_t aScreenX, ErrorResult& aError)
|
||||
{
|
||||
FORWARD_TO_OUTER_OR_THROW(SetScreenXOuter, (aScreenX, aError), aError, );
|
||||
FORWARD_TO_OUTER_OR_THROW(SetScreenXOuter, (aScreenX, aError, nsContentUtils::IsCallerChrome()), aError, );
|
||||
}
|
||||
|
||||
void
|
||||
@ -5683,14 +5684,14 @@ nsGlobalWindow::SetScreenX(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::SetScreenX(int32_t aScreenX)
|
||||
{
|
||||
FORWARD_TO_INNER(SetScreenX, (aScreenX), NS_ERROR_UNEXPECTED);
|
||||
FORWARD_TO_OUTER(SetScreenX, (aScreenX), NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (IsFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
SetScreenX(aScreenX, rv);
|
||||
SetScreenXOuter(aScreenX, rv, /* aCallerIsChrome = */ true);
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
@ -5730,7 +5731,7 @@ nsGlobalWindow::GetScreenY(JSContext* aCx,
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SetScreenYOuter(int32_t aScreenY, ErrorResult& aError)
|
||||
nsGlobalWindow::SetScreenYOuter(int32_t aScreenY, ErrorResult& aError, bool aCallerIsChrome)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsOuterWindow());
|
||||
|
||||
@ -5746,7 +5747,7 @@ nsGlobalWindow::SetScreenYOuter(int32_t aScreenY, ErrorResult& aError)
|
||||
return;
|
||||
}
|
||||
|
||||
CheckSecurityLeftAndTop(nullptr, &aScreenY);
|
||||
CheckSecurityLeftAndTop(nullptr, &aScreenY, aCallerIsChrome);
|
||||
y = CSSToDevIntPixels(aScreenY);
|
||||
|
||||
aError = treeOwnerAsWin->SetPosition(x, y);
|
||||
@ -5755,7 +5756,7 @@ nsGlobalWindow::SetScreenYOuter(int32_t aScreenY, ErrorResult& aError)
|
||||
void
|
||||
nsGlobalWindow::SetScreenY(int32_t aScreenY, ErrorResult& aError)
|
||||
{
|
||||
FORWARD_TO_OUTER_OR_THROW(SetScreenYOuter, (aScreenY, aError), aError, );
|
||||
FORWARD_TO_OUTER_OR_THROW(SetScreenYOuter, (aScreenY, aError, nsContentUtils::IsCallerChrome()), aError, );
|
||||
}
|
||||
|
||||
void
|
||||
@ -5769,14 +5770,14 @@ nsGlobalWindow::SetScreenY(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::SetScreenY(int32_t aScreenY)
|
||||
{
|
||||
FORWARD_TO_INNER(SetScreenY, (aScreenY), NS_ERROR_UNEXPECTED);
|
||||
FORWARD_TO_OUTER(SetScreenY, (aScreenY), NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (IsFrame()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
SetScreenY(aScreenY, rv);
|
||||
SetScreenYOuter(aScreenY, rv, /* aCallerIsChrome = */ true);
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
@ -5784,12 +5785,12 @@ nsGlobalWindow::SetScreenY(int32_t aScreenY)
|
||||
// NOTE: Arguments to this function should have values scaled to
|
||||
// CSS pixels, not device pixels.
|
||||
void
|
||||
nsGlobalWindow::CheckSecurityWidthAndHeight(int32_t* aWidth, int32_t* aHeight)
|
||||
nsGlobalWindow::CheckSecurityWidthAndHeight(int32_t* aWidth, int32_t* aHeight, bool aCallerIsChrome)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
if (!nsContentUtils::IsCallerChrome()) {
|
||||
if (!aCallerIsChrome) {
|
||||
// if attempting to resize the window, hide any open popups
|
||||
nsContentUtils::HidePopupsInDocument(mDoc);
|
||||
}
|
||||
@ -5848,7 +5849,7 @@ nsGlobalWindow::SetCSSViewportWidthAndHeight(nscoord aInnerWidth, nscoord aInner
|
||||
// NOTE: Arguments to this function should have values scaled to
|
||||
// CSS pixels, not device pixels.
|
||||
void
|
||||
nsGlobalWindow::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop)
|
||||
nsGlobalWindow::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop, bool aCallerIsChrome)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
@ -5856,7 +5857,7 @@ nsGlobalWindow::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop)
|
||||
|
||||
// Check security state for use in determing window dimensions
|
||||
|
||||
if (!nsContentUtils::IsCallerChrome()) {
|
||||
if (!aCallerIsChrome) {
|
||||
#ifdef MOZ_XUL
|
||||
// if attempting to move the window, hide any open popups
|
||||
nsContentUtils::HidePopupsInDocument(mDoc);
|
||||
@ -7623,7 +7624,7 @@ nsGlobalWindow::MoveToOuter(int32_t aXPos, int32_t aYPos, ErrorResult& aError, b
|
||||
|
||||
// Mild abuse of a "size" object so we don't need more helper functions.
|
||||
nsIntSize cssPos(aXPos, aYPos);
|
||||
CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height);
|
||||
CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height, aCallerIsChrome);
|
||||
|
||||
nsIntSize devPos = CSSToDevIntPixels(cssPos);
|
||||
|
||||
@ -7683,7 +7684,7 @@ nsGlobalWindow::MoveByOuter(int32_t aXDif, int32_t aYDif, ErrorResult& aError, b
|
||||
cssPos.width += aXDif;
|
||||
cssPos.height += aYDif;
|
||||
|
||||
CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height);
|
||||
CheckSecurityLeftAndTop(&cssPos.width, &cssPos.height, aCallerIsChrome);
|
||||
|
||||
nsIntSize newDevPos(CSSToDevIntPixels(cssPos));
|
||||
|
||||
@ -7741,7 +7742,7 @@ nsGlobalWindow::ResizeToOuter(int32_t aWidth, int32_t aHeight, ErrorResult& aErr
|
||||
}
|
||||
|
||||
nsIntSize cssSize(aWidth, aHeight);
|
||||
CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
|
||||
CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerIsChrome);
|
||||
|
||||
nsIntSize devSz(CSSToDevIntPixels(cssSize));
|
||||
|
||||
@ -7821,7 +7822,7 @@ nsGlobalWindow::ResizeByOuter(int32_t aWidthDif, int32_t aHeightDif,
|
||||
cssSize.width += aWidthDif;
|
||||
cssSize.height += aHeightDif;
|
||||
|
||||
CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
|
||||
CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerIsChrome);
|
||||
|
||||
nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
|
||||
|
||||
@ -7888,7 +7889,7 @@ nsGlobalWindow::SizeToContentOuter(ErrorResult& aError, bool aCallerIsChrome)
|
||||
}
|
||||
|
||||
nsIntSize cssSize(DevToCSSIntPixels(nsIntSize(width, height)));
|
||||
CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height);
|
||||
CheckSecurityWidthAndHeight(&cssSize.width, &cssSize.height, aCallerIsChrome);
|
||||
|
||||
nsIntSize newDevSize(CSSToDevIntPixels(cssSize));
|
||||
|
||||
|
@ -1207,27 +1207,27 @@ protected:
|
||||
// And the implementations of WindowCoordGetter/WindowCoordSetter.
|
||||
int32_t GetInnerWidthOuter(mozilla::ErrorResult& aError);
|
||||
int32_t GetInnerWidth(mozilla::ErrorResult& aError);
|
||||
void SetInnerWidthOuter(int32_t aInnerWidth, mozilla::ErrorResult& aError);
|
||||
void SetInnerWidthOuter(int32_t aInnerWidth, mozilla::ErrorResult& aError, bool aCallerIsChrome);
|
||||
void SetInnerWidth(int32_t aInnerWidth, mozilla::ErrorResult& aError);
|
||||
int32_t GetInnerHeightOuter(mozilla::ErrorResult& aError);
|
||||
int32_t GetInnerHeight(mozilla::ErrorResult& aError);
|
||||
void SetInnerHeightOuter(int32_t aInnerHeight, mozilla::ErrorResult& aError);
|
||||
void SetInnerHeightOuter(int32_t aInnerHeight, mozilla::ErrorResult& aError, bool aCallerIsChrome);
|
||||
void SetInnerHeight(int32_t aInnerHeight, mozilla::ErrorResult& aError);
|
||||
int32_t GetScreenXOuter(mozilla::ErrorResult& aError);
|
||||
int32_t GetScreenX(mozilla::ErrorResult& aError);
|
||||
void SetScreenXOuter(int32_t aScreenX, mozilla::ErrorResult& aError);
|
||||
void SetScreenXOuter(int32_t aScreenX, mozilla::ErrorResult& aError, bool aCallerIsChrome);
|
||||
void SetScreenX(int32_t aScreenX, mozilla::ErrorResult& aError);
|
||||
int32_t GetScreenYOuter(mozilla::ErrorResult& aError);
|
||||
int32_t GetScreenY(mozilla::ErrorResult& aError);
|
||||
void SetScreenYOuter(int32_t aScreenY, mozilla::ErrorResult& aError);
|
||||
void SetScreenYOuter(int32_t aScreenY, mozilla::ErrorResult& aError, bool aCallerIsChrome);
|
||||
void SetScreenY(int32_t aScreenY, mozilla::ErrorResult& aError);
|
||||
int32_t GetOuterWidthOuter(mozilla::ErrorResult& aError);
|
||||
int32_t GetOuterWidth(mozilla::ErrorResult& aError);
|
||||
void SetOuterWidthOuter(int32_t aOuterWidth, mozilla::ErrorResult& aError);
|
||||
void SetOuterWidthOuter(int32_t aOuterWidth, mozilla::ErrorResult& aError, bool aCallerIsChrome);
|
||||
void SetOuterWidth(int32_t aOuterWidth, mozilla::ErrorResult& aError);
|
||||
int32_t GetOuterHeightOuter(mozilla::ErrorResult& aError);
|
||||
int32_t GetOuterHeight(mozilla::ErrorResult& aError);
|
||||
void SetOuterHeightOuter(int32_t aOuterHeight, mozilla::ErrorResult& aError);
|
||||
void SetOuterHeightOuter(int32_t aOuterHeight, mozilla::ErrorResult& aError, bool aCallerIsChrome);
|
||||
void SetOuterHeight(int32_t aOuterHeight, mozilla::ErrorResult& aError);
|
||||
|
||||
// Array of idle observers that are notified of idle events.
|
||||
@ -1457,8 +1457,8 @@ public:
|
||||
|
||||
// Outer windows only.
|
||||
void EnsureReflowFlushAndPaint();
|
||||
void CheckSecurityWidthAndHeight(int32_t* width, int32_t* height);
|
||||
void CheckSecurityLeftAndTop(int32_t* left, int32_t* top);
|
||||
void CheckSecurityWidthAndHeight(int32_t* width, int32_t* height, bool aCallerIsChrome);
|
||||
void CheckSecurityLeftAndTop(int32_t* left, int32_t* top, bool aCallerIsChrome);
|
||||
|
||||
// Outer windows only.
|
||||
// Arguments to this function should have values in app units
|
||||
@ -1486,7 +1486,7 @@ public:
|
||||
nsresult GetInnerSize(mozilla::CSSIntSize& aSize);
|
||||
nsIntSize GetOuterSize(mozilla::ErrorResult& aError);
|
||||
void SetOuterSize(int32_t aLengthCSSPixels, bool aIsWidth,
|
||||
mozilla::ErrorResult& aError);
|
||||
mozilla::ErrorResult& aError, bool aCallerIsChrome);
|
||||
nsRect GetInnerScreenRect();
|
||||
|
||||
void ScrollTo(const mozilla::CSSIntPoint& aScroll,
|
||||
|
@ -571,11 +571,14 @@ nsHostObjectProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
nsAutoString contentType;
|
||||
blob->GetType(contentType);
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
|
||||
uri,
|
||||
stream,
|
||||
EmptyCString(), // aContentType
|
||||
NS_ConvertUTF16toUTF8(contentType),
|
||||
EmptyCString(), // aContentCharset
|
||||
aLoadInfo);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
|
@ -944,7 +944,7 @@ public:
|
||||
eAgentSheet,
|
||||
eUserSheet,
|
||||
eAuthorSheet,
|
||||
SheetTypeCount
|
||||
AdditionalSheetTypeCount
|
||||
};
|
||||
|
||||
virtual nsresult LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) = 0;
|
||||
|
@ -275,30 +275,41 @@ nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class ContextMediator : public nsIStreamLoaderObserver
|
||||
{
|
||||
public:
|
||||
explicit ContextMediator(nsScriptLoader *aScriptLoader, nsISupports *aContext)
|
||||
: mScriptLoader(aScriptLoader)
|
||||
, mContext(aContext) {}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLOADEROBSERVER
|
||||
|
||||
private:
|
||||
virtual ~ContextMediator() {}
|
||||
RefPtr<nsScriptLoader> mScriptLoader;
|
||||
nsCOMPtr<nsISupports> mContext;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ContextMediator, nsIStreamLoaderObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContextMediator::OnStreamComplete(nsIStreamLoader* aLoader,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatus,
|
||||
uint32_t aStringLen,
|
||||
const uint8_t* aString)
|
||||
{
|
||||
// pass arguments through except for the aContext,
|
||||
// we have to mediate and use mContext instead.
|
||||
return mScriptLoader->OnStreamComplete(aLoader, mContext, aStatus,
|
||||
aStringLen, aString);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
|
||||
bool aScriptFromHead)
|
||||
{
|
||||
nsISupports *context = aRequest->mElement.get()
|
||||
? static_cast<nsISupports *>(aRequest->mElement.get())
|
||||
: static_cast<nsISupports *>(mDocument);
|
||||
nsresult rv = ShouldLoadScript(mDocument, context, aRequest->mURI, aType, aRequest->IsPreload());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->MasterDocument()->GetWindow()));
|
||||
|
||||
if (!window) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
nsIDocShell *docshell = window->GetDocShell();
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
|
||||
|
||||
// If this document is sandboxed without 'allow-scripts', abort.
|
||||
if (mDocument->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
|
||||
return NS_OK;
|
||||
@ -307,17 +318,39 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
|
||||
nsContentPolicyType contentPolicyType = aRequest->IsPreload()
|
||||
? nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD
|
||||
: nsIContentPolicy::TYPE_INTERNAL_SCRIPT;
|
||||
nsCOMPtr<nsINode> context;
|
||||
if (aRequest->mElement) {
|
||||
context = do_QueryInterface(aRequest->mElement);
|
||||
}
|
||||
else {
|
||||
context = mDocument;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
|
||||
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->MasterDocument()->GetWindow()));
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_NULL_POINTER);
|
||||
nsIDocShell *docshell = window->GetDocShell();
|
||||
nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
|
||||
|
||||
nsSecurityFlags securityFlags =
|
||||
aRequest->mCORSMode == CORS_NONE
|
||||
? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL
|
||||
: nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
|
||||
if (aRequest->mCORSMode == CORS_USE_CREDENTIALS) {
|
||||
securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
|
||||
}
|
||||
securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
||||
aRequest->mURI,
|
||||
mDocument,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
contentPolicyType,
|
||||
loadGroup,
|
||||
prompter,
|
||||
nsIRequest::LOAD_NORMAL |
|
||||
nsIChannel::LOAD_CLASSIFY_URI);
|
||||
nsresult rv = NS_NewChannel(getter_AddRefs(channel),
|
||||
aRequest->mURI,
|
||||
context,
|
||||
securityFlags,
|
||||
contentPolicyType,
|
||||
loadGroup,
|
||||
prompter,
|
||||
nsIRequest::LOAD_NORMAL |
|
||||
nsIChannel::LOAD_CLASSIFY_URI);
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -356,26 +389,13 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
|
||||
timedChannel->SetInitiatorType(NS_LITERAL_STRING("script"));
|
||||
}
|
||||
|
||||
RefPtr<ContextMediator> mediator = new ContextMediator(this, aRequest);
|
||||
|
||||
nsCOMPtr<nsIStreamLoader> loader;
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), mediator);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener = loader.get();
|
||||
|
||||
if (aRequest->mCORSMode != CORS_NONE) {
|
||||
bool withCredentials = (aRequest->mCORSMode == CORS_USE_CREDENTIALS);
|
||||
RefPtr<nsCORSListenerProxy> corsListener =
|
||||
new nsCORSListenerProxy(listener, mDocument->NodePrincipal(),
|
||||
withCredentials);
|
||||
rv = corsListener->Init(channel, DataURIHandling::Allow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
listener = corsListener;
|
||||
}
|
||||
|
||||
rv = channel->AsyncOpen(listener, aRequest);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
return channel->AsyncOpen2(loader);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -51,10 +51,7 @@
|
||||
#include "nsICachingChannel.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsError.h"
|
||||
#include "nsCORSListenerProxy.h"
|
||||
#include "nsIHTMLDocument.h"
|
||||
#include "nsIStorageStream.h"
|
||||
#include "nsIPromptFactory.h"
|
||||
@ -126,7 +123,7 @@ using namespace mozilla::dom;
|
||||
#define XML_HTTP_REQUEST_SYNCLOOPING (1 << 10) // Internal
|
||||
#define XML_HTTP_REQUEST_BACKGROUND (1 << 13) // Internal
|
||||
#define XML_HTTP_REQUEST_USE_XSITE_AC (1 << 14) // Internal
|
||||
#define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 15) // Internal
|
||||
#define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE (1 << 15) // Internal
|
||||
#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 16) // Internal
|
||||
#define XML_HTTP_REQUEST_TIMED_OUT (1 << 17) // Internal
|
||||
#define XML_HTTP_REQUEST_DELETED (1 << 18) // Internal
|
||||
@ -1533,47 +1530,15 @@ nsXMLHttpRequest::IsSystemXHR()
|
||||
return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
|
||||
}
|
||||
|
||||
nsresult
|
||||
void
|
||||
nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
|
||||
{
|
||||
// A system XHR (chrome code or a web app with the right permission) can
|
||||
// always perform cross-site requests. In the web app case, however, we
|
||||
// must still check for protected URIs like file:///.
|
||||
if (IsSystemXHR()) {
|
||||
if (!nsContentUtils::IsSystemPrincipal(mPrincipal)) {
|
||||
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
aChannel->GetOriginalURI(getter_AddRefs(uri));
|
||||
return secMan->CheckLoadURIWithPrincipal(
|
||||
mPrincipal, uri, nsIScriptSecurityManager::STANDARD);
|
||||
}
|
||||
return NS_OK;
|
||||
// load anything, and same-origin loads are always allowed.
|
||||
if (!IsSystemXHR() &&
|
||||
!nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
|
||||
mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
|
||||
}
|
||||
|
||||
// If this is a same-origin request or the channel's URI inherits
|
||||
// its principal, it's allowed.
|
||||
if (nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This is a cross-site request
|
||||
mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
|
||||
|
||||
// Check if we need to do a preflight request.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
||||
NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
|
||||
|
||||
nsAutoCString method;
|
||||
httpChannel->GetRequestMethod(method);
|
||||
if (!mCORSUnsafeHeaders.IsEmpty() ||
|
||||
(mUpload && mUpload->HasListeners()) ||
|
||||
(!method.LowerCaseEqualsLiteral("get") &&
|
||||
!method.LowerCaseEqualsLiteral("post") &&
|
||||
!method.LowerCaseEqualsLiteral("head"))) {
|
||||
mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1680,21 +1645,6 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
||||
|
||||
rv = CheckInnerWindowCorrectness();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
|
||||
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
|
||||
uri,
|
||||
mPrincipal,
|
||||
doc,
|
||||
EmptyCString(), //mime guess
|
||||
nullptr, //extra
|
||||
&shouldLoad,
|
||||
nsContentUtils::GetContentPolicy(),
|
||||
nsContentUtils::GetSecurityManager());
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
if (NS_CP_REJECTED(shouldLoad)) {
|
||||
// Disallowed by content policy
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
// XXXbz this is wrong: we should only be looking at whether
|
||||
// user/password were passed, not at the values! See bug 759624.
|
||||
@ -1717,20 +1667,27 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
||||
// will be automatically aborted if the user leaves the page.
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
|
||||
|
||||
nsSecurityFlags secFlags = nsILoadInfo::SEC_NORMAL;
|
||||
nsSecurityFlags secFlags;
|
||||
nsLoadFlags loadFlags = nsIRequest::LOAD_BACKGROUND;
|
||||
if (IsSystemXHR()) {
|
||||
// Don't give this document the system principal. We need to keep track of
|
||||
// mPrincipal being system because we use it for various security checks
|
||||
// that should be passing, but the document data shouldn't get a system
|
||||
// principal. Hence we set the sandbox flag in loadinfo, so that
|
||||
// GetChannelResultPrincipal will give us the nullprincipal.
|
||||
secFlags |= nsILoadInfo::SEC_SANDBOXED;
|
||||
|
||||
//For a XHR, disable interception
|
||||
if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
|
||||
// When chrome is loading we want to make sure to sandbox any potential
|
||||
// result document. We also want to allow cross-origin loads.
|
||||
secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL |
|
||||
nsILoadInfo::SEC_SANDBOXED;
|
||||
}
|
||||
else if (IsSystemXHR()) {
|
||||
// For pages that have appropriate permissions, we want to still allow
|
||||
// cross-origin loads, but make sure that the any potential result
|
||||
// documents get the same principal as the loader.
|
||||
secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
|
||||
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
|
||||
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
|
||||
} else {
|
||||
secFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
|
||||
}
|
||||
else {
|
||||
// Otherwise use CORS. Again, make sure that potential result documents
|
||||
// use the same principal as the loader.
|
||||
secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
|
||||
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
|
||||
}
|
||||
|
||||
// If we have the document, use it
|
||||
@ -1755,10 +1712,10 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
||||
loadFlags);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
|
||||
XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
|
||||
XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE);
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
|
||||
if (httpChannel) {
|
||||
@ -1774,7 +1731,7 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
||||
|
||||
ChangeState(XML_HTTP_REQUEST_OPENED);
|
||||
|
||||
return rv;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
@ -2818,14 +2775,21 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
|
||||
ResetResponse();
|
||||
|
||||
rv = CheckChannelForCrossSiteRequest(mChannel);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
CheckChannelForCrossSiteRequest(mChannel);
|
||||
|
||||
bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
|
||||
|
||||
// Hook us up to listen to redirects and the like
|
||||
mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
|
||||
mChannel->SetNotificationCallbacks(this);
|
||||
if (!IsSystemXHR() && withCredentials) {
|
||||
// This is quite sad. We have to create the channel in .open(), since the
|
||||
// chrome-only xhr.channel API depends on that. However .withCredentials
|
||||
// can be modified after, so we don't know what to set the
|
||||
// SEC_REQUIRE_CORS_WITH_CREDENTIALS flag to when the channel is
|
||||
// created. So set it here using a hacky internal API.
|
||||
|
||||
// Not doing this for system XHR uses since those don't use CORS.
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
|
||||
static_cast<LoadInfo*>(loadInfo.get())->SetWithCredentialsSecFlag();
|
||||
}
|
||||
|
||||
// Blocking gets are common enough out of XHR that we should mark
|
||||
// the channel slow by default for pipeline purposes
|
||||
@ -2846,24 +2810,6 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
internalHttpChannel->SetResponseTimeoutEnabled(false);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener = this;
|
||||
if (!IsSystemXHR()) {
|
||||
// Always create a nsCORSListenerProxy here even if it's
|
||||
// a same-origin request right now, since it could be redirected.
|
||||
RefPtr<nsCORSListenerProxy> corsListener =
|
||||
new nsCORSListenerProxy(listener, mPrincipal, withCredentials);
|
||||
rv = corsListener->Init(mChannel, DataURIHandling::Allow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
listener = corsListener;
|
||||
}
|
||||
else {
|
||||
// Because of bug 682305, we can't let listener be the XHR object itself
|
||||
// because JS wouldn't be able to use it. So if we haven't otherwise
|
||||
// created a listener around 'this', do so now.
|
||||
|
||||
listener = new nsStreamListenerWrapper(listener);
|
||||
}
|
||||
|
||||
if (mIsAnon) {
|
||||
AddLoadFlags(mChannel, nsIRequest::LOAD_ANONYMOUS);
|
||||
}
|
||||
@ -2871,9 +2817,6 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
|
||||
}
|
||||
|
||||
NS_ASSERTION(listener != this,
|
||||
"Using an object as a listener that can't be exposed to JS");
|
||||
|
||||
// When we are sync loading, we need to bypass the local cache when it would
|
||||
// otherwise block us waiting for exclusive access to the cache. If we don't
|
||||
// do this, then we could dead lock in some cases (see bug 309424).
|
||||
@ -2906,10 +2849,19 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
mRequestSentTime = PR_Now();
|
||||
StartTimeoutTimer();
|
||||
|
||||
// Check if we need to do a preflight request.
|
||||
if (!mCORSUnsafeHeaders.IsEmpty() ||
|
||||
(mUpload && mUpload->HasListeners()) ||
|
||||
(!method.LowerCaseEqualsLiteral("get") &&
|
||||
!method.LowerCaseEqualsLiteral("post") &&
|
||||
!method.LowerCaseEqualsLiteral("head"))) {
|
||||
mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE;
|
||||
}
|
||||
|
||||
// Set up the preflight if needed
|
||||
if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
|
||||
// Check to see if this initial OPTIONS request has already been cached
|
||||
// in our special Access Control Cache.
|
||||
if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) &&
|
||||
(mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE)) {
|
||||
NS_ENSURE_TRUE(internalHttpChannel, NS_ERROR_DOM_BAD_URI);
|
||||
|
||||
rv = internalHttpChannel->SetCorsPreflightParameters(mCORSUnsafeHeaders,
|
||||
withCredentials, mPrincipal);
|
||||
@ -2932,16 +2884,30 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
||||
}
|
||||
}
|
||||
|
||||
// Hook us up to listen to redirects and the like
|
||||
// Only do this very late since this creates a cycle between
|
||||
// the channel and us. This cycle has to be manually broken if anything
|
||||
// below fails.
|
||||
mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
|
||||
mChannel->SetNotificationCallbacks(this);
|
||||
|
||||
// Start reading from the channel
|
||||
rv = mChannel->AsyncOpen(listener, nullptr);
|
||||
// Because of bug 682305, we can't let listener be the XHR object itself
|
||||
// because JS wouldn't be able to use it. So create a listener around 'this'.
|
||||
// Make sure to hold a strong reference so that we don't leak the wrapper.
|
||||
nsCOMPtr<nsIStreamListener> listener = new nsStreamListenerWrapper(this);
|
||||
rv = mChannel->AsyncOpen2(listener);
|
||||
listener = nullptr;
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// Drop our ref to the channel to avoid cycles
|
||||
// Drop our ref to the channel to avoid cycles. Also drop channel's
|
||||
// ref to us to be extra safe.
|
||||
mChannel->SetNotificationCallbacks(mNotificationCallbacks);
|
||||
mChannel = nullptr;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Either AsyncOpen was called, or CORS will open the channel later.
|
||||
mWaitingForOnStopRequest = true;
|
||||
|
||||
// If we're synchronous, spin an event loop here and wait
|
||||
@ -3384,17 +3350,12 @@ nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
||||
nsresult rv;
|
||||
|
||||
if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
|
||||
rv = CheckChannelForCrossSiteRequest(aNewChannel);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("nsXMLHttpRequest::OnChannelRedirect: "
|
||||
"CheckChannelForCrossSiteRequest returned failure");
|
||||
return rv;
|
||||
}
|
||||
CheckChannelForCrossSiteRequest(aNewChannel);
|
||||
|
||||
// Disable redirects for preflighted cross-site requests entirely for now
|
||||
// Note, do this after the call to CheckChannelForCrossSiteRequest
|
||||
// to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date
|
||||
if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) {
|
||||
if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) &&
|
||||
(mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE)) {
|
||||
aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
@ -3555,7 +3516,7 @@ bool
|
||||
nsXMLHttpRequest::AllowUploadProgress()
|
||||
{
|
||||
return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
|
||||
(mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
|
||||
(mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT_IF_XSITE);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
@ -626,7 +626,7 @@ protected:
|
||||
*
|
||||
* Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit.
|
||||
*/
|
||||
nsresult CheckChannelForCrossSiteRequest(nsIChannel* aChannel);
|
||||
void CheckChannelForCrossSiteRequest(nsIChannel* aChannel);
|
||||
|
||||
void StartProgressEventTimer();
|
||||
|
||||
|
@ -50,28 +50,6 @@ function info(aMessage) {
|
||||
message(obj);
|
||||
}
|
||||
|
||||
function todo(aBool, aMessage) {
|
||||
var obj = {
|
||||
type: "todo",
|
||||
bool: aBool,
|
||||
message: aMessage
|
||||
};
|
||||
++message.ping;
|
||||
message(obj);
|
||||
}
|
||||
|
||||
function todo_is(aActual, aExpected, aMessage) {
|
||||
var obj = {
|
||||
type: "todo_is",
|
||||
actual: aActual,
|
||||
expected: aExpected,
|
||||
message: aMessage
|
||||
};
|
||||
++message.ping;
|
||||
message(obj);
|
||||
}
|
||||
|
||||
|
||||
function request(aURL) {
|
||||
return new Promise(function (aResolve, aReject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
@ -125,15 +103,11 @@ function testSuccessResponse() {
|
||||
message: "request to same-origin redirect to cross-origin URL",
|
||||
requestURL: "http://mochi.test:8888/tests/dom/base/test/file_XHRResponseURL.sjs?url=http://example.com/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
responseURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
skip: isInWorker(),
|
||||
reason: "cross-origin redirect request not works on Workers, see bug 882458"
|
||||
},
|
||||
{
|
||||
message: "request to same-origin redirects several times and finally go to cross-origin URL",
|
||||
requestURL: "http://mochi.test:8888/tests/dom/base/test/file_XHRResponseURL.sjs?url=" + encodeURIComponent("http://mochi.test:8888/tests/dom/base/test/file_XHRResponseURL.sjs?url=http://example.com/tests/dom/base/test/file_XHRResponseURL.text"),
|
||||
responseURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
skip: isInWorker(),
|
||||
reason: "cross-origin redirect request not works on Workers, see bug 882458"
|
||||
},
|
||||
|
||||
// tests that start with cross-origin request
|
||||
@ -151,43 +125,31 @@ function testSuccessResponse() {
|
||||
message: "request to cross-origin redirect to the same cross-origin URL",
|
||||
requestURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.sjs?url=http://example.com/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
responseURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
skip: isInWorker(),
|
||||
reason: "cross-origin redirect request not works on Workers, see bug 882458"
|
||||
},
|
||||
{
|
||||
message: "request to cross-origin redirect to another cross-origin URL",
|
||||
requestURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.sjs?url=http://example.org/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
responseURL: "http://example.org/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
skip: isInWorker(),
|
||||
reason: "cross-origin redirect request not works on Workers, see bug 882458"
|
||||
},
|
||||
{
|
||||
message: "request to cross-origin redirects several times and finally go to same-origin URL",
|
||||
requestURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.sjs?url=" + encodeURIComponent("http://example.com/tests/dom/base/test/file_XHRResponseURL.sjs?url=http://mochi.test:8888/tests/dom/base/test/file_XHRResponseURL.text"),
|
||||
responseURL: "http://mochi.test:8888/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
skip: isInWorker(),
|
||||
reason: "cross-origin redirect request not works on Workers, see bug 882458"
|
||||
},
|
||||
{
|
||||
message: "request to cross-origin redirects several times and finally go to the same cross-origin URL",
|
||||
requestURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.sjs?url=" + encodeURIComponent("http://example.com/tests/dom/base/test/file_XHRResponseURL.sjs?url=http://example.com/tests/dom/base/test/file_XHRResponseURL.text"),
|
||||
responseURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
skip: isInWorker(),
|
||||
reason: "cross-origin redirect request not works on Workers, see bug 882458"
|
||||
},
|
||||
{
|
||||
message: "request to cross-origin redirects several times and finally go to another cross-origin URL",
|
||||
requestURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.sjs?url=" + encodeURIComponent("http://example.com/tests/dom/base/test/file_XHRResponseURL.sjs?url=http://example.org/tests/dom/base/test/file_XHRResponseURL.text"),
|
||||
responseURL: "http://example.org/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
skip: isInWorker(),
|
||||
reason: "cross-origin redirect request not works on Workers, see bug 882458"
|
||||
},
|
||||
{
|
||||
message: "request to cross-origin redirects to another cross-origin and finally go to the other cross-origin URL",
|
||||
requestURL: "http://example.com/tests/dom/base/test/file_XHRResponseURL.sjs?url=" + encodeURIComponent("http://example.org/tests/dom/base/test/file_XHRResponseURL.sjs?url=http://test1.example.com/tests/dom/base/test/file_XHRResponseURL.text"),
|
||||
responseURL: "http://test1.example.com/tests/dom/base/test/file_XHRResponseURL.text",
|
||||
skip: isInWorker(),
|
||||
reason: "cross-origin redirect request not works on Workers, see bug 882458"
|
||||
},
|
||||
{
|
||||
message: "request URL has fragment",
|
||||
@ -209,13 +171,8 @@ function testSuccessResponse() {
|
||||
];
|
||||
|
||||
var sequence = createSequentialRequest(parameters, function (aXHR, aParam) {
|
||||
if (aParam.skip) {
|
||||
todo(aXHR.succeeded, aParam.reason);
|
||||
todo_is(aXHR.responseURL, aParam.responseURL, aParam.reason);
|
||||
} else {
|
||||
ok(aXHR.succeeded, "assert request succeeded");
|
||||
is(aXHR.responseURL, aParam.responseURL, aParam.message);
|
||||
}
|
||||
ok(aXHR.succeeded, "assert request succeeded");
|
||||
is(aXHR.responseURL, aParam.responseURL, aParam.message);
|
||||
});
|
||||
|
||||
sequence.then(function () {
|
||||
|
@ -510,7 +510,6 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
|
||||
[test_bug433533.html]
|
||||
[test_bug433662.html]
|
||||
[test_bug435425.html]
|
||||
[test_bug438519.html]
|
||||
[test_bug444030.xhtml]
|
||||
[test_bug444322.html]
|
||||
[test_bug444722.html]
|
||||
|
@ -1,44 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=438519
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 438519</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body onload="doTest()">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=438519">Mozilla Bug 438519</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display:none">
|
||||
|
||||
<iframe id="empty" src="data:text/xml,<!DOCTYPE HTML []><html></html>"></iframe>
|
||||
<iframe id="missing" src="data:text/xml,<!DOCTYPE HTML><html></html>"></iframe>
|
||||
<iframe id="entity" src="data:text/xml,<!DOCTYPE HTML [ <!ENTITY foo 'foo'> ]><html></html>"></iframe>
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 218236 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function doTest() {
|
||||
function checkInternalSubset(id, expected) {
|
||||
var e = document.getElementById(id);
|
||||
is(e.contentDocument.doctype.internalSubset, expected, "checking '" + id + "'");
|
||||
}
|
||||
|
||||
checkInternalSubset("empty", "");
|
||||
checkInternalSubset("missing", null);
|
||||
checkInternalSubset("entity", " <!ENTITY foo 'foo'> ");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -24,7 +24,6 @@ function checkDoc(title, expectedtitle, normalizedtitle) {
|
||||
is(doc.doctype.name, "html");
|
||||
is(doc.doctype.publicId, "");
|
||||
is(doc.doctype.systemId, "");
|
||||
is(doc.doctype.internalSubset, null, "internalSubset should be null!");
|
||||
isElement(doc.documentElement, "html");
|
||||
isElement(doc.documentElement.firstChild, "head");
|
||||
if (title !== undefined) {
|
||||
|
@ -237,6 +237,58 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
|
||||
runTest();
|
||||
}
|
||||
|
||||
function testIterable() {
|
||||
var u = new URLSearchParams();
|
||||
u.set('1','2');
|
||||
u.set('2','4');
|
||||
u.set('3','6');
|
||||
u.set('4','8');
|
||||
u.set('5','10');
|
||||
|
||||
var key_iter = u.keys();
|
||||
var value_iter = u.values();
|
||||
var entries_iter = u.entries();
|
||||
for (var i = 0; i < 5; ++i) {
|
||||
var v = i + 1;
|
||||
var key = key_iter.next();
|
||||
var value = value_iter.next();
|
||||
var entry = entries_iter.next();
|
||||
is(key.value, v.toString(), "Correct Key iterator: " + v.toString());
|
||||
ok(!key.done, "Key.done is false");
|
||||
is(value.value, (v * 2).toString(), "Correct Value iterator: " + (v * 2).toString());
|
||||
ok(!value.done, "Value.done is false");
|
||||
is(entry.value[0], v.toString(), "Correct Entry 0 iterator: " + v.toString());
|
||||
is(entry.value[1], (v * 2).toString(), "Correct Entry 1 iterator: " + (v * 2).toString());
|
||||
ok(!entry.done, "Entry.done is false");
|
||||
}
|
||||
|
||||
var last = key_iter.next();
|
||||
ok(last.done, "Nothing more to read.");
|
||||
is(last.value, undefined, "Undefined is the last key");
|
||||
|
||||
last = value_iter.next();
|
||||
ok(last.done, "Nothing more to read.");
|
||||
is(last.value, undefined, "Undefined is the last value");
|
||||
|
||||
last = entries_iter.next();
|
||||
ok(last.done, "Nothing more to read.");
|
||||
|
||||
key_iter = u.keys();
|
||||
key_iter.next();
|
||||
key_iter.next();
|
||||
u.delete('1');
|
||||
u.delete('2');
|
||||
u.delete('3');
|
||||
u.delete('4');
|
||||
u.delete('5');
|
||||
|
||||
last = key_iter.next();
|
||||
ok(last.done, "Nothing more to read.");
|
||||
is(last.value, undefined, "Undefined is the last key");
|
||||
|
||||
runTest();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
testSimpleURLSearchParams,
|
||||
testCopyURLSearchParams,
|
||||
@ -248,7 +300,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=887836
|
||||
testOrdering,
|
||||
testDelete,
|
||||
testGetNULL,
|
||||
testSet
|
||||
testSet,
|
||||
testIterable
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
|
@ -700,10 +700,6 @@ DOMInterfaces = {
|
||||
'notflattened': True
|
||||
},
|
||||
|
||||
'IterableIterator': {
|
||||
'skipGen': True
|
||||
},
|
||||
|
||||
'KeyEvent': {
|
||||
'concrete': False
|
||||
},
|
||||
|
@ -740,7 +740,7 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
"interface that already has %s "
|
||||
"declaration" %
|
||||
(member.maplikeOrSetlikeOrIterableType,
|
||||
self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType),
|
||||
self.maplikeOrSetlikeOrIterable.maplikeOrSetlikeOrIterableType),
|
||||
[self.maplikeOrSetlikeOrIterable.location,
|
||||
member.location])
|
||||
self.maplikeOrSetlikeOrIterable = member
|
||||
@ -6567,6 +6567,7 @@ class Parser(Tokenizer):
|
||||
interfaceStatements = [p for p in self._productions if
|
||||
isinstance(p, IDLInterface)]
|
||||
|
||||
iterableIteratorIface = None
|
||||
for iface in interfaceStatements:
|
||||
iterable = None
|
||||
# We haven't run finish() on the interface yet, so we don't know
|
||||
@ -6578,33 +6579,30 @@ class Parser(Tokenizer):
|
||||
iterable = m
|
||||
break
|
||||
if iterable:
|
||||
def simpleExtendedAttr(str):
|
||||
return IDLExtendedAttribute(iface.location, (str, ))
|
||||
nextMethod = IDLMethod(
|
||||
iface.location,
|
||||
IDLUnresolvedIdentifier(iface.location, "next"),
|
||||
BuiltinTypes[IDLBuiltinType.Types.object], [])
|
||||
nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
|
||||
itr_ident = IDLUnresolvedIdentifier(iface.location,
|
||||
iface.identifier.name + "Iterator")
|
||||
itr_iface = IDLInterface(iface.location, self.globalScope(),
|
||||
itr_ident, None, [],
|
||||
itr_ident, None, [nextMethod],
|
||||
isKnownNonPartial=True)
|
||||
itr_iface.addExtendedAttributes([IDLExtendedAttribute(iface.location,
|
||||
("NoInterfaceObject", ))])
|
||||
itr_iface.addExtendedAttributes([simpleExtendedAttr("NoInterfaceObject")])
|
||||
# Make sure the exposure set for the iterator interface is the
|
||||
# same as the exposure set for the iterable interface, because
|
||||
# we're going to generate methods on the iterable that return
|
||||
# instances of the iterator.
|
||||
itr_iface._exposureGlobalNames = set(iface._exposureGlobalNames)
|
||||
# Always append generated iterable interfaces and their
|
||||
# matching implements statements after the interface they're a
|
||||
# member of, otherwise nativeType generation won't work
|
||||
# correctly.
|
||||
# Always append generated iterable interfaces after the
|
||||
# interface they're a member of, otherwise nativeType generation
|
||||
# won't work correctly.
|
||||
itr_iface.iterableInterface = iface
|
||||
self._productions.append(itr_iface)
|
||||
iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
|
||||
itrPlaceholder = IDLIdentifierPlaceholder(iface.location,
|
||||
IDLUnresolvedIdentifier(iface.location,
|
||||
"IterableIterator"))
|
||||
implements = IDLImplementsStatement(iface.location,
|
||||
IDLIdentifierPlaceholder(iface.location,
|
||||
itr_ident),
|
||||
itrPlaceholder)
|
||||
self._productions.append(implements)
|
||||
|
||||
# Then, finish all the IDLImplementsStatements. In particular, we
|
||||
# have to make sure we do those before we do the IDLInterfaces.
|
||||
|
@ -10,20 +10,24 @@ def WebIDLTest(parser, harness):
|
||||
"%s - Should have production count %d" % (prefix, numProductions))
|
||||
harness.ok(isinstance(results[0], WebIDL.IDLInterface),
|
||||
"%s - Should be an IDLInterface" % (prefix))
|
||||
harness.check(len(results[0].members), len(expectedMembers),
|
||||
"%s - Should be %d members" % (prefix,
|
||||
len(expectedMembers)))
|
||||
# Make a copy, since we plan to modify it
|
||||
expectedMembers = list(expectedMembers)
|
||||
for m in results[0].members:
|
||||
name = m.identifier.name
|
||||
if (name, type(m)) in expectedMembers:
|
||||
harness.ok(True, "%s - %s - Should be a %s" % (prefix, name,
|
||||
type(m)))
|
||||
elif isinstance(m, WebIDL.IDLMaplikeOrSetlike):
|
||||
harness.ok(True, "%s - %s - Should be a MaplikeOrSetlike" %
|
||||
(prefix, name))
|
||||
expectedMembers.remove((name, type(m)))
|
||||
else:
|
||||
harness.ok(False, "%s - %s - Unknown symbol of type %s" %
|
||||
(prefix, name, type(m)))
|
||||
# A bit of a hoop because we can't generate the error string if we pass
|
||||
if len(expectedMembers) == 0:
|
||||
harness.ok(True, "Found all the members")
|
||||
else:
|
||||
harness.ok(False,
|
||||
"Expected member not found: %s of type %s" %
|
||||
(expectedMembers[0][0], expectedMembers[0][1]))
|
||||
return results
|
||||
|
||||
def shouldFail(prefix, iface):
|
||||
@ -38,11 +42,11 @@ def WebIDLTest(parser, harness):
|
||||
prefix + " - Interface failed as expected")
|
||||
except Exception, e:
|
||||
harness.ok(False,
|
||||
prefix + " - Interface failed but not as a WebIDLError exception")
|
||||
prefix + " - Interface failed but not as a WebIDLError exception: %s" % e)
|
||||
|
||||
iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys",
|
||||
"values"]]
|
||||
setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has", "foreach"]] +
|
||||
setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has", "forEach"]] +
|
||||
[("__setlike", WebIDL.IDLMaplikeOrSetlike)] +
|
||||
iterableMembers)
|
||||
setROMembers.extend([("size", WebIDL.IDLAttribute)])
|
||||
@ -58,7 +62,7 @@ def WebIDLTest(parser, harness):
|
||||
"__clear",
|
||||
"__delete"]] +
|
||||
setRWMembers)
|
||||
mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has", "foreach"]] +
|
||||
mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has", "forEach"]] +
|
||||
[("__maplike", WebIDL.IDLMaplikeOrSetlike)] +
|
||||
iterableMembers)
|
||||
mapROMembers.extend([("size", WebIDL.IDLAttribute)])
|
||||
@ -70,6 +74,10 @@ def WebIDLTest(parser, harness):
|
||||
"__delete"]] +
|
||||
mapRWMembers)
|
||||
|
||||
# OK, now that we've used iterableMembers to set up the above, append
|
||||
# __iterable to it for the iterable<> case.
|
||||
iterableMembers.append(("__iterable", WebIDL.IDLIterable))
|
||||
|
||||
disallowedIterableNames = ["keys", "entries", "values"]
|
||||
disallowedMemberNames = ["forEach", "has", "size"] + disallowedIterableNames
|
||||
mapDisallowedMemberNames = ["get"] + disallowedMemberNames
|
||||
@ -86,14 +94,18 @@ def WebIDLTest(parser, harness):
|
||||
interface Foo1 {
|
||||
iterable<long>;
|
||||
};
|
||||
""", iterableMembers)
|
||||
""", iterableMembers,
|
||||
# numProductions == 2 because of the generated iterator iface,
|
||||
numProductions=2)
|
||||
|
||||
shouldPass("Iterable (key and value)",
|
||||
"""
|
||||
interface Foo1 {
|
||||
iterable<long, long>;
|
||||
};
|
||||
""", iterableMembers)
|
||||
""", iterableMembers,
|
||||
# numProductions == 2 because of the generated iterator iface,
|
||||
numProductions=2)
|
||||
|
||||
shouldPass("Maplike (readwrite)",
|
||||
"""
|
||||
@ -574,5 +586,5 @@ def WebIDLTest(parser, harness):
|
||||
if m.identifier.name in ["clear", "set", "delete"]:
|
||||
harness.ok(m.isMethod(), "%s should be a method" % m.identifier.name)
|
||||
harness.check(m.maxArgCount, 4, "%s should have 4 arguments" % m.identifier.name)
|
||||
harness.ok(not m.isMaplikeOrSetlikeMethod(),
|
||||
harness.ok(not m.isMaplikeOrSetlikeOrIterableMethod(),
|
||||
"%s should not be a maplike/setlike function" % m.identifier.name)
|
||||
|
3
dom/cache/DBSchema.cpp
vendored
3
dom/cache/DBSchema.cpp
vendored
@ -192,8 +192,7 @@ static_assert(int(HeadersGuardEnum::None) == 0 &&
|
||||
static_assert(int(RequestMode::Same_origin) == 0 &&
|
||||
int(RequestMode::No_cors) == 1 &&
|
||||
int(RequestMode::Cors) == 2 &&
|
||||
int(RequestMode::Cors_with_forced_preflight) == 3 &&
|
||||
int(RequestMode::EndGuard_) == 4,
|
||||
int(RequestMode::EndGuard_) == 3,
|
||||
"RequestMode values are as expected");
|
||||
static_assert(int(RequestCredentials::Omit) == 0 &&
|
||||
int(RequestCredentials::Same_origin) == 1 &&
|
||||
|
@ -71,6 +71,11 @@ public:
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
|
||||
GLbitfield mask, GLenum filter);
|
||||
void FramebufferTextureLayer(GLenum target, GLenum attachment, WebGLTexture* texture, GLint level, GLint layer);
|
||||
|
||||
virtual JS::Value GetFramebufferAttachmentParameter(JSContext* cx, GLenum target,
|
||||
GLenum attachment, GLenum pname,
|
||||
ErrorResult& rv) override;
|
||||
|
||||
void InvalidateFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
|
||||
ErrorResult& rv);
|
||||
void InvalidateSubFramebuffer (GLenum target, const dom::Sequence<GLenum>& attachments, GLint x, GLint y,
|
||||
|
@ -8,10 +8,17 @@
|
||||
#include "GLContext.h"
|
||||
#include "GLScreenBuffer.h"
|
||||
#include "WebGLContextUtils.h"
|
||||
#include "WebGLFormats.h"
|
||||
#include "WebGLFramebuffer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using gl::GLContext;
|
||||
using gl::GLFormats;
|
||||
using webgl::EffectiveFormat;
|
||||
using webgl::FormatInfo;
|
||||
using webgl::ComponentType;
|
||||
|
||||
// Returns one of FLOAT, INT, UNSIGNED_INT.
|
||||
// Fixed-points (normalized ints) are considered FLOAT.
|
||||
static GLenum
|
||||
@ -418,6 +425,103 @@ WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment,
|
||||
fb->FramebufferTextureLayer(attachment, texture, level, layer);
|
||||
}
|
||||
|
||||
JS::Value
|
||||
WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
|
||||
GLenum target,
|
||||
GLenum attachment,
|
||||
GLenum pname,
|
||||
ErrorResult& rv)
|
||||
{
|
||||
if (IsContextLost())
|
||||
return JS::NullValue();
|
||||
|
||||
// OpenGL ES 3.0.4 (August 27, 2014) 6.1. QUERYING GL STATE 240
|
||||
// "getFramebufferAttachmentParamter returns information about attachments of a bound
|
||||
// framebuffer object. target must be DRAW_FRAMEBUFFER, READ_FRAMEBUFFER, or
|
||||
// FRAMEBUFFER."
|
||||
|
||||
if (!ValidateFramebufferTarget(target, "getFramebufferAttachmentParameter"))
|
||||
return JS::NullValue();
|
||||
|
||||
// FRAMEBUFFER is equivalent to DRAW_FRAMEBUFFER.
|
||||
if (target == LOCAL_GL_FRAMEBUFFER)
|
||||
target = LOCAL_GL_DRAW_FRAMEBUFFER;
|
||||
|
||||
WebGLFramebuffer* boundFB = nullptr;
|
||||
switch (target) {
|
||||
case LOCAL_GL_DRAW_FRAMEBUFFER: boundFB = mBoundDrawFramebuffer; break;
|
||||
case LOCAL_GL_READ_FRAMEBUFFER: boundFB = mBoundReadFramebuffer; break;
|
||||
}
|
||||
|
||||
if (boundFB) {
|
||||
return boundFB->GetAttachmentParameter(cx, attachment, pname, rv);
|
||||
}
|
||||
|
||||
// Handle default FB
|
||||
const gl::GLFormats& formats = gl->GetGLFormats();
|
||||
GLenum internalFormat = LOCAL_GL_NONE;
|
||||
|
||||
/* If the default framebuffer is bound to target, then attachment must be BACK,
|
||||
identifying the color buffer; DEPTH, identifying the depth buffer; or STENCIL,
|
||||
identifying the stencil buffer. */
|
||||
switch (attachment) {
|
||||
case LOCAL_GL_BACK:
|
||||
internalFormat = formats.color_texInternalFormat;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_DEPTH:
|
||||
internalFormat = formats.depth;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_STENCIL:
|
||||
internalFormat = formats.stencil;
|
||||
break;
|
||||
|
||||
default:
|
||||
ErrorInvalidEnum("getFramebufferAttachmentParameter: Can only query "
|
||||
"attachment BACK, DEPTH, or STENCIL from default "
|
||||
"framebuffer");
|
||||
return JS::NullValue();
|
||||
}
|
||||
|
||||
const FormatInfo* info = webgl::GetInfoBySizedFormat(internalFormat);
|
||||
MOZ_RELEASE_ASSERT(info);
|
||||
EffectiveFormat effectiveFormat = info->effectiveFormat;
|
||||
|
||||
switch (pname) {
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
|
||||
return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
|
||||
return JS::Int32Value(webgl::GetComponentSize(effectiveFormat, pname));
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
|
||||
if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT &&
|
||||
pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)
|
||||
{
|
||||
ErrorInvalidOperation("getFramebufferAttachmentParameter: Querying "
|
||||
"FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE against "
|
||||
"DEPTH_STENCIL_ATTACHMENT is an error.");
|
||||
return JS::NullValue();
|
||||
}
|
||||
|
||||
return JS::Int32Value(webgl::GetComponentType(effectiveFormat));
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
|
||||
return JS::Int32Value(webgl::GetColorEncoding(effectiveFormat));
|
||||
}
|
||||
|
||||
/* Any combinations of framebuffer type and pname not described above will generate an
|
||||
INVALID_ENUM error. */
|
||||
ErrorInvalidEnum("getFramebufferAttachmentParameter: Invalid combination of ");
|
||||
return JS::NullValue();
|
||||
}
|
||||
|
||||
// Map attachments intended for the default buffer, to attachments for a non-
|
||||
// default buffer.
|
||||
static bool
|
||||
|
@ -458,9 +458,9 @@ public:
|
||||
}
|
||||
|
||||
GLenum GetError();
|
||||
JS::Value GetFramebufferAttachmentParameter(JSContext* cx, GLenum target,
|
||||
GLenum attachment, GLenum pname,
|
||||
ErrorResult& rv);
|
||||
virtual JS::Value GetFramebufferAttachmentParameter(JSContext* cx, GLenum target,
|
||||
GLenum attachment, GLenum pname,
|
||||
ErrorResult& rv);
|
||||
|
||||
void GetFramebufferAttachmentParameter(JSContext* cx, GLenum target,
|
||||
GLenum attachment, GLenum pname,
|
||||
|
@ -809,5 +809,319 @@ FormatUsageAuthority::AddUnpackOption(GLenum unpackFormat, GLenum unpackType,
|
||||
MOZ_ALWAYS_TRUE(didInsert);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ComponentSizes
|
||||
{
|
||||
GLubyte redSize;
|
||||
GLubyte greenSize;
|
||||
GLubyte blueSize;
|
||||
GLubyte alphaSize;
|
||||
GLubyte depthSize;
|
||||
GLubyte stencilSize;
|
||||
};
|
||||
|
||||
static ComponentSizes kComponentSizes[] = {
|
||||
// GLES 3.0.4, p128-129, "Required Texture Formats"
|
||||
// "Texture and renderbuffer color formats"
|
||||
{ 32, 32, 32, 32, 0, 0 }, // RGBA32I,
|
||||
{ 32, 32, 32, 32, 0, 0 }, // RGBA32UI,
|
||||
{ 16, 16, 16, 16, 0, 0 }, // RGBA16I,
|
||||
{ 16, 16, 16, 16, 0, 0 }, // RGBA16UI,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // RGBA8,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // RGBA8I,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // RGBA8UI,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // SRGB8_ALPHA8,
|
||||
{ 10, 10, 10, 2, 0, 0 }, // RGB10_A2,
|
||||
{ 10, 10, 10, 2, 0, 0 }, // RGB10_A2UI,
|
||||
{ 4, 4, 4, 4, 0, 0 }, // RGBA4,
|
||||
{ 5, 5, 5, 1, 0, 0 }, // RGB5_A1,
|
||||
|
||||
{ 8, 8, 8, 0, 0, 0 }, // RGB8,
|
||||
{ 8, 8, 8, 0, 0, 0 }, // RGB565,
|
||||
|
||||
{ 32, 32, 0, 0, 0, 0 }, // RG32I,
|
||||
{ 32, 32, 0, 0, 0, 0 }, // RG32UI,
|
||||
{ 16, 16, 0, 0, 0, 0 }, // RG16I,
|
||||
{ 16, 16, 0, 0, 0, 0 }, // RG16UI,
|
||||
{ 8, 8, 0, 0, 0, 0 }, // RG8,
|
||||
{ 8, 8, 0, 0, 0, 0 }, // RG8I,
|
||||
{ 8, 8, 0, 0, 0, 0 }, // RG8UI,
|
||||
|
||||
{ 32, 0, 0, 0, 0, 0 }, // R32I,
|
||||
{ 32, 0, 0, 0, 0, 0 }, // R32UI,
|
||||
{ 16, 0, 0, 0, 0, 0 }, // R16I,
|
||||
{ 16, 0, 0, 0, 0, 0 }, // R16UI,
|
||||
{ 8, 0, 0, 0, 0, 0 }, // R8,
|
||||
{ 8, 0, 0, 0, 0, 0 }, // R8I,
|
||||
{ 8, 0, 0, 0, 0, 0 }, // R8UI,
|
||||
|
||||
// "Texture-only color formats"
|
||||
{ 32, 32, 32, 32, 0, 0 }, // RGBA32F,
|
||||
{ 16, 16, 16, 16, 0, 0 }, // RGBA16F,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // RGBA8_SNORM,
|
||||
|
||||
{ 32, 32, 32, 0, 0, 0 }, // RGB32F,
|
||||
{ 32, 32, 32, 0, 0, 0 }, // RGB32I,
|
||||
{ 32, 32, 32, 0, 0, 0 }, // RGB32UI,
|
||||
|
||||
{ 16, 16, 16, 0, 0, 0 }, // RGB16F,
|
||||
{ 16, 16, 16, 0, 0, 0 }, // RGB16I,
|
||||
{ 16, 16, 16, 0, 0, 0 }, // RGB16UI,
|
||||
|
||||
{ 8, 8, 8, 0, 0, 0 }, // RGB8_SNORM,
|
||||
{ 8, 8, 8, 0, 0, 0 }, // RGB8I,
|
||||
{ 8, 8, 8, 0, 0, 0 }, // RGB8UI,
|
||||
{ 8, 8, 8, 0, 0, 0 }, // SRGB8,
|
||||
|
||||
{ 11, 11, 11, 0, 0, 0 }, // R11F_G11F_B10F,
|
||||
{ 9, 9, 9, 0, 0, 0 }, // RGB9_E5,
|
||||
|
||||
{ 32, 32, 0, 0, 0, 0 }, // RG32F,
|
||||
{ 16, 16, 0, 0, 0, 0 }, // RG16F,
|
||||
{ 8, 8, 0, 0, 0, 0 }, // RG8_SNORM,
|
||||
|
||||
{ 32, 0, 0, 0, 0, 0 }, // R32F,
|
||||
{ 16, 0, 0, 0, 0, 0 }, // R16F,
|
||||
{ 8, 0, 0, 0, 0, 0 }, // R8_SNORM,
|
||||
|
||||
// "Depth formats"
|
||||
{ 0, 0, 0, 0, 32, 0 }, // DEPTH_COMPONENT32F,
|
||||
{ 0, 0, 0, 0, 24, 0 }, // DEPTH_COMPONENT24,
|
||||
{ 0, 0, 0, 0, 16, 0 }, // DEPTH_COMPONENT16,
|
||||
|
||||
// "Combined depth+stencil formats"
|
||||
{ 0, 0, 0, 0, 32, 8 }, // DEPTH32F_STENCIL0,
|
||||
{ 0, 0, 0, 0, 24, 8 }, // DEPTH24_STENCIL8,
|
||||
|
||||
// GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
|
||||
{ 0, 0, 0, 0, 0, 8 }, // STENCIL_INDEX8,
|
||||
|
||||
// GLES 3.0.4, p128, table 3.12.
|
||||
{ 8, 8, 8, 8, 0, 0 }, // Luminance8Alpha8,
|
||||
{ 8, 8, 8, 0, 0, 0 }, // Luminance8,
|
||||
{ 0, 0, 0, 8, 0, 0 }, // Alpha8,
|
||||
|
||||
// GLES 3.0.4, p147, table 3.19
|
||||
// GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_R11_EAC,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SIGNED_R11_EAC,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RG11_EAC,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SIGNED_RG11_EAC,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGB8_ETC2,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SRGB8_ETC2,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA8_ETC2_EAC,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
|
||||
|
||||
// AMD_compressed_ATC_texture
|
||||
{ 8, 8, 8, 0, 0, 0 }, // ATC_RGB_AMD,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // ATC_RGBA_EXPLICIT_ALPHA_AMD,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // ATC_RGBA_INTERPOLATED_ALPHA_AMD,
|
||||
|
||||
// EXT_texture_compression_s3tc
|
||||
{ 8, 8, 8, 0, 0, 0 }, // COMPRESSED_RGB_S3TC_DXT1,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_S3TC_DXT1,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_S3TC_DXT3,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_S3TC_DXT5,
|
||||
|
||||
// IMG_texture_compression_pvrtc
|
||||
{ 8, 8, 8, 0, 0, 0 }, // COMPRESSED_RGB_PVRTC_4BPPV1,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_PVRTC_4BPPV1,
|
||||
{ 8, 8, 8, 0, 0, 0 }, // COMPRESSED_RGB_PVRTC_2BPPV1,
|
||||
{ 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_PVRTC_2BPPV1,
|
||||
|
||||
// OES_compressed_ETC1_RGB8_texture
|
||||
{ 8, 8, 8, 0, 0, 0 }, // ETC1_RGB8,
|
||||
|
||||
// OES_texture_float
|
||||
{ 32, 32, 32, 32, 0, 0 }, // Luminance32FAlpha32F,
|
||||
{ 32, 32, 32, 0, 0, 0 }, // Luminance32F,
|
||||
{ 0, 0, 0, 32, 0, 0 }, // Alpha32F,
|
||||
|
||||
// OES_texture_half_float
|
||||
{ 16, 16, 16, 16, 0, 0 }, // Luminance16FAlpha16F,
|
||||
{ 16, 16, 16, 0, 0, 0 }, // Luminance16F,
|
||||
{ 0, 0, 0, 16, 0, 0 }, // Alpha16F,
|
||||
|
||||
{ 0, } // MAX
|
||||
};
|
||||
|
||||
GLint
|
||||
GetComponentSize(EffectiveFormat format, GLenum component)
|
||||
{
|
||||
ComponentSizes compSize = kComponentSizes[(int) format];
|
||||
switch (component) {
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
|
||||
case LOCAL_GL_RENDERBUFFER_RED_SIZE:
|
||||
case LOCAL_GL_RED_BITS:
|
||||
return compSize.redSize;
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
|
||||
case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
|
||||
case LOCAL_GL_GREEN_BITS:
|
||||
return compSize.greenSize;
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
|
||||
case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
|
||||
case LOCAL_GL_BLUE_BITS:
|
||||
return compSize.blueSize;
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
|
||||
case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
|
||||
case LOCAL_GL_ALPHA_BITS:
|
||||
return compSize.alphaSize;
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
|
||||
case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
|
||||
case LOCAL_GL_DEPTH_BITS:
|
||||
return compSize.depthSize;
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
|
||||
case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
|
||||
case LOCAL_GL_STENCIL_BITS:
|
||||
return compSize.stencilSize;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static GLenum kComponentTypes[] = {
|
||||
// GLES 3.0.4, p128-129, "Required Texture Formats"
|
||||
// "Texture and renderbuffer color formats"
|
||||
LOCAL_GL_INT, // RGBA32I,
|
||||
LOCAL_GL_UNSIGNED_INT, // RGBA32UI,
|
||||
LOCAL_GL_INT, // RGBA16I,
|
||||
LOCAL_GL_UNSIGNED_INT, // RGBA16UI,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // RGBA8,
|
||||
LOCAL_GL_INT, // RGBA8I,
|
||||
LOCAL_GL_UNSIGNED_INT, // RGBA8UI,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // SRGB8_ALPHA8,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // RGB10_A2,
|
||||
LOCAL_GL_UNSIGNED_INT, // RGB10_A2UI,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // RGBA4,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // RGB5_A1,
|
||||
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // RGB8,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // RGB565,
|
||||
|
||||
LOCAL_GL_INT, // RG32I,
|
||||
LOCAL_GL_UNSIGNED_INT, // RG32UI,
|
||||
LOCAL_GL_INT, // RG16I,
|
||||
LOCAL_GL_UNSIGNED_INT, // RG16UI,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // RG8,
|
||||
LOCAL_GL_INT, // RG8I,
|
||||
LOCAL_GL_UNSIGNED_INT, // RG8UI,
|
||||
|
||||
LOCAL_GL_INT, // R32I,
|
||||
LOCAL_GL_UNSIGNED_INT, // R32UI,
|
||||
LOCAL_GL_INT, // R16I,
|
||||
LOCAL_GL_UNSIGNED_INT, // R16UI,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // R8,
|
||||
LOCAL_GL_INT, // R8I,
|
||||
LOCAL_GL_UNSIGNED_INT, // R8UI,
|
||||
|
||||
// "Texture-only color formats"
|
||||
LOCAL_GL_FLOAT, // RGBA32F,
|
||||
LOCAL_GL_FLOAT, // RGBA16F,
|
||||
LOCAL_GL_SIGNED_NORMALIZED, // RGBA8_SNORM,
|
||||
|
||||
LOCAL_GL_FLOAT, // RGB32F,
|
||||
LOCAL_GL_INT, // RGB32I,
|
||||
LOCAL_GL_UNSIGNED_INT, // RGB32UI,
|
||||
|
||||
LOCAL_GL_FLOAT, // RGB16F,
|
||||
LOCAL_GL_INT, // RGB16I,
|
||||
LOCAL_GL_UNSIGNED_INT, // RGB16UI,
|
||||
|
||||
LOCAL_GL_SIGNED_NORMALIZED, // RGB8_SNORM,
|
||||
LOCAL_GL_INT, // RGB8I,
|
||||
LOCAL_GL_UNSIGNED_INT, // RGB8UI,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // SRGB8,
|
||||
|
||||
LOCAL_GL_FLOAT, // R11F_G11F_B10F,
|
||||
LOCAL_GL_FLOAT, // RGB9_E5,
|
||||
|
||||
LOCAL_GL_FLOAT, // RG32F,
|
||||
LOCAL_GL_FLOAT, // RG16F,
|
||||
LOCAL_GL_SIGNED_NORMALIZED, // RG8_SNORM,
|
||||
|
||||
LOCAL_GL_FLOAT, // R32F,
|
||||
LOCAL_GL_FLOAT, // R16F,
|
||||
LOCAL_GL_SIGNED_NORMALIZED, // R8_SNORM,
|
||||
|
||||
// "Depth formats"
|
||||
LOCAL_GL_FLOAT, // DEPTH_COMPONENT32F,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // DEPTH_COMPONENT24,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // DEPTH_COMPONENT16,
|
||||
|
||||
// "Combined depth+stencil formats"
|
||||
LOCAL_GL_FLOAT, // DEPTH32F_STENCIL8,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // DEPTH24_STENCIL8,
|
||||
|
||||
// GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // STENCIL_INDEX8,
|
||||
|
||||
// GLES 3.0.4, p128, table 3.12.
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // Luminance8Alpha8,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // Luminance8,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // Alpha8,
|
||||
|
||||
// GLES 3.0.4, p147, table 3.19
|
||||
// GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_R11_EAC,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SIGNED_R11_EAC,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RG11_EAC,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SIGNED_RG11_EAC,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB8_ETC2,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SRGB8_ETC2,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA8_ETC2_EAC,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SRGB8_ALPHA8_ETC2_EAC,
|
||||
|
||||
// AMD_compressed_ATC_texture
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // ATC_RGB_AMD,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // ATC_RGBA_EXPLICIT_ALPHA_AMD,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // ATC_RGBA_INTERPOLATED_ALPHA_AMD,
|
||||
|
||||
// EXT_texture_compression_s3tc
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB_S3TC_DXT1,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_S3TC_DXT1,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_S3TC_DXT3,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_S3TC_DXT5,
|
||||
|
||||
// IMG_texture_compression_pvrtc
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB_PVRTC_4BPPV1,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_PVRTC_4BPPV1,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB_PVRTC_2BPPV1,
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_PVRTC_2BPPV1,
|
||||
|
||||
// OES_compressed_ETC1_RGB8_texture
|
||||
LOCAL_GL_UNSIGNED_NORMALIZED, // ETC1_RGB8,
|
||||
|
||||
// OES_texture_float
|
||||
LOCAL_GL_FLOAT, // Luminance32FAlpha32F,
|
||||
LOCAL_GL_FLOAT, // Luminance32F,
|
||||
LOCAL_GL_FLOAT, // Alpha32F,
|
||||
|
||||
// OES_texture_half_float
|
||||
LOCAL_GL_FLOAT, // Luminance16FAlpha16F,
|
||||
LOCAL_GL_FLOAT, // Luminance16F,
|
||||
LOCAL_GL_FLOAT, // Alpha16F,
|
||||
|
||||
LOCAL_GL_NONE // MAX
|
||||
};
|
||||
|
||||
GLenum
|
||||
GetComponentType(EffectiveFormat format)
|
||||
{
|
||||
return kComponentTypes[(int) format];
|
||||
}
|
||||
|
||||
GLenum
|
||||
GetColorEncoding(EffectiveFormat format)
|
||||
{
|
||||
const bool isSRGB = (GetFormatInfo(format)->colorComponentType ==
|
||||
ComponentType::NormUIntSRGB);
|
||||
return (isSRGB) ? LOCAL_GL_SRGB : LOCAL_GL_LINEAR;
|
||||
}
|
||||
|
||||
} // namespace webgl
|
||||
} // namespace mozilla
|
||||
|
@ -252,13 +252,21 @@ public:
|
||||
EffectiveFormat effectiveFormat);
|
||||
|
||||
FormatUsageInfo* GetUsage(EffectiveFormat format);
|
||||
|
||||
FormatUsageInfo* GetUsage(const FormatInfo* format)
|
||||
{
|
||||
if (!format)
|
||||
return nullptr;
|
||||
|
||||
return GetUsage(format->effectiveFormat);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLint GetComponentSize(EffectiveFormat format, GLenum component);
|
||||
GLenum GetComponentType(EffectiveFormat format);
|
||||
GLenum GetColorEncoding(EffectiveFormat format);
|
||||
|
||||
} // namespace webgl
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -433,6 +433,66 @@ WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl,
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
JS::Value
|
||||
WebGLFBAttachPoint::GetParameter(WebGLContext* context, GLenum pname)
|
||||
{
|
||||
// TODO: WebGLTexture and WebGLRenderbuffer should store FormatInfo instead of doing
|
||||
// this dance every time.
|
||||
const GLenum internalFormat = EffectiveInternalFormat().get();
|
||||
const webgl::FormatInfo* info = webgl::GetInfoBySizedFormat(internalFormat);
|
||||
MOZ_ASSERT(info);
|
||||
|
||||
WebGLTexture* tex = Texture();
|
||||
|
||||
switch (pname) {
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
|
||||
return JS::Int32Value(webgl::GetComponentSize(info->effectiveFormat, pname));
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
|
||||
return JS::Int32Value(webgl::GetComponentType(info->effectiveFormat));
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
|
||||
return JS::Int32Value(webgl::GetColorEncoding(info->effectiveFormat));
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
|
||||
if (tex) {
|
||||
return JS::Int32Value(MipLevel());
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
|
||||
if (tex) {
|
||||
int32_t face = 0;
|
||||
if (tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) {
|
||||
face = ImageTarget().get();
|
||||
}
|
||||
return JS::Int32Value(face);
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
|
||||
if (tex) {
|
||||
int32_t layer = 0;
|
||||
if (tex->Target() == LOCAL_GL_TEXTURE_2D_ARRAY ||
|
||||
tex->Target() == LOCAL_GL_TEXTURE_3D)
|
||||
{
|
||||
layer = Layer();
|
||||
}
|
||||
return JS::Int32Value(layer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
context->ErrorInvalidEnum("getFramebufferParameter: Invalid combination of "
|
||||
"attachment and pname.");
|
||||
return JS::NullValue();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// WebGLFramebuffer
|
||||
|
||||
@ -987,6 +1047,110 @@ WebGLFramebuffer::ValidateForRead(const char* info, TexInternalFormat* const out
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
AttachmentsDontMatch(const WebGLFBAttachPoint& a, const WebGLFBAttachPoint& b)
|
||||
{
|
||||
if (a.Texture()) {
|
||||
return (a.Texture() != b.Texture());
|
||||
}
|
||||
|
||||
if (a.Renderbuffer()) {
|
||||
return (a.Renderbuffer() != b.Renderbuffer());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Value
|
||||
WebGLFramebuffer::GetAttachmentParameter(JSContext* cx,
|
||||
GLenum attachment,
|
||||
GLenum pname,
|
||||
ErrorResult& rv)
|
||||
{
|
||||
// "If a framebuffer object is bound to target, then attachment must be one of the
|
||||
// attachment points of the framebuffer listed in table 4.6."
|
||||
switch (attachment) {
|
||||
case LOCAL_GL_DEPTH_ATTACHMENT:
|
||||
case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
|
||||
break;
|
||||
|
||||
case LOCAL_GL_STENCIL_ATTACHMENT:
|
||||
// "If attachment is DEPTH_STENCIL_ATTACHMENT, and different objects are bound to
|
||||
// the depth and stencil attachment points of target, the query will fail and
|
||||
// generate an INVALID_OPERATION error. If the same object is bound to both
|
||||
// attachment points, information about that object will be returned."
|
||||
|
||||
// Does this mean it has to be the same level or layer? Because the queries are
|
||||
// independent of level or layer.
|
||||
if (AttachmentsDontMatch(DepthAttachment(), StencilAttachment())) {
|
||||
mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: "
|
||||
"DEPTH_ATTACHMENT and STENCIL_ATTACHMENT "
|
||||
"have different objects bound.");
|
||||
return JS::NullValue();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
|
||||
attachment > mContext->LastColorAttachment())
|
||||
{
|
||||
mContext->ErrorInvalidEnum("getFramebufferAttachmentParameter: Can only "
|
||||
"query COLOR_ATTACHMENTi, DEPTH_ATTACHMENT, "
|
||||
"DEPTH_STENCIL_ATTACHMENT, or STENCIL_ATTACHMENT "
|
||||
"on framebuffer.");
|
||||
return JS::NullValue();
|
||||
}
|
||||
}
|
||||
|
||||
if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT &&
|
||||
pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)
|
||||
{
|
||||
mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: Querying "
|
||||
"FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE against "
|
||||
"DEPTH_STENCIL_ATTACHMENT is an error.");
|
||||
return JS::NullValue();
|
||||
}
|
||||
|
||||
GLenum objectType = LOCAL_GL_NONE;
|
||||
auto& fba = GetAttachPoint(attachment);
|
||||
if (fba.Texture()) {
|
||||
objectType = LOCAL_GL_TEXTURE;
|
||||
} else if (fba.Renderbuffer()) {
|
||||
objectType = LOCAL_GL_RENDERBUFFER;
|
||||
}
|
||||
|
||||
switch (pname) {
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
|
||||
return JS::Int32Value(objectType);
|
||||
|
||||
case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
|
||||
if (objectType == LOCAL_GL_NONE) {
|
||||
return JS::NullValue();
|
||||
}
|
||||
|
||||
if (objectType == LOCAL_GL_RENDERBUFFER) {
|
||||
const WebGLRenderbuffer* rb = fba.Renderbuffer();
|
||||
return mContext->WebGLObjectAsJSValue(cx, rb, rv);
|
||||
}
|
||||
|
||||
/* If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE, then */
|
||||
if (objectType == LOCAL_GL_TEXTURE) {
|
||||
const WebGLTexture* tex = fba.Texture();
|
||||
return mContext->WebGLObjectAsJSValue(cx, tex, rv);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (objectType == LOCAL_GL_NONE) {
|
||||
mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: No "
|
||||
"attachment at %s",
|
||||
mContext->EnumName(attachment));
|
||||
return JS::NullValue();
|
||||
}
|
||||
|
||||
return fba.GetParameter(mContext, pname);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Goop.
|
||||
|
||||
|
@ -62,7 +62,7 @@ public:
|
||||
void SetTexImageLayer(WebGLTexture* tex, TexImageTarget target, GLint level,
|
||||
GLint layer);
|
||||
void SetRenderbuffer(WebGLRenderbuffer* rb);
|
||||
|
||||
|
||||
const WebGLTexture* Texture() const {
|
||||
return mTexturePtr;
|
||||
}
|
||||
@ -95,6 +95,8 @@ public:
|
||||
|
||||
void FinalizeAttachment(gl::GLContext* gl,
|
||||
FBAttachment attachmentLoc) const;
|
||||
|
||||
JS::Value GetParameter(WebGLContext* context, GLenum pname);
|
||||
};
|
||||
|
||||
class WebGLFramebuffer final
|
||||
@ -225,6 +227,9 @@ public:
|
||||
}
|
||||
|
||||
bool ValidateForRead(const char* info, TexInternalFormat* const out_format);
|
||||
|
||||
JS::Value GetAttachmentParameter(JSContext* cx, GLenum attachment, GLenum pname,
|
||||
ErrorResult& rv);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -19,8 +19,6 @@
|
||||
#include "WebGLObjectModel.h"
|
||||
|
||||
|
||||
template<class> class nsRefPtr;
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
class WebGLActiveInfo;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "mozilla/dom/FileList.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/OSFileSystem.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
||||
class nsINode;
|
||||
class nsITransferable;
|
||||
@ -36,6 +35,7 @@ namespace dom {
|
||||
class DOMStringList;
|
||||
class Element;
|
||||
class FileList;
|
||||
class Promise;
|
||||
template<typename T> class Optional;
|
||||
|
||||
/**
|
||||
|
@ -67,6 +67,23 @@ Touch::Touch(int32_t aIdentifier,
|
||||
nsJSContext::LikelyShortLivingObjectCreated();
|
||||
}
|
||||
|
||||
Touch::Touch(const Touch& aOther)
|
||||
: mTarget(aOther.mTarget)
|
||||
, mRefPoint(aOther.mRefPoint)
|
||||
, mChanged(aOther.mChanged)
|
||||
, mMessage(aOther.mMessage)
|
||||
, mIdentifier(aOther.mIdentifier)
|
||||
, mPagePoint(aOther.mPagePoint)
|
||||
, mClientPoint(aOther.mClientPoint)
|
||||
, mScreenPoint(aOther.mScreenPoint)
|
||||
, mRadius(aOther.mRadius)
|
||||
, mRotationAngle(aOther.mRotationAngle)
|
||||
, mForce(aOther.mForce)
|
||||
, mPointsInitialized(aOther.mPointsInitialized)
|
||||
{
|
||||
nsJSContext::LikelyShortLivingObjectCreated();
|
||||
}
|
||||
|
||||
Touch::~Touch()
|
||||
{
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ public:
|
||||
nsIntPoint aRadius,
|
||||
float aRotationAngle,
|
||||
float aForce);
|
||||
Touch(const Touch& aOther);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Touch)
|
||||
|
@ -186,10 +186,10 @@ TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
|
||||
int32_t flag = 0;
|
||||
if (NS_SUCCEEDED(Preferences::GetInt("dom.w3c_touch_events.enabled", &flag))) {
|
||||
if (flag == 2) {
|
||||
#ifdef XP_WIN
|
||||
#if defined(XP_WIN) || MOZ_WIDGET_GTK == 3
|
||||
static bool sDidCheckTouchDeviceSupport = false;
|
||||
static bool sIsTouchDeviceSupportPresent = false;
|
||||
// On Windows we auto-detect based on device support.
|
||||
// On Windows and GTK3 we auto-detect based on device support.
|
||||
if (!sDidCheckTouchDeviceSupport) {
|
||||
sDidCheckTouchDeviceSupport = true;
|
||||
sIsTouchDeviceSupportPresent = WidgetUtils::IsTouchDeviceSupportPresent();
|
||||
|
@ -78,6 +78,7 @@ UNIFIED_SOURCES += [
|
||||
'CommandEvent.cpp',
|
||||
'CompositionEvent.cpp',
|
||||
'ContentEventHandler.cpp',
|
||||
'CustomEvent.cpp',
|
||||
'DataContainerEvent.cpp',
|
||||
'DataTransfer.cpp',
|
||||
'DeviceMotionEvent.cpp',
|
||||
@ -117,7 +118,6 @@ UNIFIED_SOURCES += [
|
||||
|
||||
# nsEventStateManager.cpp should be built separately because of Mac OS X headers.
|
||||
SOURCES += [
|
||||
'CustomEvent.cpp',
|
||||
'EventStateManager.cpp',
|
||||
]
|
||||
|
||||
|
@ -50,9 +50,9 @@ FetchDriver::FetchDriver(InternalRequest* aRequest, nsIPrincipal* aPrincipal,
|
||||
: mPrincipal(aPrincipal)
|
||||
, mLoadGroup(aLoadGroup)
|
||||
, mRequest(aRequest)
|
||||
, mFetchRecursionCount(0)
|
||||
, mCORSFlagEverSet(false)
|
||||
, mHasBeenCrossSite(false)
|
||||
, mResponseAvailableCalled(false)
|
||||
, mFetchCalled(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -67,323 +67,104 @@ nsresult
|
||||
FetchDriver::Fetch(FetchDriverObserver* aObserver)
|
||||
{
|
||||
workers::AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!mFetchCalled);
|
||||
mFetchCalled = true;
|
||||
|
||||
mObserver = aObserver;
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REQUEST_PASSTHROUGH,
|
||||
mRequest->WasCreatedByFetchEvent());
|
||||
|
||||
return Fetch(false /* CORS flag */);
|
||||
// FIXME(nsm): Deal with HSTS.
|
||||
|
||||
MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
|
||||
"Synchronous fetch not supported");
|
||||
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(this, &FetchDriver::ContinueFetch);
|
||||
return NS_DispatchToCurrentThread(r);
|
||||
}
|
||||
|
||||
nsresult
|
||||
FetchDriver::Fetch(bool aCORSFlag)
|
||||
{
|
||||
// We do not currently implement parts of the spec that lead to recursion.
|
||||
MOZ_ASSERT(mFetchRecursionCount == 0);
|
||||
mFetchRecursionCount++;
|
||||
|
||||
// FIXME(nsm): Deal with HSTS.
|
||||
|
||||
if (!mRequest->IsSynchronous() && mFetchRecursionCount <= 1) {
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethodWithArg<bool>(this, &FetchDriver::ContinueFetch, aCORSFlag);
|
||||
nsresult rv = NS_DispatchToCurrentThread(r);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_CRASH("Synchronous fetch not supported");
|
||||
}
|
||||
|
||||
FetchDriver::MainFetchOp
|
||||
FetchDriver::SetTaintingAndGetNextOp(bool aCORSFlag)
|
||||
FetchDriver::SetTainting()
|
||||
{
|
||||
workers::AssertIsOnMainThread();
|
||||
|
||||
// If we've already been cross-site then we should be fully updated
|
||||
if (mHasBeenCrossSite) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString url;
|
||||
mRequest->GetURL(url);
|
||||
nsCOMPtr<nsIURI> requestURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url,
|
||||
nullptr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return MainFetchOp(NETWORK_ERROR);
|
||||
}
|
||||
|
||||
// CSP/mixed content checks.
|
||||
int16_t shouldLoad;
|
||||
rv = NS_CheckContentLoadPolicy(mRequest->ContentPolicyType(),
|
||||
requestURI,
|
||||
mPrincipal,
|
||||
mDocument,
|
||||
// FIXME(nsm): Should MIME be extracted from
|
||||
// Content-Type header?
|
||||
EmptyCString(), /* mime guess */
|
||||
nullptr, /* extra */
|
||||
&shouldLoad,
|
||||
nsContentUtils::GetContentPolicy(),
|
||||
nsContentUtils::GetSecurityManager());
|
||||
if (NS_WARN_IF(NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad))) {
|
||||
// Disallowed by content policy.
|
||||
return MainFetchOp(NETWORK_ERROR);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Begin Step 8 of the Main Fetch algorithm
|
||||
// https://fetch.spec.whatwg.org/#fetching
|
||||
|
||||
nsAutoCString scheme;
|
||||
rv = requestURI->GetScheme(scheme);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return MainFetchOp(NETWORK_ERROR);
|
||||
}
|
||||
|
||||
// request's current url's origin is request's origin and the CORS flag is unset
|
||||
// request's current url's scheme is "data" and request's same-origin data-URL flag is set
|
||||
// request's current url's scheme is "about"
|
||||
rv = mPrincipal->CheckMayLoad(requestURI, false /* report */,
|
||||
false /* allowIfInheritsPrincipal */);
|
||||
if ((!aCORSFlag && NS_SUCCEEDED(rv)) ||
|
||||
(scheme.EqualsLiteral("data") && mRequest->SameOriginDataURL()) ||
|
||||
scheme.EqualsLiteral("about")) {
|
||||
return MainFetchOp(BASIC_FETCH);
|
||||
|
||||
// We have to manually check about:blank here since it's not treated as
|
||||
// an inheriting URL by CheckMayLoad.
|
||||
if (NS_IsAboutBlank(requestURI) ||
|
||||
NS_SUCCEEDED(mPrincipal->CheckMayLoad(requestURI, false /* report */,
|
||||
true /*allowIfInheritsPrincipal*/))) {
|
||||
// What the spec calls "basic fetch" is handled within our necko channel
|
||||
// code. Therefore everything goes through HTTP Fetch
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mHasBeenCrossSite = true;
|
||||
|
||||
// request's mode is "same-origin"
|
||||
if (mRequest->Mode() == RequestMode::Same_origin) {
|
||||
return MainFetchOp(NETWORK_ERROR);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// request's mode is "no-cors"
|
||||
if (mRequest->Mode() == RequestMode::No_cors) {
|
||||
mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUE);
|
||||
return MainFetchOp(BASIC_FETCH);
|
||||
}
|
||||
|
||||
// request's current url's scheme is not one of "http" and "https"
|
||||
if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
|
||||
return MainFetchOp(NETWORK_ERROR);
|
||||
}
|
||||
|
||||
// request's mode is "cors-with-forced-preflight"
|
||||
// request's unsafe-request flag is set and either request's method is not
|
||||
// a simple method or a header in request's header list is not a simple header
|
||||
if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
|
||||
(mRequest->UnsafeRequest() && (!mRequest->HasSimpleMethod() ||
|
||||
!mRequest->Headers()->HasOnlySimpleHeaders()))) {
|
||||
mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS);
|
||||
mRequest->SetRedirectMode(RequestRedirect::Error);
|
||||
|
||||
// Note, the following text from Main Fetch step 8 is handled in
|
||||
// nsCORSListenerProxy when CheckRequestApproved() fails:
|
||||
//
|
||||
// The result of performing an HTTP fetch using request with the CORS
|
||||
// flag and CORS-preflight flag set. If the result is a network error,
|
||||
// clear cache entries using request.
|
||||
|
||||
return MainFetchOp(HTTP_FETCH, true /* cors */, true /* preflight */);
|
||||
// What the spec calls "basic fetch" is handled within our necko channel
|
||||
// code. Therefore everything goes through HTTP Fetch
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Otherwise
|
||||
mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS);
|
||||
return MainFetchOp(HTTP_FETCH, true /* cors */, false /* preflight */);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FetchDriver::ContinueFetch(bool aCORSFlag)
|
||||
FetchDriver::ContinueFetch()
|
||||
{
|
||||
workers::AssertIsOnMainThread();
|
||||
|
||||
MainFetchOp nextOp = SetTaintingAndGetNextOp(aCORSFlag);
|
||||
|
||||
if (nextOp.mType == NETWORK_ERROR) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
if (nextOp.mType == BASIC_FETCH) {
|
||||
return BasicFetch();
|
||||
}
|
||||
|
||||
if (nextOp.mType == HTTP_FETCH) {
|
||||
return HttpFetch(nextOp.mCORSFlag, nextOp.mCORSPreflightFlag);
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected main fetch operation!");
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
nsresult
|
||||
FetchDriver::BasicFetch()
|
||||
{
|
||||
nsAutoCString url;
|
||||
mRequest->GetURL(url);
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri),
|
||||
url,
|
||||
nullptr,
|
||||
nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsresult rv = HttpFetch();
|
||||
if (NS_FAILED(rv)) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoCString scheme;
|
||||
rv = uri->GetScheme(scheme);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (scheme.LowerCaseEqualsLiteral("about")) {
|
||||
if (url.EqualsLiteral("about:blank")) {
|
||||
RefPtr<InternalResponse> response =
|
||||
new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
|
||||
ErrorResult result;
|
||||
response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
|
||||
NS_LITERAL_CSTRING("text/html;charset=utf-8"),
|
||||
result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
nsCOMPtr<nsIInputStream> body;
|
||||
rv = NS_NewCStringInputStream(getter_AddRefs(body), EmptyCString());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
response->SetBody(body);
|
||||
BeginResponse(response);
|
||||
return SucceedWithResponse();
|
||||
}
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
if (scheme.LowerCaseEqualsLiteral("blob")) {
|
||||
RefPtr<BlobImpl> blobImpl;
|
||||
rv = NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl));
|
||||
BlobImpl* blob = static_cast<BlobImpl*>(blobImpl.get());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
RefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
|
||||
ErrorResult result;
|
||||
uint64_t size = blob->GetSize(result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
FailWithNetworkError();
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
nsAutoString sizeStr;
|
||||
sizeStr.AppendInt(size);
|
||||
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), NS_ConvertUTF16toUTF8(sizeStr), result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
FailWithNetworkError();
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
nsAutoString type;
|
||||
blob->GetType(type);
|
||||
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), NS_ConvertUTF16toUTF8(type), result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
FailWithNetworkError();
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
blob->GetInternalStream(getter_AddRefs(stream), result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
FailWithNetworkError();
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
response->SetBody(stream);
|
||||
BeginResponse(response);
|
||||
return SucceedWithResponse();
|
||||
}
|
||||
|
||||
if (scheme.LowerCaseEqualsLiteral("data")) {
|
||||
nsAutoCString method;
|
||||
mRequest->GetMethod(method);
|
||||
if (method.LowerCaseEqualsASCII("get")) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIProtocolHandler> dataHandler =
|
||||
do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "data", &rv);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = dataHandler->NewChannel(uri, getter_AddRefs(channel));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = channel->Open(getter_AddRefs(stream));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
// nsDataChannel will parse the data URI when it is Open()ed and set the
|
||||
// correct content type and charset.
|
||||
nsAutoCString contentType;
|
||||
if (NS_SUCCEEDED(channel->GetContentType(contentType))) {
|
||||
nsAutoCString charset;
|
||||
if (NS_SUCCEEDED(channel->GetContentCharset(charset)) && !charset.IsEmpty()) {
|
||||
contentType.AppendLiteral(";charset=");
|
||||
contentType.Append(charset);
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Could not get content type from data channel");
|
||||
}
|
||||
|
||||
RefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
|
||||
ErrorResult result;
|
||||
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
FailWithNetworkError();
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
response->SetBody(stream);
|
||||
BeginResponse(response);
|
||||
return SucceedWithResponse();
|
||||
}
|
||||
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
||||
if (scheme.LowerCaseEqualsLiteral("http") ||
|
||||
scheme.LowerCaseEqualsLiteral("https") ||
|
||||
scheme.LowerCaseEqualsLiteral("app")) {
|
||||
return HttpFetch();
|
||||
}
|
||||
|
||||
return FailWithNetworkError();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// This function implements the "HTTP Fetch" algorithm from the Fetch spec.
|
||||
// Functionality is often split between here, the CORS listener proxy and the
|
||||
// Necko HTTP implementation.
|
||||
nsresult
|
||||
FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthenticationFlag)
|
||||
FetchDriver::HttpFetch()
|
||||
{
|
||||
// Step 1. "Let response be null."
|
||||
mResponse = nullptr;
|
||||
nsresult rv;
|
||||
|
||||
// We need to track the CORS flag through redirects. Since there is no way
|
||||
// for us to go from CORS mode to non-CORS mode, we just need to remember
|
||||
// if it has ever been set.
|
||||
mCORSFlagEverSet = mCORSFlagEverSet || aCORSFlag;
|
||||
|
||||
nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString url;
|
||||
mRequest->GetURL(url);
|
||||
@ -393,10 +174,10 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
nullptr,
|
||||
nullptr,
|
||||
ios);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = SetTainting();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Step 2 deals with letting ServiceWorkers intercept requests. This is
|
||||
// handled by Necko after the channel is opened.
|
||||
@ -419,19 +200,18 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
// - request's credentials mode is "same-origin" and either the CORS flag
|
||||
// is unset or response tainting is "opaque"
|
||||
// is true, and unset otherwise."
|
||||
bool useCredentials = false;
|
||||
if (mRequest->GetCredentialsMode() == RequestCredentials::Include ||
|
||||
(mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && !aCORSFlag &&
|
||||
mRequest->GetResponseTainting() != InternalRequest::RESPONSETAINT_OPAQUE)) {
|
||||
useCredentials = true;
|
||||
}
|
||||
|
||||
// This is effectivetly the opposite of the use credentials flag in "HTTP
|
||||
// network or cache fetch" in the spec and decides whether to transmit
|
||||
// cookies and other identifying information. LOAD_ANONYMOUS also prevents
|
||||
// new cookies sent by the server from being stored. This value will
|
||||
// propagate across redirects, which is what we want.
|
||||
const nsLoadFlags credentialsFlag = useCredentials ? 0 : nsIRequest::LOAD_ANONYMOUS;
|
||||
const nsLoadFlags credentialsFlag =
|
||||
(mRequest->GetCredentialsMode() == RequestCredentials::Omit ||
|
||||
(mHasBeenCrossSite &&
|
||||
mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
|
||||
mRequest->Mode() == RequestMode::No_cors)) ?
|
||||
nsIRequest::LOAD_ANONYMOUS : 0;
|
||||
|
||||
// Set skip serviceworker flag.
|
||||
// While the spec also gates on the client being a ServiceWorker, we can't
|
||||
@ -439,24 +219,56 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ?
|
||||
nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0;
|
||||
|
||||
nsSecurityFlags secFlags;
|
||||
if (mRequest->Mode() == RequestMode::Cors &&
|
||||
mRequest->GetCredentialsMode() == RequestCredentials::Include) {
|
||||
secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
|
||||
nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
|
||||
} else if (mRequest->Mode() == RequestMode::Cors) {
|
||||
secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
|
||||
} else if (mRequest->Mode() == RequestMode::Same_origin) {
|
||||
secFlags = nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
|
||||
} else if (mRequest->Mode() == RequestMode::No_cors) {
|
||||
secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// From here on we create a channel and set its properties with the
|
||||
// information from the InternalRequest. This is an implementation detail.
|
||||
MOZ_ASSERT(mLoadGroup);
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
uri,
|
||||
mPrincipal,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
mRequest->ContentPolicyType(),
|
||||
mLoadGroup,
|
||||
nullptr, /* aCallbacks */
|
||||
nsIRequest::LOAD_NORMAL | credentialsFlag | bypassFlag,
|
||||
ios);
|
||||
mLoadGroup = nullptr;
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
|
||||
// For dedicated workers mDocument refers to the parent document of the
|
||||
// worker (why do we do that?). In that case we don't want to use the
|
||||
// document here since that is not the correct principal.
|
||||
if (mDocument && mDocument->NodePrincipal() == mPrincipal) {
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
uri,
|
||||
mDocument,
|
||||
secFlags |
|
||||
nsILoadInfo::SEC_ABOUT_BLANK_INHERITS,
|
||||
mRequest->ContentPolicyType(),
|
||||
mLoadGroup,
|
||||
nullptr, /* aCallbacks */
|
||||
nsIRequest::LOAD_NORMAL | credentialsFlag | bypassFlag,
|
||||
ios);
|
||||
} else {
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
uri,
|
||||
mPrincipal,
|
||||
secFlags |
|
||||
nsILoadInfo::SEC_ABOUT_BLANK_INHERITS,
|
||||
mRequest->ContentPolicyType(),
|
||||
mLoadGroup,
|
||||
nullptr, /* aCallbacks */
|
||||
nsIRequest::LOAD_NORMAL | credentialsFlag | bypassFlag,
|
||||
ios);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mLoadGroup = nullptr;
|
||||
|
||||
// Insert ourselves into the notification callbacks chain so we can handle
|
||||
// cross-origin redirects.
|
||||
@ -485,10 +297,7 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
nsAutoCString method;
|
||||
mRequest->GetMethod(method);
|
||||
rv = httpChan->SetRequestMethod(method);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set the same headers.
|
||||
nsAutoTArray<InternalHeaders::Entry, 5> headers;
|
||||
@ -508,14 +317,10 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
|
||||
mDocument,
|
||||
httpChan);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (referrer.IsEmpty()) {
|
||||
rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
// From "Determine request's Referrer" step 3
|
||||
// "If request's referrer is a URL, let referrerSource be request's
|
||||
@ -527,26 +332,21 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
// someone tries to use FetchDriver for non-fetch() APIs?
|
||||
nsCOMPtr<nsIURI> referrerURI;
|
||||
rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv =
|
||||
httpChan->SetReferrerWithPolicy(referrerURI,
|
||||
mDocument ? mDocument->GetReferrerPolicy() :
|
||||
net::RP_Default);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Step 3 "If HTTPRequest's force Origin header flag is set..."
|
||||
if (mRequest->ForceOriginHeader()) {
|
||||
nsAutoString origin;
|
||||
rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
httpChan->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
|
||||
NS_ConvertUTF16toUTF8(origin),
|
||||
false /* merge */);
|
||||
@ -577,7 +377,7 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
// This is an error because the Request constructor explicitly extracts and
|
||||
// sets a content-type per spec.
|
||||
if (result.Failed()) {
|
||||
return FailWithNetworkError();
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> bodyStream;
|
||||
@ -586,77 +386,49 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
nsAutoCString method;
|
||||
mRequest->GetMethod(method);
|
||||
rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener = this;
|
||||
|
||||
MOZ_ASSERT_IF(aCORSFlag, mRequest->Mode() == RequestMode::Cors);
|
||||
|
||||
// Only use nsCORSListenerProxy if we are in CORS mode. Otherwise it
|
||||
// will overwrite the CorsMode flag unconditionally to "cors" or
|
||||
// "cors-with-forced-preflight".
|
||||
if (mRequest->Mode() == RequestMode::Cors) {
|
||||
// Passing false for the credentials flag to nsCORSListenerProxy is semantically
|
||||
// the same as the "same-origin" RequestCredentials value. We implement further
|
||||
// blocking of credentials for "omit" by setting LOAD_ANONYMOUS manually above.
|
||||
bool corsCredentials =
|
||||
mRequest->GetCredentialsMode() == RequestCredentials::Include;
|
||||
|
||||
// Set up a CORS proxy that will handle the various requirements of the CORS
|
||||
// protocol. It handles the preflight cache and CORS response headers.
|
||||
// If the request is allowed, it will start our original request
|
||||
// and our observer will be notified. On failure, our observer is notified
|
||||
// directly.
|
||||
RefPtr<nsCORSListenerProxy> corsListener =
|
||||
new nsCORSListenerProxy(this, mPrincipal, corsCredentials);
|
||||
rv = corsListener->Init(chan, DataURIHandling::Allow);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
listener = corsListener.forget();
|
||||
}
|
||||
|
||||
// If preflight is required, start a "CORS preflight fetch"
|
||||
// https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
|
||||
// implementation is handled by NS_StartCORSPreflight, we just set up the
|
||||
// unsafeHeaders so they can be verified against the response's
|
||||
// "Access-Control-Allow-Headers" header.
|
||||
if (aCORSPreflightFlag) {
|
||||
MOZ_ASSERT(mRequest->Mode() != RequestMode::No_cors,
|
||||
"FetchDriver::ContinueFetch() should ensure that the request is not no-cors");
|
||||
MOZ_ASSERT(httpChan, "CORS preflight can only be used with HTTP channels");
|
||||
// implementation is handled by the http channel calling into
|
||||
// nsCORSListenerProxy. We just inform it which unsafe headers are included
|
||||
// in the request.
|
||||
if (IsUnsafeRequest()) {
|
||||
if (mRequest->Mode() == RequestMode::No_cors) {
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
mRequest->SetRedirectMode(RequestRedirect::Error);
|
||||
|
||||
nsAutoTArray<nsCString, 5> unsafeHeaders;
|
||||
mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
|
||||
rv = internalChan->SetCorsPreflightParameters(unsafeHeaders, useCredentials, mPrincipal);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
NS_ENSURE_TRUE(internalChan, NS_ERROR_DOM_BAD_URI);
|
||||
|
||||
rv = internalChan->SetCorsPreflightParameters(
|
||||
unsafeHeaders,
|
||||
mRequest->GetCredentialsMode() == RequestCredentials::Include,
|
||||
mPrincipal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
rv = chan->AsyncOpen(listener, nullptr);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
rv = chan->AsyncOpen2(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FetchDriver::ContinueHttpFetchAfterNetworkFetch()
|
||||
bool
|
||||
FetchDriver::IsUnsafeRequest()
|
||||
{
|
||||
workers::AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mResponse);
|
||||
MOZ_ASSERT(!mResponse->IsError());
|
||||
|
||||
return SucceedWithResponse();
|
||||
return mHasBeenCrossSite &&
|
||||
(mRequest->UnsafeRequest() &&
|
||||
(!mRequest->HasSimpleMethod() ||
|
||||
!mRequest->Headers()->HasOnlySimpleHeaders()));
|
||||
}
|
||||
|
||||
already_AddRefed<InternalResponse>
|
||||
@ -699,24 +471,6 @@ FetchDriver::BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aF
|
||||
return filteredResponse.forget();
|
||||
}
|
||||
|
||||
void
|
||||
FetchDriver::BeginResponse(InternalResponse* aResponse)
|
||||
{
|
||||
RefPtr<InternalResponse> r = BeginAndGetFilteredResponse(aResponse, nullptr);
|
||||
// Release the ref.
|
||||
}
|
||||
|
||||
nsresult
|
||||
FetchDriver::SucceedWithResponse()
|
||||
{
|
||||
workers::AssertIsOnMainThread();
|
||||
if (mObserver) {
|
||||
mObserver->OnResponseEnd();
|
||||
mObserver = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FetchDriver::FailWithNetworkError()
|
||||
{
|
||||
@ -785,7 +539,10 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
|
||||
MOZ_ASSERT(mObserver);
|
||||
|
||||
RefPtr<InternalResponse> response;
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
|
||||
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aRequest);
|
||||
|
||||
if (httpChannel) {
|
||||
uint32_t responseStatus;
|
||||
httpChannel->GetResponseStatus(&responseStatus);
|
||||
@ -800,11 +557,7 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
NS_WARNING("Failed to visit all headers.");
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aRequest);
|
||||
// If it is not an http channel, it has to be a jar one.
|
||||
MOZ_ASSERT(jarChannel);
|
||||
|
||||
} else if (jarChannel) {
|
||||
// We simulate the http protocol for jar/app requests
|
||||
uint32_t responseStatus = 200;
|
||||
nsAutoCString statusText;
|
||||
@ -816,6 +569,35 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
|
||||
contentType,
|
||||
result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
} else {
|
||||
response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
|
||||
|
||||
ErrorResult result;
|
||||
nsAutoCString contentType;
|
||||
rv = channel->GetContentType(contentType);
|
||||
if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
|
||||
nsAutoCString contentCharset;
|
||||
channel->GetContentCharset(contentCharset);
|
||||
if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
|
||||
contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
|
||||
}
|
||||
|
||||
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
|
||||
contentType,
|
||||
result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
}
|
||||
|
||||
int64_t contentLength;
|
||||
rv = channel->GetContentLength(&contentLength);
|
||||
if (NS_SUCCEEDED(rv) && contentLength) {
|
||||
nsAutoCString contentLenStr;
|
||||
contentLenStr.AppendInt(contentLength);
|
||||
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
|
||||
contentLenStr,
|
||||
result);
|
||||
MOZ_ASSERT(!result.Failed());
|
||||
}
|
||||
}
|
||||
|
||||
// We open a pipe so that we can immediately set the pipe's read end as the
|
||||
@ -838,7 +620,6 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
|
||||
}
|
||||
response->SetBody(pipeInputStream);
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
response->InitChannelInfo(channel);
|
||||
|
||||
nsCOMPtr<nsIURI> channelURI;
|
||||
@ -899,17 +680,23 @@ FetchDriver::OnStopRequest(nsIRequest* aRequest,
|
||||
if (outputStream) {
|
||||
outputStream->CloseWithStatus(NS_BINDING_FAILED);
|
||||
}
|
||||
|
||||
// We proceed as usual here, since we've already created a successful response
|
||||
// from OnStartRequest.
|
||||
SucceedWithResponse();
|
||||
return aStatusCode;
|
||||
} else {
|
||||
MOZ_ASSERT(mResponse);
|
||||
MOZ_ASSERT(!mResponse->IsError());
|
||||
|
||||
if (mPipeOutputStream) {
|
||||
mPipeOutputStream->Close();
|
||||
}
|
||||
}
|
||||
|
||||
if (mPipeOutputStream) {
|
||||
mPipeOutputStream->Close();
|
||||
if (mObserver) {
|
||||
mObserver->OnResponseEnd();
|
||||
mObserver = nullptr;
|
||||
}
|
||||
|
||||
ContinueHttpFetchAfterNetworkFetch();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -922,6 +709,12 @@ FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
|
||||
{
|
||||
NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
|
||||
|
||||
if (NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) ||
|
||||
NS_IsHSTSUpgradeRedirect(aOldChannel, aNewChannel, aFlags)) {
|
||||
aCallback->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// HTTP Fetch step 5, "redirect status", step 1
|
||||
if (NS_WARN_IF(mRequest->GetRedirectMode() == RequestRedirect::Error)) {
|
||||
aOldChannel->Cancel(NS_BINDING_FAILED);
|
||||
@ -936,10 +729,8 @@ FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
|
||||
// count are done by Necko. The pref used is "network.http.redirection-limit"
|
||||
// which is set to 20 by default.
|
||||
|
||||
// HTTP Fetch Step 9, "redirect status". We only unset this for spec
|
||||
// compatibility. Any actions we take on mRequest here do not affect what the
|
||||
//channel does.
|
||||
mRequest->UnsetSameOriginDataURL();
|
||||
// HTTP Fetch Step 9, "redirect status". This is enforced by the
|
||||
// nsCORSListenerProxy. It forbids redirecting to data:
|
||||
|
||||
// HTTP Fetch step 5, "redirect status", step 10 requires us to halt the
|
||||
// redirect, but successfully return an opaqueredirect Response to the
|
||||
@ -994,63 +785,68 @@ FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
|
||||
mRequest->SetURL(newUrl);
|
||||
|
||||
// Implement Main Fetch step 8 again on redirect.
|
||||
MainFetchOp nextOp = SetTaintingAndGetNextOp(mCORSFlagEverSet);
|
||||
|
||||
if (nextOp.mType == NETWORK_ERROR) {
|
||||
// Cancel the channel if Main Fetch blocks the redirect from continuing.
|
||||
aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// Otherwise, we rely on necko and the CORS proxy to do the right thing
|
||||
// as the redirect is followed. In general this means basic or http
|
||||
// fetch. If we've ever been CORS, we need to stay CORS.
|
||||
MOZ_ASSERT(nextOp.mType == BASIC_FETCH || nextOp.mType == HTTP_FETCH);
|
||||
MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mType == HTTP_FETCH);
|
||||
MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mCORSFlag);
|
||||
|
||||
// Examine and possibly set the LOAD_ANONYMOUS flag on the channel.
|
||||
nsLoadFlags flags;
|
||||
rv = aNewChannel->GetLoadFlags(&flags);
|
||||
rv = SetTainting();
|
||||
if (NS_FAILED(rv)) {
|
||||
aOldChannel->Cancel(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
|
||||
mRequest->GetResponseTainting() == InternalRequest::RESPONSETAINT_OPAQUE) {
|
||||
// Requests that require preflight are not permitted to redirect.
|
||||
// Fetch spec section 4.2 "HTTP Fetch", step 4.9 just uses the manual
|
||||
// redirect flag to decide whether to execute step 4.10 or not. We do not
|
||||
// represent it in our implementation.
|
||||
// The only thing we do is to check if the request requires a preflight (part
|
||||
// of step 4.9), in which case we abort. This part cannot be done by
|
||||
// nsCORSListenerProxy since it does not have access to mRequest.
|
||||
// which case. Step 4.10.3 is handled by OnRedirectVerifyCallback(), and all
|
||||
// the other steps are handled by nsCORSListenerProxy.
|
||||
|
||||
if (IsUnsafeRequest()) {
|
||||
// We can't handle redirects that require preflight yet.
|
||||
// This is especially true for no-cors requests, which much always be
|
||||
// blocked if they require preflight.
|
||||
|
||||
// Simply fire an error here.
|
||||
aOldChannel->Cancel(NS_BINDING_FAILED);
|
||||
return NS_BINDING_FAILED;
|
||||
}
|
||||
|
||||
// Otherwise, we rely on necko and the CORS proxy to do the right thing
|
||||
// as the redirect is followed. In general this means http
|
||||
// fetch. If we've ever been CORS, we need to stay CORS.
|
||||
|
||||
// Possibly set the LOAD_ANONYMOUS flag on the channel.
|
||||
if (mHasBeenCrossSite &&
|
||||
mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
|
||||
mRequest->Mode() == RequestMode::No_cors) {
|
||||
// In the case of a "no-cors" mode request with "same-origin" credentials,
|
||||
// we have to set LOAD_ANONYMOUS manually here in order to avoid sending
|
||||
// credentials on a cross-origin redirect.
|
||||
flags |= nsIRequest::LOAD_ANONYMOUS;
|
||||
rv = aNewChannel->SetLoadFlags(flags);
|
||||
nsLoadFlags flags;
|
||||
rv = aNewChannel->GetLoadFlags(&flags);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
flags |= nsIRequest::LOAD_ANONYMOUS;
|
||||
rv = aNewChannel->SetLoadFlags(flags);
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
aOldChannel->Cancel(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
} else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) {
|
||||
// Make sure nothing in the redirect chain screws up our credentials
|
||||
// settings. LOAD_ANONYMOUS must be set if we RequestCredentials is "omit".
|
||||
MOZ_ASSERT(flags & nsIRequest::LOAD_ANONYMOUS);
|
||||
|
||||
} else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin &&
|
||||
nextOp.mCORSFlag) {
|
||||
// We also want to verify the LOAD_ANONYMOUS flag is set when we are in
|
||||
// "same-origin" credentials mode and the CORS flag is set. We can't
|
||||
// unconditionally assert here, however, because the nsCORSListenerProxy
|
||||
// will set the flag later in the redirect callback chain. Instead,
|
||||
// perform a weaker assertion here by checking if CORS flag was set
|
||||
// before this redirect. In that case LOAD_ANONYMOUS must still be set.
|
||||
MOZ_ASSERT_IF(mCORSFlagEverSet, flags & nsIRequest::LOAD_ANONYMOUS);
|
||||
|
||||
} else {
|
||||
// Otherwise, we should be sending credentials
|
||||
MOZ_ASSERT(!(flags & nsIRequest::LOAD_ANONYMOUS));
|
||||
}
|
||||
|
||||
// Track the CORSFlag through redirects.
|
||||
mCORSFlagEverSet = mCORSFlagEverSet || nextOp.mCORSFlag;
|
||||
#ifdef DEBUG
|
||||
{
|
||||
// Make sure nothing in the redirect chain screws up our credentials
|
||||
// settings. LOAD_ANONYMOUS must be set if we RequestCredentials is "omit"
|
||||
// or "same-origin".
|
||||
nsLoadFlags flags;
|
||||
aNewChannel->GetLoadFlags(&flags);
|
||||
bool shouldBeAnon =
|
||||
mRequest->GetCredentialsMode() == RequestCredentials::Omit ||
|
||||
(mHasBeenCrossSite &&
|
||||
mRequest->GetCredentialsMode() == RequestCredentials::Same_origin);
|
||||
MOZ_ASSERT(!!(flags & nsIRequest::LOAD_ANONYMOUS) == shouldBeAnon);
|
||||
}
|
||||
#endif
|
||||
|
||||
aCallback->OnRedirectVerifyCallback(NS_OK);
|
||||
|
||||
@ -1063,33 +859,6 @@ FetchDriver::CheckListenerChain()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Returns NS_OK if no preflight is required, error otherwise.
|
||||
nsresult
|
||||
FetchDriver::DoesNotRequirePreflight(nsIChannel* aChannel)
|
||||
{
|
||||
// If this is a same-origin request or the channel's URI inherits
|
||||
// its principal, it's allowed.
|
||||
if (nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Check if we need to do a preflight request.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
||||
NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
|
||||
|
||||
nsAutoCString method;
|
||||
httpChannel->GetRequestMethod(method);
|
||||
if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
|
||||
!mRequest->Headers()->HasOnlySimpleHeaders() ||
|
||||
(!method.LowerCaseEqualsLiteral("get") &&
|
||||
!method.LowerCaseEqualsLiteral("post") &&
|
||||
!method.LowerCaseEqualsLiteral("head"))) {
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchDriver::GetInterface(const nsIID& aIID, void **aResult)
|
||||
{
|
||||
@ -1117,7 +886,7 @@ void
|
||||
FetchDriver::SetDocument(nsIDocument* aDocument)
|
||||
{
|
||||
// Cannot set document after Fetch() has been called.
|
||||
MOZ_ASSERT(mFetchRecursionCount == 0);
|
||||
MOZ_ASSERT(!mFetchCalled);
|
||||
mDocument = aDocument;
|
||||
}
|
||||
|
||||
|
@ -82,53 +82,27 @@ private:
|
||||
nsCOMPtr<nsIOutputStream> mPipeOutputStream;
|
||||
RefPtr<FetchDriverObserver> mObserver;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
uint32_t mFetchRecursionCount;
|
||||
bool mCORSFlagEverSet;
|
||||
bool mHasBeenCrossSite;
|
||||
|
||||
DebugOnly<bool> mResponseAvailableCalled;
|
||||
DebugOnly<bool> mFetchCalled;
|
||||
|
||||
FetchDriver() = delete;
|
||||
FetchDriver(const FetchDriver&) = delete;
|
||||
FetchDriver& operator=(const FetchDriver&) = delete;
|
||||
~FetchDriver();
|
||||
|
||||
enum MainFetchOpType
|
||||
{
|
||||
NETWORK_ERROR,
|
||||
BASIC_FETCH,
|
||||
HTTP_FETCH,
|
||||
NUM_MAIN_FETCH_OPS
|
||||
};
|
||||
|
||||
struct MainFetchOp
|
||||
{
|
||||
explicit MainFetchOp(MainFetchOpType aType, bool aCORSFlag = false,
|
||||
bool aCORSPreflightFlag = false)
|
||||
: mType(aType), mCORSFlag(aCORSFlag),
|
||||
mCORSPreflightFlag(aCORSPreflightFlag)
|
||||
{ }
|
||||
|
||||
MainFetchOpType mType;
|
||||
bool mCORSFlag;
|
||||
bool mCORSPreflightFlag;
|
||||
};
|
||||
|
||||
nsresult Fetch(bool aCORSFlag);
|
||||
MainFetchOp SetTaintingAndGetNextOp(bool aCORSFlag);
|
||||
nsresult ContinueFetch(bool aCORSFlag);
|
||||
nsresult BasicFetch();
|
||||
nsresult HttpFetch(bool aCORSFlag = false, bool aCORSPreflightFlag = false, bool aAuthenticationFlag = false);
|
||||
nsresult ContinueHttpFetchAfterNetworkFetch();
|
||||
nsresult SetTainting();
|
||||
nsresult ContinueFetch();
|
||||
nsresult HttpFetch();
|
||||
bool IsUnsafeRequest();
|
||||
// Returns the filtered response sent to the observer.
|
||||
// Callers who don't have access to a channel can pass null for aFinalURI.
|
||||
already_AddRefed<InternalResponse>
|
||||
BeginAndGetFilteredResponse(InternalResponse* aResponse, nsIURI* aFinalURI);
|
||||
// Utility since not all cases need to do any post processing of the filtered
|
||||
// response.
|
||||
void BeginResponse(InternalResponse* aResponse);
|
||||
nsresult FailWithNetworkError();
|
||||
nsresult SucceedWithResponse();
|
||||
nsresult DoesNotRequirePreflight(nsIChannel* aChannel);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -260,16 +260,6 @@ Request::Constructor(const GlobalObject& aGlobal,
|
||||
fallbackCache = RequestCache::Default;
|
||||
}
|
||||
|
||||
// CORS-with-forced-preflight is not publicly exposed and should not be
|
||||
// considered a valid value.
|
||||
if (aInit.mMode.WasPassed() &&
|
||||
aInit.mMode.Value() == RequestMode::Cors_with_forced_preflight) {
|
||||
NS_NAMED_LITERAL_STRING(sourceDescription, "'mode' member of RequestInit");
|
||||
NS_NAMED_LITERAL_STRING(value, "cors-with-forced-preflight");
|
||||
NS_NAMED_LITERAL_STRING(type, "RequestMode");
|
||||
aRv.ThrowTypeError<MSG_INVALID_ENUM_VALUE>(&sourceDescription, &value, &type);
|
||||
return nullptr;
|
||||
}
|
||||
RequestMode mode = aInit.mMode.WasPassed() ? aInit.mMode.Value() : fallbackMode;
|
||||
RequestCredentials credentials =
|
||||
aInit.mCredentials.WasPassed() ? aInit.mCredentials.Value()
|
||||
|
@ -58,9 +58,6 @@ public:
|
||||
RequestMode
|
||||
Mode() const
|
||||
{
|
||||
if (mRequest->mMode == RequestMode::Cors_with_forced_preflight) {
|
||||
return RequestMode::Cors;
|
||||
}
|
||||
return mRequest->mMode;
|
||||
}
|
||||
|
||||
|
@ -2221,13 +2221,22 @@ HTMLMediaElement::ResetConnectionState()
|
||||
|
||||
void
|
||||
HTMLMediaElement::Play(ErrorResult& aRv)
|
||||
{
|
||||
nsresult rv = PlayInternal(nsContentUtils::IsCallerChrome());
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLMediaElement::PlayInternal(bool aCallerIsChrome)
|
||||
{
|
||||
// Prevent media element from being auto-started by a script when
|
||||
// media.autoplay.enabled=false
|
||||
if (!mHasUserInteraction
|
||||
&& !IsAutoplayEnabled()
|
||||
&& !EventStateManager::IsHandlingUserInput()
|
||||
&& !nsContentUtils::IsCallerChrome()) {
|
||||
&& !aCallerIsChrome) {
|
||||
LOG(LogLevel::Debug, ("%p Blocked attempt to autoplay media.", this));
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
|
||||
@ -2236,7 +2245,7 @@ HTMLMediaElement::Play(ErrorResult& aRv)
|
||||
false,
|
||||
false);
|
||||
#endif
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Play was not blocked so assume user interacted with the element.
|
||||
@ -2257,7 +2266,7 @@ HTMLMediaElement::Play(ErrorResult& aRv)
|
||||
OwnerDoc()->Hidden()) {
|
||||
LOG(LogLevel::Debug, ("%p Blocked playback because owner hidden.", this));
|
||||
mPlayBlockedBecauseHidden = true;
|
||||
return;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Even if we just did Load() or ResumeLoad(), we could already have a decoder
|
||||
@ -2267,9 +2276,9 @@ HTMLMediaElement::Play(ErrorResult& aRv)
|
||||
SetCurrentTime(0);
|
||||
}
|
||||
if (!mPausedForInactiveDocumentOrChannel) {
|
||||
aRv = mDecoder->Play();
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
nsresult rv = mDecoder->Play();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2305,13 +2314,13 @@ HTMLMediaElement::Play(ErrorResult& aRv)
|
||||
AddRemoveSelfReference();
|
||||
UpdatePreloadAction();
|
||||
UpdateSrcMediaStreamPlaying();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP HTMLMediaElement::Play()
|
||||
{
|
||||
ErrorResult rv;
|
||||
Play(rv);
|
||||
return rv.StealNSResult();
|
||||
return PlayInternal(/* aCallerIsChrome = */ true);
|
||||
}
|
||||
|
||||
HTMLMediaElement::WakeLockBoolWrapper&
|
||||
|
@ -711,6 +711,8 @@ protected:
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
};
|
||||
|
||||
nsresult PlayInternal(bool aCallerIsChrome);
|
||||
|
||||
/** Use this method to change the mReadyState member, so required
|
||||
* events can be fired.
|
||||
*/
|
||||
|
@ -6,6 +6,5 @@
|
||||
"Historical DOM features must be removed: getAttributeNode": true,
|
||||
"Historical DOM features must be removed: getAttributeNodeNS": true,
|
||||
"Historical DOM features must be removed: setAttributeNode": true,
|
||||
"Historical DOM features must be removed: removeAttributeNode": true,
|
||||
"DocumentType member must be nuked: internalSubset": true
|
||||
"Historical DOM features must be removed: removeAttributeNode": true
|
||||
}
|
||||
|
@ -285,10 +285,15 @@ MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIApplication> ownApp = GetAppForId(originAttributes.mAppId);
|
||||
if ((ownApp == nullptr) != (originAttributes.mAppId == NO_APP_ID)) {
|
||||
mInvalidReason = "Got an ownAppId that didn't correspond to an app.";
|
||||
return;
|
||||
nsCOMPtr<mozIApplication> ownApp;
|
||||
if (!originAttributes.mInBrowser) {
|
||||
// mAppId corresponds to OwnOrContainingAppId; if mInBrowser is
|
||||
// false then it's ownApp otherwise it's containingApp
|
||||
ownApp = GetAppForId(originAttributes.mAppId);
|
||||
if ((ownApp == nullptr) != (originAttributes.mAppId == NO_APP_ID)) {
|
||||
mInvalidReason = "Got an ownAppId that didn't correspond to an app.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIApplication> containingApp = GetAppForId(containingAppId);
|
||||
|
@ -486,7 +486,7 @@ TabParent::ShouldSwitchProcess(nsIChannel* aChannel)
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
aChannel->GetURI(getter_AddRefs(uri));
|
||||
LogChannelRelevantInfo(uri, loadingPrincipal, resultPrincipal,
|
||||
loadInfo->GetContentPolicyType());
|
||||
loadInfo->InternalContentPolicyType());
|
||||
|
||||
// Check if the signed package is loaded from the same origin.
|
||||
bool sameOrigin = false;
|
||||
@ -497,7 +497,7 @@ TabParent::ShouldSwitchProcess(nsIChannel* aChannel)
|
||||
}
|
||||
|
||||
// If this is not a top level document, there's no need to switch process.
|
||||
if (nsIContentPolicy::TYPE_DOCUMENT != loadInfo->GetContentPolicyType()) {
|
||||
if (nsIContentPolicy::TYPE_DOCUMENT != loadInfo->InternalContentPolicyType()) {
|
||||
LOG("Subresource of a document. No need to switch process.\n");
|
||||
return false;
|
||||
}
|
||||
|
@ -141,8 +141,7 @@ MediaDecoder::NotifyOwnerActivityChanged()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mOwner) {
|
||||
NS_WARNING("MediaDecoder without a decoder owner, can't update dormant");
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -170,7 +169,8 @@ MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mDecoderStateMachine ||
|
||||
if (mShuttingDown ||
|
||||
!mDecoderStateMachine ||
|
||||
mPlayState == PLAY_STATE_SHUTDOWN ||
|
||||
!mOwner->GetVideoFrameContainer() ||
|
||||
(mOwner->GetMediaElement() && mOwner->GetMediaElement()->IsBeingDestroyed()) ||
|
||||
@ -256,7 +256,6 @@ MediaDecoder::StartDormantTimer()
|
||||
|
||||
if (mIsHeuristicDormant ||
|
||||
mShuttingDown ||
|
||||
!mOwner ||
|
||||
!mOwner->IsHidden() ||
|
||||
(mPlayState != PLAY_STATE_PAUSED &&
|
||||
!IsEnded()))
|
||||
@ -478,8 +477,6 @@ MediaDecoder::Shutdown()
|
||||
|
||||
ChangeState(PLAY_STATE_SHUTDOWN);
|
||||
|
||||
mOwner = nullptr;
|
||||
|
||||
MediaShutdownManager::Instance().Unregister(this);
|
||||
}
|
||||
|
||||
@ -599,12 +596,8 @@ MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
|
||||
CallSeek(target);
|
||||
|
||||
if (mPlayState == PLAY_STATE_ENDED) {
|
||||
bool paused = false;
|
||||
if (mOwner) {
|
||||
paused = mOwner->GetPaused();
|
||||
}
|
||||
PinForSeek();
|
||||
ChangeState(paused ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING);
|
||||
ChangeState(mOwner->GetPaused() ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -664,14 +657,12 @@ MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
mInfo = aInfo.forget();
|
||||
ConstructMediaTracks();
|
||||
|
||||
if (mOwner) {
|
||||
// Make sure the element and the frame (if any) are told about
|
||||
// our new size.
|
||||
Invalidate();
|
||||
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mFiredMetadataLoaded = true;
|
||||
mOwner->MetadataLoaded(mInfo, nsAutoPtr<const MetadataTags>(aTags.forget()));
|
||||
}
|
||||
// Make sure the element and the frame (if any) are told about
|
||||
// our new size.
|
||||
Invalidate();
|
||||
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mFiredMetadataLoaded = true;
|
||||
mOwner->MetadataLoaded(mInfo, nsAutoPtr<const MetadataTags>(aTags.forget()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -706,11 +697,9 @@ MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
|
||||
mInfo = aInfo.forget();
|
||||
|
||||
if (mOwner) {
|
||||
Invalidate();
|
||||
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mOwner->FirstFrameLoaded();
|
||||
}
|
||||
Invalidate();
|
||||
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mOwner->FirstFrameLoaded();
|
||||
}
|
||||
|
||||
// This can run cache callbacks.
|
||||
@ -736,10 +725,8 @@ MediaDecoder::ResetConnectionState()
|
||||
if (mShuttingDown)
|
||||
return;
|
||||
|
||||
if (mOwner) {
|
||||
// Notify the media element that connection gets lost.
|
||||
mOwner->ResetConnectionState();
|
||||
}
|
||||
// Notify the media element that connection gets lost.
|
||||
mOwner->ResetConnectionState();
|
||||
|
||||
// Since we have notified the media element the connection
|
||||
// lost event, the decoder will be reloaded when user tries
|
||||
@ -754,9 +741,7 @@ MediaDecoder::NetworkError()
|
||||
if (mShuttingDown)
|
||||
return;
|
||||
|
||||
if (mOwner)
|
||||
mOwner->NetworkError();
|
||||
|
||||
mOwner->NetworkError();
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
@ -767,9 +752,7 @@ MediaDecoder::DecodeError()
|
||||
if (mShuttingDown)
|
||||
return;
|
||||
|
||||
if (mOwner)
|
||||
mOwner->DecodeError();
|
||||
|
||||
mOwner->DecodeError();
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
@ -815,10 +798,7 @@ MediaDecoder::PlaybackEnded()
|
||||
|
||||
ChangeState(PLAY_STATE_ENDED);
|
||||
InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);
|
||||
|
||||
if (mOwner) {
|
||||
mOwner->PlaybackEnded();
|
||||
}
|
||||
mOwner->PlaybackEnded();
|
||||
|
||||
// This must be called after |mOwner->PlaybackEnded()| call above, in order
|
||||
// to fire the required durationchange.
|
||||
@ -887,7 +867,10 @@ void
|
||||
MediaDecoder::NotifySuspendedStatusChanged()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mResource && mOwner) {
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
if (mResource) {
|
||||
bool suspended = mResource->IsSuspendedByCache();
|
||||
mOwner->NotifySuspendedByCache(suspended);
|
||||
}
|
||||
@ -897,10 +880,11 @@ void
|
||||
MediaDecoder::NotifyBytesDownloaded()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
UpdatePlaybackRate();
|
||||
if (mOwner) {
|
||||
mOwner->DownloadProgressed();
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
UpdatePlaybackRate();
|
||||
mOwner->DownloadProgressed();
|
||||
}
|
||||
|
||||
void
|
||||
@ -908,13 +892,15 @@ MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
DECODER_LOG("NotifyDownloadEnded, status=%x", aStatus);
|
||||
|
||||
if (aStatus == NS_BINDING_ABORTED) {
|
||||
// Download has been cancelled by user.
|
||||
if (mOwner) {
|
||||
mOwner->LoadAborted();
|
||||
}
|
||||
mOwner->LoadAborted();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -934,9 +920,10 @@ void
|
||||
MediaDecoder::NotifyPrincipalChanged()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mOwner) {
|
||||
mOwner->NotifyDecoderPrincipalChanged();
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
mOwner->NotifyDecoderPrincipalChanged();
|
||||
}
|
||||
|
||||
void
|
||||
@ -978,12 +965,10 @@ MediaDecoder::OnSeekResolved(SeekResolveValue aVal)
|
||||
|
||||
UpdateLogicalPosition(aVal.mEventVisibility);
|
||||
|
||||
if (mOwner) {
|
||||
if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mOwner->SeekCompleted();
|
||||
if (fireEnded) {
|
||||
mOwner->PlaybackEnded();
|
||||
}
|
||||
if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mOwner->SeekCompleted();
|
||||
if (fireEnded) {
|
||||
mOwner->PlaybackEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -995,10 +980,8 @@ MediaDecoder::SeekingStarted(MediaDecoderEventVisibility aEventVisibility)
|
||||
if (mShuttingDown)
|
||||
return;
|
||||
|
||||
if (mOwner) {
|
||||
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mOwner->SeekStarted();
|
||||
}
|
||||
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mOwner->SeekStarted();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1052,7 +1035,7 @@ MediaDecoder::UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisibility
|
||||
// frame has reflowed and the size updated beforehand.
|
||||
Invalidate();
|
||||
|
||||
if (mOwner && logicalPositionChanged &&
|
||||
if (logicalPositionChanged &&
|
||||
aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
FireTimeUpdate();
|
||||
}
|
||||
@ -1063,6 +1046,10 @@ MediaDecoder::DurationChanged()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
double oldDuration = mDuration;
|
||||
if (IsInfinite()) {
|
||||
mDuration = std::numeric_limits<double>::infinity();
|
||||
@ -1083,7 +1070,7 @@ MediaDecoder::DurationChanged()
|
||||
|
||||
// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=28822 for a discussion
|
||||
// of whether we should fire durationchange on explicit infinity.
|
||||
if (mOwner && mFiredMetadataLoaded &&
|
||||
if (mFiredMetadataLoaded &&
|
||||
(!mozilla::IsInfinite<double>(mDuration) || mExplicitDuration.Ref().isSome())) {
|
||||
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
|
||||
}
|
||||
@ -1215,7 +1202,7 @@ MediaDecoder::SetPlaybackRate(double aPlaybackRate)
|
||||
mPausedForPlaybackRateNull = false;
|
||||
// If the playbackRate is no longer null, restart the playback, iff the
|
||||
// media was playing.
|
||||
if (mOwner && !mOwner->GetPaused()) {
|
||||
if (!mOwner->GetPaused()) {
|
||||
Play();
|
||||
}
|
||||
}
|
||||
@ -1346,8 +1333,9 @@ void
|
||||
MediaDecoder::FireTimeUpdate()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mOwner)
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
mOwner->FireTimeUpdate(true);
|
||||
}
|
||||
|
||||
@ -1571,11 +1559,7 @@ MediaDecoder::ConstructMediaTracks()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mMediaTracksConstructed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mOwner || !mInfo) {
|
||||
if (mShuttingDown || mMediaTracksConstructed || !mInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1611,7 +1595,7 @@ MediaDecoder::RemoveMediaTracks()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mOwner) {
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -750,7 +750,8 @@ private:
|
||||
|
||||
void UpdateReadyState()
|
||||
{
|
||||
if (mOwner) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mShuttingDown) {
|
||||
mOwner->UpdateReadyState();
|
||||
}
|
||||
}
|
||||
@ -868,7 +869,7 @@ protected:
|
||||
// This should only ever be accessed from the main thread.
|
||||
// It is set in Init and cleared in Shutdown when the element goes away.
|
||||
// The decoder does not add a reference the element.
|
||||
MediaDecoderOwner* mOwner;
|
||||
MediaDecoderOwner* const mOwner;
|
||||
|
||||
// Counters related to decode and presentation of frames.
|
||||
FrameStatistics mFrameStats;
|
||||
|
@ -104,7 +104,6 @@ MediaDecoderReader::InitializationTask()
|
||||
MediaDecoderReader::~MediaDecoderReader()
|
||||
{
|
||||
MOZ_ASSERT(mShutdown);
|
||||
ResetDecode();
|
||||
MOZ_COUNT_DTOR(MediaDecoderReader);
|
||||
}
|
||||
|
||||
@ -250,12 +249,12 @@ MediaDecoderReader::GetBuffered()
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
MediaDecoderReader::AsyncReadMetadata()
|
||||
MediaDecoderReader::AsyncReadMetadataInternal()
|
||||
{
|
||||
typedef ReadMetadataFailureReason Reason;
|
||||
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
DECODER_LOG("MediaDecoderReader::AsyncReadMetadata");
|
||||
DECODER_LOG("MediaDecoderReader::AsyncReadMetadataInternal");
|
||||
|
||||
// Attempt to read the metadata.
|
||||
RefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||
|
@ -94,6 +94,14 @@ public:
|
||||
// on failure.
|
||||
virtual nsresult Init() { return NS_OK; }
|
||||
|
||||
RefPtr<MetadataPromise> AsyncReadMetadata()
|
||||
{
|
||||
return OnTaskQueue() ?
|
||||
AsyncReadMetadataInternal() :
|
||||
InvokeAsync(OwnerThread(), this, __func__,
|
||||
&MediaDecoderReader::AsyncReadMetadataInternal);
|
||||
}
|
||||
|
||||
// Release media resources they should be released in dormant state
|
||||
// The reader can be made usable again by calling ReadMetadata().
|
||||
void ReleaseMediaResources()
|
||||
@ -177,18 +185,6 @@ public:
|
||||
virtual bool HasAudio() = 0;
|
||||
virtual bool HasVideo() = 0;
|
||||
|
||||
// The default implementation of AsyncReadMetadata is implemented in terms of
|
||||
// synchronous ReadMetadata() calls. Implementations may also
|
||||
// override AsyncReadMetadata to create a more proper async implementation.
|
||||
virtual RefPtr<MetadataPromise> AsyncReadMetadata();
|
||||
|
||||
// Read header data for all bitstreams in the file. Fills aInfo with
|
||||
// the data required to present the media, and optionally fills *aTags
|
||||
// with tag metadata from the file.
|
||||
// Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) { MOZ_CRASH(); }
|
||||
|
||||
// Fills aInfo with the latest cached data required to present the media,
|
||||
// ReadUpdatedMetadata will always be called once ReadMetadata has succeeded.
|
||||
virtual void ReadUpdatedMetadata(MediaInfo* aInfo) { };
|
||||
@ -268,6 +264,18 @@ private:
|
||||
virtual void ReleaseMediaResourcesInternal() {}
|
||||
virtual void DisableHardwareAccelerationInternal() {}
|
||||
|
||||
// Read header data for all bitstreams in the file. Fills aInfo with
|
||||
// the data required to present the media, and optionally fills *aTags
|
||||
// with tag metadata from the file.
|
||||
// Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
|
||||
virtual nsresult ReadMetadata(MediaInfo*, MetadataTags**) { MOZ_CRASH(); }
|
||||
|
||||
// The default implementation of AsyncReadMetadataInternal is implemented in
|
||||
// terms of synchronous ReadMetadata() calls. Implementations may also
|
||||
// override AsyncReadMetadataInternal to create a more proper async
|
||||
// implementation.
|
||||
virtual RefPtr<MetadataPromise> AsyncReadMetadataInternal();
|
||||
|
||||
protected:
|
||||
friend class TrackBuffer;
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) { }
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "mediasink/DecodedAudioDataSink.h"
|
||||
#include "mediasink/AudioSinkWrapper.h"
|
||||
#include "mediasink/VideoSink.h"
|
||||
#include "mediasink/DecodedStream.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Logging.h"
|
||||
@ -202,7 +203,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
|
||||
/* aSupportsTailDispatch = */ true)),
|
||||
mWatchManager(this, mTaskQueue),
|
||||
mProducerID(ImageContainer::AllocateProducerID()),
|
||||
mRealTime(aRealTime),
|
||||
mDispatchedStateMachine(false),
|
||||
mDelayedScheduler(mTaskQueue),
|
||||
@ -212,7 +212,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
mFragmentEndTime(-1),
|
||||
mReader(aReader),
|
||||
mDecodedAudioEndTime(-1),
|
||||
mVideoFrameEndTime(-1),
|
||||
mDecodedVideoEndTime(-1),
|
||||
mPlaybackRate(1.0),
|
||||
mLowAudioThresholdUsecs(detail::LOW_AUDIO_USECS),
|
||||
@ -305,7 +304,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
|
||||
mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
|
||||
|
||||
mMediaSink = CreateAudioSink();
|
||||
mMediaSink = CreateMediaSink(mAudioCaptured);
|
||||
|
||||
#ifdef MOZ_EME
|
||||
mCDMProxyPromise.Begin(mDecoder->RequestCDMProxy()->Then(
|
||||
@ -381,6 +380,23 @@ MediaDecoderStateMachine::CreateAudioSink()
|
||||
return new AudioSinkWrapper(mTaskQueue, audioSinkCreator);
|
||||
}
|
||||
|
||||
already_AddRefed<media::MediaSink>
|
||||
MediaDecoderStateMachine::CreateMediaSink(bool aAudioCaptured)
|
||||
{
|
||||
// TODO: We can't really create a new DecodedStream until OutputStreamManager
|
||||
// is extracted. It is tricky that the implementation of DecodedStream
|
||||
// happens to allow reuse after shutdown without creating a new one.
|
||||
RefPtr<media::MediaSink> audioSink = aAudioCaptured ?
|
||||
mStreamSink : CreateAudioSink();
|
||||
|
||||
RefPtr<media::MediaSink> mediaSink =
|
||||
new VideoSink(mTaskQueue, audioSink, mVideoQueue,
|
||||
mDecoder->GetVideoFrameContainer(), mRealTime,
|
||||
mDecoder->GetFrameStatistics(), AUDIO_DURATION_USECS,
|
||||
sVideoQueueSendToCompositorSize);
|
||||
return mediaSink.forget();
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::HasFutureAudio()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
@ -1050,7 +1066,6 @@ void MediaDecoderStateMachine::StopPlayback()
|
||||
mDecoder->DispatchPlaybackStopped();
|
||||
|
||||
if (IsPlaying()) {
|
||||
RenderVideoFrames(1);
|
||||
mMediaSink->SetPlaying(false);
|
||||
MOZ_ASSERT(!IsPlaying());
|
||||
}
|
||||
@ -1489,7 +1504,8 @@ void MediaDecoderStateMachine::StopMediaSink()
|
||||
if (mMediaSink->IsStarted()) {
|
||||
DECODER_LOG("Stop MediaSink");
|
||||
mMediaSink->Stop();
|
||||
mMediaSinkPromise.DisconnectIfExists();
|
||||
mMediaSinkAudioPromise.DisconnectIfExists();
|
||||
mMediaSinkVideoPromise.DisconnectIfExists();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1764,12 +1780,20 @@ MediaDecoderStateMachine::StartMediaSink()
|
||||
mAudioCompleted = false;
|
||||
mMediaSink->Start(GetMediaTime(), mInfo);
|
||||
|
||||
auto promise = mMediaSink->OnEnded(TrackInfo::kAudioTrack);
|
||||
if (promise) {
|
||||
mMediaSinkPromise.Begin(promise->Then(
|
||||
auto videoPromise = mMediaSink->OnEnded(TrackInfo::kVideoTrack);
|
||||
auto audioPromise = mMediaSink->OnEnded(TrackInfo::kAudioTrack);
|
||||
|
||||
if (audioPromise) {
|
||||
mMediaSinkAudioPromise.Begin(audioPromise->Then(
|
||||
OwnerThread(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnMediaSinkComplete,
|
||||
&MediaDecoderStateMachine::OnMediaSinkError));
|
||||
&MediaDecoderStateMachine::OnMediaSinkAudioComplete,
|
||||
&MediaDecoderStateMachine::OnMediaSinkAudioError));
|
||||
}
|
||||
if (videoPromise) {
|
||||
mMediaSinkVideoPromise.Begin(videoPromise->Then(
|
||||
OwnerThread(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnMediaSinkVideoComplete,
|
||||
&MediaDecoderStateMachine::OnMediaSinkVideoError));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1832,7 +1856,7 @@ bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
|
||||
|
||||
int64_t endOfDecodedVideoData = INT64_MAX;
|
||||
if (HasVideo() && !VideoQueue().AtEndOfStream()) {
|
||||
endOfDecodedVideoData = VideoQueue().Peek() ? VideoQueue().Peek()->GetEndTime() : mVideoFrameEndTime;
|
||||
endOfDecodedVideoData = VideoQueue().Peek() ? VideoQueue().Peek()->GetEndTime() : VideoEndTime();
|
||||
}
|
||||
int64_t endOfDecodedAudioData = INT64_MAX;
|
||||
if (HasAudio() && !AudioQueue().AtEndOfStream()) {
|
||||
@ -2032,7 +2056,7 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame()
|
||||
DECODER_LOG("FinishDecodeFirstFrame");
|
||||
|
||||
if (!IsRealTime() && !mSentFirstFrameLoadedEvent) {
|
||||
RenderVideoFrames(1);
|
||||
mMediaSink->Redraw();
|
||||
}
|
||||
|
||||
// If we don't know the duration by this point, we assume infinity, per spec.
|
||||
@ -2129,7 +2153,7 @@ MediaDecoderStateMachine::SeekCompleted()
|
||||
ScheduleStateMachine();
|
||||
|
||||
if (video) {
|
||||
RenderVideoFrames(1);
|
||||
mMediaSink->Redraw();
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate);
|
||||
AbstractThread::MainThread()->Dispatch(event.forget());
|
||||
@ -2256,11 +2280,10 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
|
||||
case DECODER_STATE_DECODING_METADATA: {
|
||||
if (!mMetadataRequest.Exists()) {
|
||||
DECODER_LOG("Dispatching AsyncReadMetadata");
|
||||
DECODER_LOG("Calling AsyncReadMetadata");
|
||||
// Set mode to METADATA since we are about to read metadata.
|
||||
mResource->SetReadMode(MediaCacheStream::MODE_METADATA);
|
||||
mMetadataRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::AsyncReadMetadata)
|
||||
mMetadataRequest.Begin(mReader->AsyncReadMetadata()
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnMetadataRead,
|
||||
&MediaDecoderStateMachine::OnMetadataNotRead));
|
||||
@ -2285,7 +2308,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
// Start playback if necessary so that the clock can be properly queried.
|
||||
MaybeStartPlayback();
|
||||
|
||||
UpdateRenderedVideoFrames();
|
||||
UpdatePlaybackPositionPeriodically();
|
||||
NS_ASSERTION(!IsPlaying() ||
|
||||
mLogicallySeeking ||
|
||||
IsStateMachineScheduled(),
|
||||
@ -2356,7 +2379,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
{
|
||||
// Start playback if necessary to play the remaining media.
|
||||
MaybeStartPlayback();
|
||||
UpdateRenderedVideoFrames();
|
||||
UpdatePlaybackPositionPeriodically();
|
||||
NS_ASSERTION(!IsPlaying() ||
|
||||
mLogicallySeeking ||
|
||||
IsStateMachineScheduled(),
|
||||
@ -2378,7 +2401,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
if (mPlayState == MediaDecoder::PLAY_STATE_PLAYING &&
|
||||
!mSentPlaybackEndedEvent)
|
||||
{
|
||||
int64_t clockTime = std::max(AudioEndTime(), mVideoFrameEndTime);
|
||||
int64_t clockTime = std::max(AudioEndTime(), VideoEndTime());
|
||||
clockTime = std::max(int64_t(0), std::max(clockTime, Duration().ToMicroseconds()));
|
||||
UpdatePlaybackPosition(clockTime);
|
||||
|
||||
@ -2419,7 +2442,6 @@ MediaDecoderStateMachine::Reset()
|
||||
// crash for no samples to be popped.
|
||||
StopMediaSink();
|
||||
|
||||
mVideoFrameEndTime = -1;
|
||||
mDecodedVideoEndTime = -1;
|
||||
mDecodedAudioEndTime = -1;
|
||||
mAudioCompleted = false;
|
||||
@ -2469,69 +2491,6 @@ MediaDecoderStateMachine::CheckFrameValidity(VideoData* aData)
|
||||
}
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::RenderVideoFrames(int32_t aMaxFrames,
|
||||
int64_t aClockTime,
|
||||
const TimeStamp& aClockTimeStamp)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
|
||||
nsAutoTArray<RefPtr<MediaData>,16> frames;
|
||||
VideoQueue().GetFirstElements(aMaxFrames, &frames);
|
||||
if (frames.IsEmpty() || !container) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoTArray<ImageContainer::NonOwningImage,16> images;
|
||||
TimeStamp lastFrameTime;
|
||||
for (uint32_t i = 0; i < frames.Length(); ++i) {
|
||||
VideoData* frame = frames[i]->As<VideoData>();
|
||||
|
||||
bool valid = !frame->mImage || frame->mImage->IsValid();
|
||||
frame->mSentToCompositor = true;
|
||||
|
||||
if (!valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t frameTime = frame->mTime;
|
||||
if (frameTime < 0) {
|
||||
// Frame times before the start time are invalid; drop such frames
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
TimeStamp t;
|
||||
if (aMaxFrames > 1) {
|
||||
MOZ_ASSERT(!aClockTimeStamp.IsNull());
|
||||
int64_t delta = frame->mTime - aClockTime;
|
||||
t = aClockTimeStamp +
|
||||
TimeDuration::FromMicroseconds(delta / mPlaybackRate);
|
||||
if (!lastFrameTime.IsNull() && t <= lastFrameTime) {
|
||||
// Timestamps out of order; drop the new frame. In theory we should
|
||||
// probably replace the previous frame with the new frame if the
|
||||
// timestamps are equal, but this is a corrupt video file already so
|
||||
// never mind.
|
||||
continue;
|
||||
}
|
||||
lastFrameTime = t;
|
||||
}
|
||||
|
||||
ImageContainer::NonOwningImage* img = images.AppendElement();
|
||||
img->mTimeStamp = t;
|
||||
img->mImage = frame->mImage;
|
||||
img->mFrameID = frame->mFrameID;
|
||||
img->mProducerID = mProducerID;
|
||||
|
||||
VERBOSE_LOG("playing video frame %lld (id=%x) (queued=%i, state-machine=%i, decoder-queued=%i)",
|
||||
frame->mTime, frame->mFrameID,
|
||||
VideoQueue().GetSize() + mReader->SizeOfVideoQueueInFrames(),
|
||||
VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames());
|
||||
}
|
||||
|
||||
container->SetCurrentFrames(frames[0]->As<VideoData>()->mDisplay, images);
|
||||
}
|
||||
|
||||
int64_t
|
||||
MediaDecoderStateMachine::GetClock(TimeStamp* aTimeStamp) const
|
||||
{
|
||||
@ -2541,7 +2500,8 @@ MediaDecoderStateMachine::GetClock(TimeStamp* aTimeStamp) const
|
||||
return clockTime;
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
|
||||
void
|
||||
MediaDecoderStateMachine::UpdatePlaybackPositionPeriodically()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
@ -2553,47 +2513,19 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
|
||||
DiscardStreamData();
|
||||
}
|
||||
|
||||
TimeStamp nowTime;
|
||||
const int64_t clockTime = GetClock(&nowTime);
|
||||
// Skip frames up to the frame at the playback position, and figure out
|
||||
// the time remaining until it's time to display the next frame and drop
|
||||
// the current frame.
|
||||
NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
|
||||
int64_t remainingTime = AUDIO_DURATION_USECS;
|
||||
if (VideoQueue().GetSize() > 0) {
|
||||
RefPtr<MediaData> currentFrame = VideoQueue().PopFront();
|
||||
int32_t framesRemoved = 0;
|
||||
while (VideoQueue().GetSize() > 0) {
|
||||
MediaData* nextFrame = VideoQueue().PeekFront();
|
||||
if (!IsRealTime() && nextFrame->mTime > clockTime) {
|
||||
remainingTime = nextFrame->mTime - clockTime;
|
||||
break;
|
||||
}
|
||||
++framesRemoved;
|
||||
if (!currentFrame->As<VideoData>()->mSentToCompositor) {
|
||||
mDecoder->NotifyDecodedFrames(0, 0, 1);
|
||||
VERBOSE_LOG("discarding video frame mTime=%lld clock_time=%lld",
|
||||
currentFrame->mTime, clockTime);
|
||||
}
|
||||
currentFrame = VideoQueue().PopFront();
|
||||
|
||||
}
|
||||
VideoQueue().PushFront(currentFrame);
|
||||
if (framesRemoved > 0) {
|
||||
mVideoFrameEndTime = currentFrame->GetEndTime();
|
||||
FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
|
||||
frameStats.NotifyPresentedFrame();
|
||||
}
|
||||
}
|
||||
|
||||
RenderVideoFrames(sVideoQueueSendToCompositorSize, clockTime, nowTime);
|
||||
|
||||
// Cap the current time to the larger of the audio and video end time.
|
||||
// This ensures that if we're running off the system clock, we don't
|
||||
// advance the clock to after the media end time.
|
||||
if (mVideoFrameEndTime != -1 || AudioEndTime() != -1) {
|
||||
if (VideoEndTime() != -1 || AudioEndTime() != -1) {
|
||||
|
||||
const int64_t clockTime = GetClock();
|
||||
// Skip frames up to the frame at the playback position, and figure out
|
||||
// the time remaining until it's time to display the next frame and drop
|
||||
// the current frame.
|
||||
NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
|
||||
|
||||
// These will be non -1 if we've displayed a video frame, or played an audio frame.
|
||||
int64_t t = std::min(clockTime, std::max(mVideoFrameEndTime, AudioEndTime()));
|
||||
int64_t t = std::min(clockTime, std::max(VideoEndTime(), AudioEndTime()));
|
||||
// FIXME: Bug 1091422 - chained ogg files hit this assertion.
|
||||
//MOZ_ASSERT(t >= GetMediaTime());
|
||||
if (t > GetMediaTime()) {
|
||||
@ -2605,7 +2537,7 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
|
||||
// the monitor and get a staled value from GetCurrentTimeUs() which hits the
|
||||
// assertion in GetClock().
|
||||
|
||||
int64_t delay = std::max<int64_t>(1, remainingTime / mPlaybackRate);
|
||||
int64_t delay = std::max<int64_t>(1, AUDIO_DURATION_USECS / mPlaybackRate);
|
||||
ScheduleStateMachineIn(delay);
|
||||
}
|
||||
|
||||
@ -2908,22 +2840,57 @@ MediaDecoderStateMachine::AudioEndTime() const
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::OnMediaSinkComplete()
|
||||
int64_t
|
||||
MediaDecoderStateMachine::VideoEndTime() const
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
if (mMediaSink->IsStarted()) {
|
||||
return mMediaSink->GetEndTime(TrackInfo::kVideoTrack);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
mMediaSinkPromise.Complete();
|
||||
void
|
||||
MediaDecoderStateMachine::OnMediaSinkVideoComplete()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
VERBOSE_LOG("[%s]", __func__);
|
||||
|
||||
mMediaSinkVideoPromise.Complete();
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::OnMediaSinkVideoError()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
VERBOSE_LOG("[%s]", __func__);
|
||||
|
||||
mMediaSinkVideoPromise.Complete();
|
||||
if (HasAudio()) {
|
||||
return;
|
||||
}
|
||||
DecodeError();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::OnMediaSinkAudioComplete()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
VERBOSE_LOG("[%s]", __func__);
|
||||
|
||||
mMediaSinkAudioPromise.Complete();
|
||||
// Set true only when we have audio.
|
||||
mAudioCompleted = mInfo.HasAudio();
|
||||
// To notify PlaybackEnded as soon as possible.
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::OnMediaSinkError()
|
||||
void MediaDecoderStateMachine::OnMediaSinkAudioError()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
VERBOSE_LOG("[%s]", __func__);
|
||||
|
||||
mMediaSinkPromise.Complete();
|
||||
mMediaSinkAudioPromise.Complete();
|
||||
// Set true only when we have audio.
|
||||
mAudioCompleted = mInfo.HasAudio();
|
||||
|
||||
@ -2975,10 +2942,7 @@ MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured)
|
||||
mMediaSink->Shutdown();
|
||||
|
||||
// Create a new sink according to whether audio is captured.
|
||||
// TODO: We can't really create a new DecodedStream until OutputStreamManager
|
||||
// is extracted. It is tricky that the implementation of DecodedStream
|
||||
// happens to allow reuse after shutdown without creating a new one.
|
||||
mMediaSink = aCaptured ? mStreamSink : CreateAudioSink();
|
||||
mMediaSink = CreateMediaSink(aCaptured);
|
||||
|
||||
// Restore playback parameters.
|
||||
mMediaSink->SetPlaybackParams(params);
|
||||
|
@ -128,7 +128,6 @@ public:
|
||||
typedef MediaDecoderReader::AudioDataPromise AudioDataPromise;
|
||||
typedef MediaDecoderReader::VideoDataPromise VideoDataPromise;
|
||||
typedef MediaDecoderOwner::NextFrameStatus NextFrameStatus;
|
||||
typedef mozilla::layers::ImageContainer::ProducerID ProducerID;
|
||||
typedef mozilla::layers::ImageContainer::FrameID FrameID;
|
||||
MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
MediaDecoderReader* aReader,
|
||||
@ -465,25 +464,15 @@ protected:
|
||||
// acceleration.
|
||||
void CheckFrameValidity(VideoData* aData);
|
||||
|
||||
// Sets VideoQueue images into the VideoFrameContainer. Called on the shared
|
||||
// state machine thread. Decode monitor must be held. The first aMaxFrames
|
||||
// (at most) are set.
|
||||
// aClockTime and aClockTimeStamp are used as the baseline for deriving
|
||||
// timestamps for the frames; when omitted, aMaxFrames must be 1 and
|
||||
// a null timestamp is passed to the VideoFrameContainer.
|
||||
// If the VideoQueue is empty, this does nothing.
|
||||
void RenderVideoFrames(int32_t aMaxFrames, int64_t aClockTime = 0,
|
||||
const TimeStamp& aClickTimeStamp = TimeStamp());
|
||||
|
||||
// If we have video, display a video frame if it's time for display has
|
||||
// arrived, otherwise sleep until it's time for the next frame. Update the
|
||||
// current frame time as appropriate, and trigger ready state update. The
|
||||
// decoder monitor must be held with exactly one lock count. Called on the
|
||||
// state machine thread.
|
||||
void UpdateRenderedVideoFrames();
|
||||
// Update playback position and trigger next update by default time period.
|
||||
// Called on the state machine thread.
|
||||
void UpdatePlaybackPositionPeriodically();
|
||||
|
||||
media::MediaSink* CreateAudioSink();
|
||||
|
||||
// Always create mediasink which contains an AudioSink or StreamSink inside.
|
||||
already_AddRefed<media::MediaSink> CreateMediaSink(bool aAudioCaptured);
|
||||
|
||||
// Stops the media sink and shut it down.
|
||||
// The decoder monitor must be held with exactly one lock count.
|
||||
// Called on the state machine thread.
|
||||
@ -638,12 +627,14 @@ protected:
|
||||
bool IsVideoDecoding();
|
||||
|
||||
private:
|
||||
// Resolved by the MediaSink to signal that all outstanding work is complete
|
||||
// and the sink is shutting down.
|
||||
void OnMediaSinkComplete();
|
||||
// Resolved by the MediaSink to signal that all audio/video outstanding
|
||||
// work is complete and identify which part(a/v) of the sink is shutting down.
|
||||
void OnMediaSinkAudioComplete();
|
||||
void OnMediaSinkVideoComplete();
|
||||
|
||||
// Rejected by the MediaSink to signal errors.
|
||||
void OnMediaSinkError();
|
||||
// Rejected by the MediaSink to signal errors for audio/video.
|
||||
void OnMediaSinkAudioError();
|
||||
void OnMediaSinkVideoError();
|
||||
|
||||
// Return true if the video decoder's decode speed can not catch up the
|
||||
// play time.
|
||||
@ -668,10 +659,6 @@ private:
|
||||
// State-watching manager.
|
||||
WatchManager<MediaDecoderStateMachine> mWatchManager;
|
||||
|
||||
// Producer ID to help ImageContainer distinguish different streams of
|
||||
// FrameIDs. A unique and immutable value per MDSM.
|
||||
const ProducerID mProducerID;
|
||||
|
||||
// True is we are decoding a realtime stream, like a camera stream.
|
||||
const bool mRealTime;
|
||||
|
||||
@ -922,14 +909,14 @@ private:
|
||||
// of the audio stream, unless another frame is pushed to the hardware.
|
||||
int64_t AudioEndTime() const;
|
||||
|
||||
// The end time of the last rendered video frame that's been sent to
|
||||
// compositor.
|
||||
int64_t VideoEndTime() const;
|
||||
|
||||
// The end time of the last decoded audio frame. This signifies the end of
|
||||
// decoded audio data. Used to check if we are low in decoded data.
|
||||
int64_t mDecodedAudioEndTime;
|
||||
|
||||
// The presentation end time of the last video frame which has been displayed
|
||||
// in microseconds. Accessed from the state machine thread.
|
||||
int64_t mVideoFrameEndTime;
|
||||
|
||||
// The end time of the last decoded video frame. Used to check if we are low
|
||||
// on decoded video data.
|
||||
int64_t mDecodedVideoEndTime;
|
||||
@ -1192,7 +1179,9 @@ private:
|
||||
// Media data resource from the decoder.
|
||||
RefPtr<MediaResource> mResource;
|
||||
|
||||
MozPromiseRequestHolder<GenericPromise> mMediaSinkPromise;
|
||||
// Track the complete & error for audio/video separately
|
||||
MozPromiseRequestHolder<GenericPromise> mMediaSinkAudioPromise;
|
||||
MozPromiseRequestHolder<GenericPromise> mMediaSinkVideoPromise;
|
||||
|
||||
MediaEventListener mAudioQueueListener;
|
||||
MediaEventListener mVideoQueueListener;
|
||||
|
@ -243,7 +243,7 @@ MediaFormatReader::IsWaitingOnCDMResource() {
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
MediaFormatReader::AsyncReadMetadata()
|
||||
MediaFormatReader::AsyncReadMetadataInternal()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
@ -1047,7 +1047,7 @@ MediaFormatReader::Update(TrackType aTrack)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
if (mShutdown || !mInitDone) {
|
||||
if (mShutdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1058,6 +1058,10 @@ MediaFormatReader::Update(TrackType aTrack)
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
decoder.mUpdateScheduled = false;
|
||||
|
||||
if (!mInitDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (UpdateReceivedNewData(aTrack)) {
|
||||
LOGV("Nothing more to do");
|
||||
return;
|
||||
|
@ -52,8 +52,6 @@ public:
|
||||
return mAudio.mTrackDemuxer;
|
||||
}
|
||||
|
||||
RefPtr<MetadataPromise> AsyncReadMetadata() override;
|
||||
|
||||
void ReadUpdatedMetadata(MediaInfo* aInfo) override;
|
||||
|
||||
RefPtr<SeekPromise>
|
||||
@ -442,6 +440,7 @@ private:
|
||||
// For Media Resource Management
|
||||
void ReleaseMediaResourcesInternal() override;
|
||||
void DisableHardwareAccelerationInternal() override;
|
||||
RefPtr<MetadataPromise> AsyncReadMetadataInternal() override;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -35,8 +35,8 @@ AndroidMediaReader::AndroidMediaReader(AbstractMediaDecoder *aDecoder,
|
||||
{
|
||||
}
|
||||
|
||||
nsresult AndroidMediaReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
nsresult
|
||||
AndroidMediaReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
|
@ -64,8 +64,6 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags);
|
||||
virtual RefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
|
||||
@ -87,6 +85,8 @@ public:
|
||||
RefPtr<Image> mImage;
|
||||
};
|
||||
|
||||
private:
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -351,8 +351,7 @@ GetProperty(AudioFileStreamID aAudioFileStream,
|
||||
|
||||
|
||||
nsresult
|
||||
AppleMP3Reader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
AppleMP3Reader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
|
@ -31,9 +31,6 @@ public:
|
||||
virtual bool HasAudio() override;
|
||||
virtual bool HasVideo() override;
|
||||
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) override;
|
||||
|
||||
virtual RefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
|
||||
@ -50,10 +47,11 @@ protected:
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength,
|
||||
int64_t aOffset) override;
|
||||
public:
|
||||
|
||||
virtual bool IsMediaSeekable() override;
|
||||
|
||||
private:
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) override;
|
||||
|
||||
void SetupDecoder();
|
||||
nsresult Read(uint32_t *aNumBytes, char *aData);
|
||||
|
||||
|
@ -90,8 +90,7 @@ static const GUID CLSID_MPEG_LAYER_3_DECODER_FILTER =
|
||||
{ 0x38BE3000, 0xDBF4, 0x11D0, {0x86, 0x0E, 0x00, 0xA0, 0x24, 0xCF, 0xEF, 0x6D} };
|
||||
|
||||
nsresult
|
||||
DirectShowReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
DirectShowReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
HRESULT hr;
|
||||
|
@ -50,9 +50,6 @@ public:
|
||||
bool HasAudio() override;
|
||||
bool HasVideo() override;
|
||||
|
||||
nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) override;
|
||||
|
||||
RefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
|
||||
@ -64,6 +61,7 @@ public:
|
||||
bool IsMediaSeekable() override;
|
||||
|
||||
private:
|
||||
nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) override;
|
||||
|
||||
// Notifies the filter graph that playback is complete. aStatus is
|
||||
// the code to send to the filter graph. Always returns false, so
|
||||
|
@ -360,8 +360,8 @@ GStreamerReader::GetDataLength()
|
||||
return streamLen - mDataOffset;
|
||||
}
|
||||
|
||||
nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
nsresult
|
||||
GStreamerReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
nsresult ret = NS_OK;
|
||||
|
@ -46,8 +46,6 @@ public:
|
||||
virtual bool DecodeAudioData() override;
|
||||
virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
|
||||
int64_t aTimeThreshold) override;
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) override;
|
||||
virtual RefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
virtual media::TimeIntervals GetBuffered() override;
|
||||
@ -70,6 +68,7 @@ public:
|
||||
virtual bool IsMediaSeekable() override;
|
||||
|
||||
private:
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) override;
|
||||
|
||||
void ReadAndPushData(guint aLength);
|
||||
RefPtr<layers::PlanarYCbCrImage> GetImageFromBuffer(GstBuffer* aBuffer);
|
||||
|
@ -93,6 +93,11 @@ public:
|
||||
// Pause/resume the playback. Only work after playback starts.
|
||||
virtual void SetPlaying(bool aPlaying) = 0;
|
||||
|
||||
// Single frame rendering operation may need to be done before playback
|
||||
// started (1st frame) or right after seek completed or playback stopped.
|
||||
// Do nothing if this sink has no video track. Can be called in any state.
|
||||
virtual void Redraw() {};
|
||||
|
||||
// Begin a playback session with the provided start time and media info.
|
||||
// Must be called when playback is stopped.
|
||||
virtual void Start(int64_t aStartTime, const MediaInfo& aInfo) = 0;
|
||||
|
380
dom/media/mediasink/VideoSink.cpp
Normal file
380
dom/media/mediasink/VideoSink.cpp
Normal file
@ -0,0 +1,380 @@
|
||||
/* -*- 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 "VideoSink.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define VSINK_LOG(msg, ...) \
|
||||
MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, \
|
||||
("VideoSink=%p " msg, this, ##__VA_ARGS__))
|
||||
#define VSINK_LOG_V(msg, ...) \
|
||||
MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, \
|
||||
("VideoSink=%p " msg, this, ##__VA_ARGS__))
|
||||
|
||||
using namespace mozilla::layers;
|
||||
|
||||
namespace media {
|
||||
|
||||
VideoSink::VideoSink(AbstractThread* aThread,
|
||||
MediaSink* aAudioSink,
|
||||
MediaQueue<MediaData>& aVideoQueue,
|
||||
VideoFrameContainer* aContainer,
|
||||
bool aRealTime,
|
||||
FrameStatistics& aFrameStats,
|
||||
int aDelayDuration,
|
||||
uint32_t aVQueueSentToCompositerSize)
|
||||
: mOwnerThread(aThread)
|
||||
, mAudioSink(aAudioSink)
|
||||
, mVideoQueue(aVideoQueue)
|
||||
, mContainer(aContainer)
|
||||
, mProducerID(ImageContainer::AllocateProducerID())
|
||||
, mRealTime(aRealTime)
|
||||
, mFrameStats(aFrameStats)
|
||||
, mVideoFrameEndTime(-1)
|
||||
, mHasVideo(false)
|
||||
, mUpdateScheduler(aThread)
|
||||
, mDelayDuration(aDelayDuration)
|
||||
, mVideoQueueSendToCompositorSize(aVQueueSentToCompositerSize)
|
||||
{
|
||||
MOZ_ASSERT(mAudioSink, "AudioSink should exist.");
|
||||
}
|
||||
|
||||
VideoSink::~VideoSink()
|
||||
{
|
||||
}
|
||||
|
||||
const MediaSink::PlaybackParams&
|
||||
VideoSink::GetPlaybackParams() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
return mAudioSink->GetPlaybackParams();
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::SetPlaybackParams(const PlaybackParams& aParams)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
mAudioSink->SetPlaybackParams(aParams);
|
||||
}
|
||||
|
||||
RefPtr<GenericPromise>
|
||||
VideoSink::OnEnded(TrackType aType)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mAudioSink->IsStarted(), "Must be called after playback starts.");
|
||||
|
||||
if (aType == TrackInfo::kAudioTrack) {
|
||||
return mAudioSink->OnEnded(aType);
|
||||
} else if (aType == TrackInfo::kVideoTrack) {
|
||||
return mEndPromise;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int64_t
|
||||
VideoSink::GetEndTime(TrackType aType) const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mAudioSink->IsStarted(), "Must be called after playback starts.");
|
||||
|
||||
if (aType == TrackInfo::kVideoTrack) {
|
||||
return mVideoFrameEndTime;
|
||||
} else if (aType == TrackInfo::kAudioTrack) {
|
||||
return mAudioSink->GetEndTime(aType);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t
|
||||
VideoSink::GetPosition(TimeStamp* aTimeStamp) const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
return mAudioSink->GetPosition(aTimeStamp);
|
||||
}
|
||||
|
||||
bool
|
||||
VideoSink::HasUnplayedFrames(TrackType aType) const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(aType == TrackInfo::kAudioTrack, "Not implemented for non audio tracks.");
|
||||
|
||||
return mAudioSink->HasUnplayedFrames(aType);
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::SetPlaybackRate(double aPlaybackRate)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
mAudioSink->SetPlaybackRate(aPlaybackRate);
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::SetVolume(double aVolume)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
mAudioSink->SetVolume(aVolume);
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::SetPreservesPitch(bool aPreservesPitch)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
mAudioSink->SetPreservesPitch(aPreservesPitch);
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::SetPlaying(bool aPlaying)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
VSINK_LOG_V(" playing (%d) -> (%d)", mAudioSink->IsPlaying(), aPlaying);
|
||||
|
||||
if (!aPlaying) {
|
||||
// Reset any update timer if paused.
|
||||
mUpdateScheduler.Reset();
|
||||
// Since playback is paused, tell compositor to render only current frame.
|
||||
RenderVideoFrames(1);
|
||||
}
|
||||
|
||||
mAudioSink->SetPlaying(aPlaying);
|
||||
|
||||
if (mHasVideo && aPlaying) {
|
||||
// There's no thread in VideoSink for pulling video frames, need to trigger
|
||||
// rendering while becoming playing status. because the VideoQueue may be
|
||||
// full already.
|
||||
TryUpdateRenderedVideoFrames();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::Start(int64_t aStartTime, const MediaInfo& aInfo)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
VSINK_LOG("[%s]", __func__);
|
||||
|
||||
mAudioSink->Start(aStartTime, aInfo);
|
||||
|
||||
mHasVideo = aInfo.HasVideo();
|
||||
|
||||
if (mHasVideo) {
|
||||
mEndPromise = mEndPromiseHolder.Ensure(__func__);
|
||||
ConnectListener();
|
||||
TryUpdateRenderedVideoFrames();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::Stop()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mAudioSink->IsStarted(), "playback not started.");
|
||||
VSINK_LOG("[%s]", __func__);
|
||||
|
||||
mAudioSink->Stop();
|
||||
|
||||
mUpdateScheduler.Reset();
|
||||
if (mHasVideo) {
|
||||
DisconnectListener();
|
||||
mEndPromiseHolder.Resolve(true, __func__);
|
||||
mEndPromise = nullptr;
|
||||
}
|
||||
mVideoFrameEndTime = -1;
|
||||
}
|
||||
|
||||
bool
|
||||
VideoSink::IsStarted() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
return mAudioSink->IsStarted();
|
||||
}
|
||||
|
||||
bool
|
||||
VideoSink::IsPlaying() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
return mAudioSink->IsPlaying();
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::Shutdown()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(!mAudioSink->IsStarted(), "must be called after playback stops.");
|
||||
VSINK_LOG("[%s]", __func__);
|
||||
|
||||
mAudioSink->Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::OnVideoQueueEvent()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
// Listen to push event, VideoSink should try rendering ASAP if first frame
|
||||
// arrives but update scheduler is not triggered yet.
|
||||
TryUpdateRenderedVideoFrames();
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::Redraw()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
RenderVideoFrames(1);
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::TryUpdateRenderedVideoFrames()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
if (!mUpdateScheduler.IsScheduled() && VideoQueue().GetSize() >= 1 &&
|
||||
mAudioSink->IsPlaying()) {
|
||||
UpdateRenderedVideoFrames();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::UpdateRenderedVideoFramesByTimer()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
mUpdateScheduler.CompleteRequest();
|
||||
UpdateRenderedVideoFrames();
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::ConnectListener()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
mPushListener = VideoQueue().PushEvent().Connect(
|
||||
mOwnerThread, this, &VideoSink::OnVideoQueueEvent);
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::DisconnectListener()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
mPushListener.Disconnect();
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::RenderVideoFrames(int32_t aMaxFrames,
|
||||
int64_t aClockTime,
|
||||
const TimeStamp& aClockTimeStamp)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
nsAutoTArray<RefPtr<MediaData>,16> frames;
|
||||
VideoQueue().GetFirstElements(aMaxFrames, &frames);
|
||||
if (frames.IsEmpty() || !mContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoTArray<ImageContainer::NonOwningImage,16> images;
|
||||
TimeStamp lastFrameTime;
|
||||
MediaSink::PlaybackParams params = mAudioSink->GetPlaybackParams();
|
||||
for (uint32_t i = 0; i < frames.Length(); ++i) {
|
||||
VideoData* frame = frames[i]->As<VideoData>();
|
||||
|
||||
bool valid = !frame->mImage || frame->mImage->IsValid();
|
||||
frame->mSentToCompositor = true;
|
||||
|
||||
if (!valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t frameTime = frame->mTime;
|
||||
if (frameTime < 0) {
|
||||
// Frame times before the start time are invalid; drop such frames
|
||||
continue;
|
||||
}
|
||||
|
||||
TimeStamp t;
|
||||
if (aMaxFrames > 1) {
|
||||
MOZ_ASSERT(!aClockTimeStamp.IsNull());
|
||||
int64_t delta = frame->mTime - aClockTime;
|
||||
t = aClockTimeStamp +
|
||||
TimeDuration::FromMicroseconds(delta / params.mPlaybackRate);
|
||||
if (!lastFrameTime.IsNull() && t <= lastFrameTime) {
|
||||
// Timestamps out of order; drop the new frame. In theory we should
|
||||
// probably replace the previous frame with the new frame if the
|
||||
// timestamps are equal, but this is a corrupt video file already so
|
||||
// never mind.
|
||||
continue;
|
||||
}
|
||||
lastFrameTime = t;
|
||||
}
|
||||
|
||||
ImageContainer::NonOwningImage* img = images.AppendElement();
|
||||
img->mTimeStamp = t;
|
||||
img->mImage = frame->mImage;
|
||||
img->mFrameID = frame->mFrameID;
|
||||
img->mProducerID = mProducerID;
|
||||
|
||||
VSINK_LOG_V("playing video frame %lld (id=%x) (vq-queued=%i)",
|
||||
frame->mTime, frame->mFrameID, VideoQueue().GetSize());
|
||||
}
|
||||
mContainer->SetCurrentFrames(frames[0]->As<VideoData>()->mDisplay, images);
|
||||
}
|
||||
|
||||
void
|
||||
VideoSink::UpdateRenderedVideoFrames()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mAudioSink->IsPlaying(), "should be called while playing.");
|
||||
|
||||
TimeStamp nowTime;
|
||||
const int64_t clockTime = mAudioSink->GetPosition(&nowTime);
|
||||
// Skip frames up to the frame at the playback position, and figure out
|
||||
// the time remaining until it's time to display the next frame and drop
|
||||
// the current frame.
|
||||
NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
|
||||
|
||||
int64_t remainingTime = mDelayDuration;
|
||||
if (VideoQueue().GetSize() > 0) {
|
||||
RefPtr<MediaData> currentFrame = VideoQueue().PopFront();
|
||||
int32_t framesRemoved = 0;
|
||||
while (VideoQueue().GetSize() > 0) {
|
||||
MediaData* nextFrame = VideoQueue().PeekFront();
|
||||
if (!mRealTime && nextFrame->mTime > clockTime) {
|
||||
remainingTime = nextFrame->mTime - clockTime;
|
||||
break;
|
||||
}
|
||||
++framesRemoved;
|
||||
if (!currentFrame->As<VideoData>()->mSentToCompositor) {
|
||||
mFrameStats.NotifyDecodedFrames(0, 0, 1);
|
||||
VSINK_LOG_V("discarding video frame mTime=%lld clock_time=%lld",
|
||||
currentFrame->mTime, clockTime);
|
||||
}
|
||||
currentFrame = VideoQueue().PopFront();
|
||||
}
|
||||
VideoQueue().PushFront(currentFrame);
|
||||
if (framesRemoved > 0) {
|
||||
mVideoFrameEndTime = currentFrame->GetEndTime();
|
||||
mFrameStats.NotifyPresentedFrame();
|
||||
}
|
||||
}
|
||||
|
||||
RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);
|
||||
|
||||
TimeStamp target = nowTime + TimeDuration::FromMicroseconds(remainingTime);
|
||||
|
||||
RefPtr<VideoSink> self = this;
|
||||
mUpdateScheduler.Ensure(target, [self] () {
|
||||
self->UpdateRenderedVideoFramesByTimer();
|
||||
}, [self] () {
|
||||
self->UpdateRenderedVideoFramesByTimer();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
} // namespace mozilla
|
154
dom/media/mediasink/VideoSink.h
Normal file
154
dom/media/mediasink/VideoSink.h
Normal file
@ -0,0 +1,154 @@
|
||||
/* -*- 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 VideoSink_h_
|
||||
#define VideoSink_h_
|
||||
|
||||
#include "FrameStatistics.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "MediaSink.h"
|
||||
#include "MediaTimer.h"
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "VideoFrameContainer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class VideoFrameContainer;
|
||||
template <class T> class MediaQueue;
|
||||
|
||||
namespace media {
|
||||
|
||||
class VideoSink : public MediaSink
|
||||
{
|
||||
typedef mozilla::layers::ImageContainer::ProducerID ProducerID;
|
||||
public:
|
||||
VideoSink(AbstractThread* aThread,
|
||||
MediaSink* aAudioSink,
|
||||
MediaQueue<MediaData>& aVideoQueue,
|
||||
VideoFrameContainer* aContainer,
|
||||
bool aRealTime,
|
||||
FrameStatistics& aFrameStats,
|
||||
int aDelayDuration,
|
||||
uint32_t aVQueueSentToCompositerSize);
|
||||
|
||||
const PlaybackParams& GetPlaybackParams() const override;
|
||||
|
||||
void SetPlaybackParams(const PlaybackParams& aParams) override;
|
||||
|
||||
RefPtr<GenericPromise> OnEnded(TrackType aType) override;
|
||||
|
||||
int64_t GetEndTime(TrackType aType) const override;
|
||||
|
||||
int64_t GetPosition(TimeStamp* aTimeStamp = nullptr) const override;
|
||||
|
||||
bool HasUnplayedFrames(TrackType aType) const override;
|
||||
|
||||
void SetPlaybackRate(double aPlaybackRate) override;
|
||||
|
||||
void SetVolume(double aVolume) override;
|
||||
|
||||
void SetPreservesPitch(bool aPreservesPitch) override;
|
||||
|
||||
void SetPlaying(bool aPlaying) override;
|
||||
|
||||
void Redraw() override;
|
||||
|
||||
void Start(int64_t aStartTime, const MediaInfo& aInfo) override;
|
||||
|
||||
void Stop() override;
|
||||
|
||||
bool IsStarted() const override;
|
||||
|
||||
bool IsPlaying() const override;
|
||||
|
||||
void Shutdown() override;
|
||||
|
||||
private:
|
||||
virtual ~VideoSink();
|
||||
|
||||
// VideoQueue listener related.
|
||||
void OnVideoQueueEvent();
|
||||
void ConnectListener();
|
||||
void DisconnectListener();
|
||||
|
||||
// Sets VideoQueue images into the VideoFrameContainer. Called on the shared
|
||||
// state machine thread. The first aMaxFrames (at most) are set.
|
||||
// aClockTime and aClockTimeStamp are used as the baseline for deriving
|
||||
// timestamps for the frames; when omitted, aMaxFrames must be 1 and
|
||||
// a null timestamp is passed to the VideoFrameContainer.
|
||||
// If the VideoQueue is empty, this does nothing.
|
||||
void RenderVideoFrames(int32_t aMaxFrames, int64_t aClockTime = 0,
|
||||
const TimeStamp& aClickTimeStamp = TimeStamp());
|
||||
|
||||
// Triggered while videosink is started, videosink becomes "playing" status,
|
||||
// or VideoQueue event arrived.
|
||||
void TryUpdateRenderedVideoFrames();
|
||||
|
||||
// If we have video, display a video frame if it's time for display has
|
||||
// arrived, otherwise sleep until it's time for the next frame. Update the
|
||||
// current frame time as appropriate, and trigger ready state update.
|
||||
// Called on the shared state machine thread.
|
||||
void UpdateRenderedVideoFrames();
|
||||
void UpdateRenderedVideoFramesByTimer();
|
||||
|
||||
void AssertOwnerThread() const
|
||||
{
|
||||
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
|
||||
}
|
||||
|
||||
MediaQueue<MediaData>& VideoQueue() const {
|
||||
return mVideoQueue;
|
||||
}
|
||||
|
||||
const RefPtr<AbstractThread> mOwnerThread;
|
||||
RefPtr<MediaSink> mAudioSink;
|
||||
MediaQueue<MediaData>& mVideoQueue;
|
||||
VideoFrameContainer* mContainer;
|
||||
|
||||
// Producer ID to help ImageContainer distinguish different streams of
|
||||
// FrameIDs. A unique and immutable value per VideoSink.
|
||||
const ProducerID mProducerID;
|
||||
|
||||
// True if we are decoding a real-time stream.
|
||||
const bool mRealTime;
|
||||
|
||||
// Used to notify MediaDecoder's frame statistics
|
||||
FrameStatistics& mFrameStats;
|
||||
|
||||
RefPtr<GenericPromise> mEndPromise;
|
||||
MozPromiseHolder<GenericPromise> mEndPromiseHolder;
|
||||
MozPromiseRequestHolder<GenericPromise> mVideoSinkEndRequest;
|
||||
|
||||
// The presentation end time of the last video frame which has been displayed
|
||||
// in microseconds.
|
||||
int64_t mVideoFrameEndTime;
|
||||
|
||||
// Event listeners for VideoQueue
|
||||
MediaEventListener mPushListener;
|
||||
|
||||
// True if this sink is going to handle video track.
|
||||
bool mHasVideo;
|
||||
|
||||
// Used to trigger another update of rendered frames in next round.
|
||||
DelayedScheduler mUpdateScheduler;
|
||||
|
||||
// A delay duration to trigger next time UpdateRenderedVideoFrames().
|
||||
// Based on the default value in MDSM.
|
||||
const int mDelayDuration;
|
||||
|
||||
// Max frame number sent to compositor at a time.
|
||||
// Based on the pref value obtained in MDSM.
|
||||
const uint32_t mVideoQueueSendToCompositorSize;
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -8,6 +8,7 @@ UNIFIED_SOURCES += [
|
||||
'AudioSinkWrapper.cpp',
|
||||
'DecodedAudioDataSink.cpp',
|
||||
'DecodedStream.cpp',
|
||||
'VideoSink.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -371,8 +371,8 @@ void OggReader::SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials)
|
||||
}
|
||||
}
|
||||
|
||||
nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
nsresult
|
||||
OggReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
|
@ -69,8 +69,6 @@ public:
|
||||
return mTheoraState != 0 && mTheoraState->mActive;
|
||||
}
|
||||
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags) override;
|
||||
virtual RefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
virtual media::TimeIntervals GetBuffered() override;
|
||||
@ -78,6 +76,8 @@ public:
|
||||
virtual bool IsMediaSeekable() override;
|
||||
|
||||
private:
|
||||
virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) override;
|
||||
|
||||
// TODO: DEPRECATED. This uses synchronous decoding.
|
||||
// Stores the presentation time of the first frame we'd be able to play if
|
||||
// we started playback at the current position. Returns the first video
|
||||
|
@ -660,7 +660,7 @@ MediaCodecReader::ParseDataSegment(const char* aBuffer,
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
MediaCodecReader::AsyncReadMetadata()
|
||||
MediaCodecReader::AsyncReadMetadataInternal()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
|
@ -70,8 +70,11 @@ protected:
|
||||
// all contents have been continuously parsed. (ex. total duration of some
|
||||
// variable-bit-rate MP3 files.)
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
|
||||
public:
|
||||
|
||||
virtual RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
AsyncReadMetadataInternal() override;
|
||||
|
||||
public:
|
||||
// Flush the TaskQueue, flush MediaCodec and raise the mDiscontinuity.
|
||||
virtual nsresult ResetDecode() override;
|
||||
|
||||
@ -86,8 +89,6 @@ public:
|
||||
virtual bool HasAudio();
|
||||
virtual bool HasVideo();
|
||||
|
||||
virtual RefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata() override;
|
||||
|
||||
// Moves the decode head to aTime microseconds. aStartTime and aEndTime
|
||||
// denote the start and end times of the media in usecs, and aCurrentTime
|
||||
// is the current playback position in microseconds.
|
||||
|
@ -212,7 +212,7 @@ nsresult MediaOmxReader::InitOmxDecoder()
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
MediaOmxReader::AsyncReadMetadata()
|
||||
MediaOmxReader::AsyncReadMetadataInternal()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
EnsureActive();
|
||||
|
@ -72,6 +72,10 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) override;
|
||||
|
||||
virtual RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
AsyncReadMetadataInternal() override;
|
||||
|
||||
public:
|
||||
|
||||
virtual nsresult ResetDecode()
|
||||
@ -95,8 +99,6 @@ public:
|
||||
return mHasVideo;
|
||||
}
|
||||
|
||||
virtual RefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata() override;
|
||||
|
||||
virtual RefPtr<SeekPromise>
|
||||
Seek(int64_t aTime, int64_t aEndTime) override;
|
||||
|
||||
|
@ -89,13 +89,13 @@ RtspMediaCodecReader::RequestVideoData(bool aSkipToNextKeyframe,
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
RtspMediaCodecReader::AsyncReadMetadata()
|
||||
RtspMediaCodecReader::AsyncReadMetadataInternal()
|
||||
{
|
||||
mRtspResource->DisablePlayoutDelay();
|
||||
EnsureActive();
|
||||
|
||||
RefPtr<MediaDecoderReader::MetadataPromise> p =
|
||||
MediaCodecReader::AsyncReadMetadata();
|
||||
MediaCodecReader::AsyncReadMetadataInternal();
|
||||
|
||||
// Send a PAUSE to the RTSP server because the underlying media resource is
|
||||
// not ready.
|
||||
|
@ -58,12 +58,12 @@ public:
|
||||
// Disptach a DecodeAudioDataTask to decode audio data.
|
||||
virtual RefPtr<AudioDataPromise> RequestAudioData() override;
|
||||
|
||||
virtual RefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata()
|
||||
override;
|
||||
|
||||
virtual void HandleResourceAllocated() override;
|
||||
|
||||
private:
|
||||
virtual RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
AsyncReadMetadataInternal() override;
|
||||
|
||||
// A pointer to RtspMediaResource for calling the Rtsp specific function.
|
||||
// The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
|
||||
// holds the MediaDecoderStateMachine and RtspMediaResource.
|
||||
|
@ -87,14 +87,14 @@ void RtspOmxReader::EnsureActive() {
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
RtspOmxReader::AsyncReadMetadata()
|
||||
RtspOmxReader::AsyncReadMetadataInternal()
|
||||
{
|
||||
// Send a PLAY command to the RTSP server before reading metadata.
|
||||
// Because we might need some decoded samples to ensure we have configuration.
|
||||
mRtspResource->DisablePlayoutDelay();
|
||||
|
||||
RefPtr<MediaDecoderReader::MetadataPromise> p =
|
||||
MediaOmxReader::AsyncReadMetadata();
|
||||
MediaOmxReader::AsyncReadMetadataInternal();
|
||||
|
||||
// Send a PAUSE to the RTSP server because the underlying media resource is
|
||||
// not ready.
|
||||
|
@ -66,12 +66,12 @@ public:
|
||||
|
||||
virtual void SetIdle() override;
|
||||
|
||||
virtual RefPtr<MediaDecoderReader::MetadataPromise> AsyncReadMetadata()
|
||||
override;
|
||||
|
||||
virtual void HandleResourceAllocated() override;
|
||||
|
||||
private:
|
||||
virtual RefPtr<MediaDecoderReader::MetadataPromise>
|
||||
AsyncReadMetadataInternal() override;
|
||||
|
||||
// A pointer to RtspMediaResource for calling the Rtsp specific function.
|
||||
// The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
|
||||
// holds the MediaDecoderStateMachine and RtspMediaResource.
|
||||
|
@ -207,11 +207,13 @@ GonkDecoderManager::ProcessToDo(bool aEndOfStream)
|
||||
mWaitOutput.RemoveElementAt(0);
|
||||
mDecodeCallback->Output(output);
|
||||
} else if (rv == NS_ERROR_ABORT) {
|
||||
GMDD_LOG("eos output");
|
||||
mWaitOutput.RemoveElementAt(0);
|
||||
MOZ_ASSERT(mQueuedSamples.IsEmpty());
|
||||
MOZ_ASSERT(mWaitOutput.IsEmpty());
|
||||
// EOS
|
||||
MOZ_ASSERT(mQueuedSamples.IsEmpty());
|
||||
mWaitOutput.RemoveElementAt(0);
|
||||
// Sometimes the decoder attaches EOS flag to the final output buffer
|
||||
// instead of emits EOS by itself, hence the 2nd condition.
|
||||
MOZ_ASSERT(mWaitOutput.IsEmpty() ||
|
||||
(mWaitOutput.Length() == 1 && output.get()));
|
||||
if (output) {
|
||||
mDecodeCallback->Output(output);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user