зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to fx-team
This commit is contained in:
Коммит
f87169c55d
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<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="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
|
||||
|
|
|
@ -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="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="90f848a40efad820ab00fa52bec52dff37255b12"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<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="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="90f848a40efad820ab00fa52bec52dff37255b12"/>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"git": {
|
||||
"git_revision": "9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d",
|
||||
"git_revision": "4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "6238380e300e25c4ec8aea2a8804d0ca3675f7fb",
|
||||
"revision": "3057b08e087df6d1ee21ca3a3741e56bb78021a1",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="90f848a40efad820ab00fa52bec52dff37255b12"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="4d87112bbf48cdd09c19e553cc9aebd2a2c4ddfd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -796,7 +796,7 @@ EventSource::InitChannelAndRequestEventSource()
|
|||
|
||||
nsRefPtr<nsCORSListenerProxy> listener =
|
||||
new nsCORSListenerProxy(this, mPrincipal, mWithCredentials);
|
||||
rv = listener->Init(mHttpChannel);
|
||||
rv = listener->Init(mHttpChannel, DataURIHandling::Allow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Start reading from the channel
|
||||
|
|
|
@ -504,7 +504,7 @@ ImportLoader::Open()
|
|||
nsRefPtr<nsCORSListenerProxy> corsListener =
|
||||
new nsCORSListenerProxy(this, principal,
|
||||
/* aWithCredentials */ false);
|
||||
rv = corsListener->Init(channel, true);
|
||||
rv = corsListener->Init(channel, DataURIHandling::Allow);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
rv = channel->AsyncOpen(corsListener, nullptr);
|
||||
|
|
|
@ -118,6 +118,10 @@
|
|||
#include "mozilla/EMEUtils.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include <cutils/properties.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -1221,7 +1225,7 @@ Navigator::SendBeacon(const nsAString& aUrl,
|
|||
principal,
|
||||
true);
|
||||
|
||||
rv = cors->Init(channel, true);
|
||||
rv = cors->Init(channel, DataURIHandling::Allow);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
nsCOMPtr<nsINetworkInterceptController> interceptController = do_QueryInterface(docShell);
|
||||
|
@ -1453,6 +1457,17 @@ Navigator::GetFeature(const nsAString& aName, ErrorResult& aRv)
|
|||
} // hardware.memory
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (aName.EqualsLiteral("acl.version")) {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
uint32_t len = property_get("persist.acl.version", value, nullptr);
|
||||
if (len > 0) {
|
||||
p->MaybeResolve(NS_ConvertUTF8toUTF16(value));
|
||||
return p.forget();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
p->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return p.forget();
|
||||
}
|
||||
|
|
|
@ -347,7 +347,7 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
|
|||
nsRefPtr<nsCORSListenerProxy> corsListener =
|
||||
new nsCORSListenerProxy(listener, mDocument->NodePrincipal(),
|
||||
withCredentials);
|
||||
rv = corsListener->Init(channel);
|
||||
rv = corsListener->Init(channel, DataURIHandling::Allow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
listener = corsListener;
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ nsSyncLoader::LoadDocument(nsIChannel* aChannel,
|
|||
if (aLoaderPrincipal) {
|
||||
nsRefPtr<nsCORSListenerProxy> corsListener =
|
||||
new nsCORSListenerProxy(listener, aLoaderPrincipal, false);
|
||||
rv = corsListener->Init(mChannel);
|
||||
rv = corsListener->Init(mChannel, DataURIHandling::Disallow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
listener = corsListener;
|
||||
}
|
||||
|
|
|
@ -2889,7 +2889,7 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
|
|||
// a same-origin request right now, since it could be redirected.
|
||||
nsRefPtr<nsCORSListenerProxy> corsListener =
|
||||
new nsCORSListenerProxy(listener, mPrincipal, withCredentials);
|
||||
rv = corsListener->Init(mChannel, true);
|
||||
rv = corsListener->Init(mChannel, DataURIHandling::Allow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
listener = corsListener;
|
||||
}
|
||||
|
|
|
@ -780,3 +780,4 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g'
|
|||
[test_bug1118689.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g'
|
||||
[test_integer_attr_with_leading_zero.html]
|
||||
[test_script_loader_crossorigin_data_url.html]
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Test for handling of 'crossorigin' attribute on script with data: URL</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
// We're going to mess with window.onerror.
|
||||
setup({ allow_uncaught_exception: true });
|
||||
</script>
|
||||
<!-- First check that data: scripts with @crossorigin run at all -->
|
||||
<script>
|
||||
var ran = false;
|
||||
</script>
|
||||
<script crossorigin src="data:application/javascript,ran = true"></script>
|
||||
<script>
|
||||
test(function() {
|
||||
assert_true(ran);
|
||||
}, "script@crossorigin with data: src should have run");
|
||||
</script>
|
||||
<!-- Then check that their syntax errors are not sanitized -->
|
||||
<script>
|
||||
var errorFired = false;
|
||||
ran = false;
|
||||
window.onerror = function(message, uri, line) {
|
||||
errorFired = true;
|
||||
test(function() {
|
||||
assert_equals(line, 3);
|
||||
}, "Should have a useful line number for exception in script@crossorigin with data: src");
|
||||
}
|
||||
</script>
|
||||
<script crossorigin src="data:application/javascript,var%20a;%0aran=true%0anoSuchFunctionHere()"></script>
|
||||
<script>
|
||||
test(function() {
|
||||
assert_true(ran, "Script with error should have run");
|
||||
assert_true(errorFired, "Script with error should have fired onerror");
|
||||
}, "Should run and correctly fire onerror");
|
||||
</script>
|
|
@ -501,7 +501,7 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
|||
// directly.
|
||||
nsRefPtr<nsCORSListenerProxy> corsListener =
|
||||
new nsCORSListenerProxy(this, mPrincipal, useCredentials);
|
||||
rv = corsListener->Init(chan, true /* allow data uri */);
|
||||
rv = corsListener->Init(chan, DataURIHandling::Allow);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return FailWithNetworkError();
|
||||
}
|
||||
|
|
|
@ -636,7 +636,7 @@ void HTMLMediaElement::ShutdownDecoder()
|
|||
RemoveMediaElementFromURITable();
|
||||
NS_ASSERTION(mDecoder, "Must have decoder to shut down");
|
||||
mDecoder->Shutdown();
|
||||
mDecoder = nullptr;
|
||||
SetDecoder(nullptr);
|
||||
}
|
||||
|
||||
void HTMLMediaElement::AbortExistingLoads()
|
||||
|
@ -700,7 +700,7 @@ void HTMLMediaElement::AbortExistingLoads()
|
|||
mPendingEncryptedInitData.mInitDatas.Clear();
|
||||
#endif // MOZ_EME
|
||||
mSourcePointer = nullptr;
|
||||
mLastNextFrameStatus = NEXT_FRAME_UNINITIALIZED;
|
||||
mNextFrameStatus = NEXT_FRAME_UNINITIALIZED;
|
||||
|
||||
mTags = nullptr;
|
||||
|
||||
|
@ -941,13 +941,13 @@ void HTMLMediaElement::NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream)
|
|||
|
||||
bool videoHasChanged = IsVideo() && HasVideo() != !VideoTracks()->IsEmpty();
|
||||
|
||||
UpdateReadyStateForData(mLastNextFrameStatus);
|
||||
|
||||
if (videoHasChanged) {
|
||||
// We are a video element and HasVideo() changed so update the screen
|
||||
// wakelock
|
||||
NotifyOwnerDocumentActivityChanged();
|
||||
}
|
||||
|
||||
mReadyStateUpdater->Notify();
|
||||
}
|
||||
|
||||
void HTMLMediaElement::LoadFromSourceChildren()
|
||||
|
@ -1275,7 +1275,7 @@ nsresult HTMLMediaElement::LoadResource()
|
|||
new nsCORSListenerProxy(loadListener,
|
||||
NodePrincipal(),
|
||||
GetCORSMode() == CORS_USE_CREDENTIALS);
|
||||
rv = corsListener->Init(channel);
|
||||
rv = corsListener->Init(channel, DataURIHandling::Allow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
listener = corsListener;
|
||||
} else {
|
||||
|
@ -2045,8 +2045,9 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||
: nsGenericHTMLElement(aNodeInfo),
|
||||
mCurrentLoadID(0),
|
||||
mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
|
||||
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
|
||||
mLastNextFrameStatus(NEXT_FRAME_UNINITIALIZED),
|
||||
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING, "HTMLMediaElement::mReadyState"),
|
||||
mReadyStateUpdater("HTMLMediaElement::mReadyStateUpdater"),
|
||||
mNextFrameStatus(NEXT_FRAME_UNINITIALIZED, "HTMLMediaElement::mNextFrameStatus"),
|
||||
mLoadWaitStatus(NOT_WAITING),
|
||||
mVolume(1.0),
|
||||
mPreloadAction(PRELOAD_UNDEFINED),
|
||||
|
@ -2087,7 +2088,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||
mMediaSecurityVerified(false),
|
||||
mCORSMode(CORS_NONE),
|
||||
mIsEncrypted(false),
|
||||
mDownloadSuspendedByCache(false),
|
||||
mDownloadSuspendedByCache(false, "HTMLMediaElement::mDownloadSuspendedByCache"),
|
||||
mAudioChannelFaded(false),
|
||||
mPlayingThroughTheAudioChannel(false),
|
||||
mDisableVideo(false),
|
||||
|
@ -2100,6 +2101,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||
if (!gMediaElementEventsLog) {
|
||||
gMediaElementEventsLog = PR_NewLogModule("nsMediaElementEvents");
|
||||
}
|
||||
EnsureStateWatchingLog();
|
||||
#endif
|
||||
|
||||
mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
|
||||
|
@ -2108,6 +2110,14 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||
|
||||
RegisterActivityObserver();
|
||||
NotifyOwnerDocumentActivityChanged();
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mReadyStateUpdater->AddWeakCallback(this, &HTMLMediaElement::UpdateReadyStateInternal);
|
||||
mReadyStateUpdater->Watch(mNextFrameStatus);
|
||||
mReadyStateUpdater->Watch(mDownloadSuspendedByCache);
|
||||
// Paradoxically, there is a self-edge whereby UpdateReadyStateInternal refuses
|
||||
// to run until mReadyState reaches at least HAVE_METADATA by some other means.
|
||||
mReadyStateUpdater->Watch(mReadyState);
|
||||
}
|
||||
|
||||
HTMLMediaElement::~HTMLMediaElement()
|
||||
|
@ -2604,8 +2614,8 @@ HTMLMediaElement::ReportMSETelemetry()
|
|||
ErrorResult ignore;
|
||||
stalled = index != TimeRanges::NoIndex &&
|
||||
(ranges->End(index, ignore) - t) < errorMargin;
|
||||
stalled |= mLastNextFrameStatus == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING &&
|
||||
mReadyState == HTMLMediaElement::HAVE_CURRENT_DATA;
|
||||
stalled |= mNextFrameStatus == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING &&
|
||||
mReadyState == HTMLMediaElement::HAVE_CURRENT_DATA;
|
||||
if (stalled) {
|
||||
state = STALLED;
|
||||
}
|
||||
|
@ -2751,7 +2761,7 @@ nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel,
|
|||
// RtspMediaResource.
|
||||
if (DecoderTraits::DecoderWaitsForOnConnected(mimeType)) {
|
||||
decoder->SetResource(resource);
|
||||
mDecoder = decoder;
|
||||
SetDecoder(decoder);
|
||||
if (aListener) {
|
||||
*aListener = nullptr;
|
||||
}
|
||||
|
@ -2777,7 +2787,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
|
|||
mPendingEvents.Clear();
|
||||
// Set mDecoder now so if methods like GetCurrentSrc get called between
|
||||
// here and Load(), they work.
|
||||
mDecoder = aDecoder;
|
||||
SetDecoder(aDecoder);
|
||||
|
||||
// Tell the decoder about its MediaResource now so things like principals are
|
||||
// available immediately.
|
||||
|
@ -2802,7 +2812,7 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
|
|||
|
||||
nsresult rv = aDecoder->Load(aListener, aCloneDonor);
|
||||
if (NS_FAILED(rv)) {
|
||||
mDecoder = nullptr;
|
||||
SetDecoder(nullptr);
|
||||
LOG(PR_LOG_DEBUG, ("%p Failed to load for decoder %p", this, aDecoder));
|
||||
return rv;
|
||||
}
|
||||
|
@ -3213,7 +3223,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
|
|||
if (!aInfo->HasVideo()) {
|
||||
ResetState();
|
||||
} else {
|
||||
UpdateReadyStateForData(mLastNextFrameStatus);
|
||||
mReadyStateUpdater->Notify();
|
||||
}
|
||||
|
||||
if (IsVideo() && aInfo->HasVideo()) {
|
||||
|
@ -3496,7 +3506,16 @@ bool HTMLMediaElement::IsCORSSameOrigin()
|
|||
|
||||
void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame)
|
||||
{
|
||||
mLastNextFrameStatus = aNextFrame;
|
||||
mNextFrameStatus = aNextFrame;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::UpdateReadyStateInternal()
|
||||
{
|
||||
if (!mDecoder && !mSrcStream) {
|
||||
// Not initialized - bail out.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDecoder && mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
||||
// aNextFrame might have a next frame because the decoder can advance
|
||||
|
@ -3520,7 +3539,7 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu
|
|||
MetadataLoaded(&mediaInfo, nsAutoPtr<const MetadataTags>(nullptr));
|
||||
}
|
||||
|
||||
if (aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
|
||||
if (mNextFrameStatus == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
||||
return;
|
||||
}
|
||||
|
@ -3549,9 +3568,9 @@ void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatu
|
|||
return;
|
||||
}
|
||||
|
||||
if (aNextFrame != MediaDecoderOwner::NEXT_FRAME_AVAILABLE) {
|
||||
if (mNextFrameStatus != MediaDecoderOwner::NEXT_FRAME_AVAILABLE) {
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
||||
if (!mWaitingFired && aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING) {
|
||||
if (!mWaitingFired && mNextFrameStatus == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING) {
|
||||
FireTimeUpdate(false);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
|
||||
mWaitingFired = true;
|
||||
|
@ -3870,7 +3889,7 @@ void HTMLMediaElement::UpdateMediaSize(const nsIntSize& aSize)
|
|||
}
|
||||
|
||||
mMediaInfo.mVideo.mDisplay = aSize;
|
||||
UpdateReadyStateForData(mLastNextFrameStatus);
|
||||
mReadyStateUpdater->Notify();
|
||||
}
|
||||
|
||||
void HTMLMediaElement::UpdateInitialMediaSize(const nsIntSize& aSize)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#ifdef MOZ_EME
|
||||
#include "mozilla/dom/MediaKeys.h"
|
||||
#endif
|
||||
#include "StateWatching.h"
|
||||
#include "nsGkAtoms.h"
|
||||
|
||||
// X.h on Linux #defines CurrentTime as 0L, so we have to #undef it here.
|
||||
|
@ -647,6 +648,17 @@ protected:
|
|||
class StreamListener;
|
||||
class StreamSizeListener;
|
||||
|
||||
void SetDecoder(MediaDecoder* aDecoder)
|
||||
{
|
||||
if (mDecoder) {
|
||||
mReadyStateUpdater->Unwatch(mDecoder->ReadyStateWatchTarget());
|
||||
}
|
||||
mDecoder = aDecoder;
|
||||
if (mDecoder) {
|
||||
mReadyStateUpdater->Watch(mDecoder->ReadyStateWatchTarget());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void GetItemValueText(DOMString& text) override;
|
||||
virtual void SetItemValueText(const nsAString& text) override;
|
||||
|
||||
|
@ -1013,6 +1025,9 @@ protected:
|
|||
// MediaElement doesn't yet have one then it will create it.
|
||||
TextTrackManager* GetOrCreateTextTrackManager();
|
||||
|
||||
// Recomputes ready state and fires events as necessary based on current state.
|
||||
void UpdateReadyStateInternal();
|
||||
|
||||
class nsAsyncEventRunner;
|
||||
using nsGenericHTMLElement::DispatchEvent;
|
||||
// For nsAsyncEventRunner.
|
||||
|
@ -1090,10 +1105,12 @@ protected:
|
|||
// Media loading flags. See:
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#video)
|
||||
nsMediaNetworkState mNetworkState;
|
||||
nsMediaReadyState mReadyState;
|
||||
Watchable<nsMediaReadyState> mReadyState;
|
||||
|
||||
WatcherHolder mReadyStateUpdater;
|
||||
|
||||
// Last value passed from codec or stream source to UpdateReadyStateForData.
|
||||
NextFrameStatus mLastNextFrameStatus;
|
||||
Watchable<NextFrameStatus> mNextFrameStatus;
|
||||
|
||||
enum LoadAlgorithmState {
|
||||
// No load algorithm instance is waiting for a source to be added to the
|
||||
|
@ -1328,7 +1345,7 @@ protected:
|
|||
#endif // MOZ_EME
|
||||
|
||||
// True if the media's channel's download has been suspended.
|
||||
bool mDownloadSuspendedByCache;
|
||||
Watchable<bool> mDownloadSuspendedByCache;
|
||||
|
||||
// Audio Channel.
|
||||
AudioChannel mAudioChannel;
|
||||
|
|
|
@ -68,7 +68,12 @@ public:
|
|||
return in;
|
||||
}
|
||||
|
||||
void FireTailDispatcher() { MOZ_ASSERT(mTailDispatcher.isSome()); mTailDispatcher.reset(); }
|
||||
void FireTailDispatcher()
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome());
|
||||
mTailDispatcher.ref().DrainDirectTasks();
|
||||
mTailDispatcher.reset();
|
||||
}
|
||||
|
||||
virtual TaskDispatcher& TailDispatcher() override
|
||||
{
|
||||
|
|
|
@ -585,6 +585,7 @@ bool MediaDecoder::IsInfinite()
|
|||
}
|
||||
|
||||
MediaDecoder::MediaDecoder() :
|
||||
mReadyStateWatchTarget("MediaDecoder::mReadyStateWatchTarget"),
|
||||
mDecoderPosition(0),
|
||||
mPlaybackPosition(0),
|
||||
mCurrentTime(0.0),
|
||||
|
@ -595,7 +596,7 @@ MediaDecoder::MediaDecoder() :
|
|||
mMediaSeekable(true),
|
||||
mSameOriginMedia(false),
|
||||
mReentrantMonitor("media.decoder"),
|
||||
mPlayState(PLAY_STATE_LOADING),
|
||||
mPlayState(PLAY_STATE_LOADING, "MediaDecoder::mPlayState"),
|
||||
mNextState(PLAY_STATE_PAUSED),
|
||||
mIgnoreProgressData(false),
|
||||
mInfiniteStream(false),
|
||||
|
@ -622,8 +623,11 @@ MediaDecoder::MediaDecoder() :
|
|||
if (!gMediaDecoderLog) {
|
||||
gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
|
||||
}
|
||||
EnsureStateWatchingLog();
|
||||
#endif
|
||||
mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
|
||||
|
||||
mReadyStateWatchTarget->Watch(mPlayState);
|
||||
}
|
||||
|
||||
bool MediaDecoder::Init(MediaDecoderOwner* aOwner)
|
||||
|
@ -1140,7 +1144,6 @@ void MediaDecoder::NotifySuspendedStatusChanged()
|
|||
if (mResource && mOwner) {
|
||||
bool suspended = mResource->IsSuspendedByCache();
|
||||
mOwner->NotifySuspendedByCache(suspended);
|
||||
UpdateReadyStateForData();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1704,8 +1707,8 @@ void MediaDecoder::UnpinForSeek()
|
|||
bool MediaDecoder::CanPlayThrough()
|
||||
{
|
||||
Statistics stats = GetStatistics();
|
||||
NS_ENSURE_TRUE(mDecoderStateMachine, false);
|
||||
|
||||
NS_ASSERTION(mDecoderStateMachine, "CanPlayThrough should have state machine!");
|
||||
if (mDecoderStateMachine->IsRealTime() ||
|
||||
(stats.mTotalBytes < 0 && stats.mDownloadRateReliable) ||
|
||||
(stats.mTotalBytes >= 0 && stats.mTotalBytes == stats.mDownloadPosition)) {
|
||||
|
|
|
@ -196,6 +196,7 @@ destroying the MediaDecoder object.
|
|||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "StateWatching.h"
|
||||
#include "necko-config.h"
|
||||
#ifdef MOZ_EME
|
||||
#include "mozilla/CDMProxy.h"
|
||||
|
@ -1040,6 +1041,8 @@ public:
|
|||
GetFrameStatistics().NotifyDecodedFrames(aParsed, aDecoded, aDropped);
|
||||
}
|
||||
|
||||
WatchTarget& ReadyStateWatchTarget() { return *mReadyStateWatchTarget; }
|
||||
|
||||
protected:
|
||||
virtual ~MediaDecoder();
|
||||
void SetStateMachineParameters();
|
||||
|
@ -1055,6 +1058,8 @@ protected:
|
|||
// Return true if the decoder has reached the end of playback
|
||||
bool IsEnded() const;
|
||||
|
||||
WatcherHolder mReadyStateWatchTarget;
|
||||
|
||||
/******
|
||||
* The following members should be accessed with the decoder lock held.
|
||||
******/
|
||||
|
@ -1137,7 +1142,7 @@ protected:
|
|||
// OR on the main thread.
|
||||
// Any change to the state on the main thread must call NotifyAll on the
|
||||
// monitor so the decode thread can wake up.
|
||||
PlayState mPlayState;
|
||||
Watchable<PlayState> mPlayState;
|
||||
|
||||
// The state to change to after a seek or load operation.
|
||||
// This can only be changed on the main thread while holding the decoder
|
||||
|
|
|
@ -35,6 +35,7 @@ extern PRLogModuleInfo* gMediaDecoderLog;
|
|||
|
||||
|
||||
PRLogModuleInfo* gMediaPromiseLog;
|
||||
PRLogModuleInfo* gStateWatchingLog;
|
||||
|
||||
void
|
||||
EnsureMediaPromiseLog()
|
||||
|
@ -44,6 +45,14 @@ EnsureMediaPromiseLog()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
EnsureStateWatchingLog()
|
||||
{
|
||||
if (!gStateWatchingLog) {
|
||||
gStateWatchingLog = PR_NewLogModule("StateWatching");
|
||||
}
|
||||
}
|
||||
|
||||
class VideoQueueMemoryFunctor : public nsDequeFunctor {
|
||||
public:
|
||||
VideoQueueMemoryFunctor() : mSize(0) {}
|
||||
|
@ -88,6 +97,7 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
|||
{
|
||||
MOZ_COUNT_CTOR(MediaDecoderReader);
|
||||
EnsureMediaPromiseLog();
|
||||
EnsureStateWatchingLog();
|
||||
}
|
||||
|
||||
MediaDecoderReader::~MediaDecoderReader()
|
||||
|
|
|
@ -619,7 +619,7 @@ nsresult ChannelMediaResource::OpenChannel(nsIStreamListener** aStreamListener)
|
|||
element->NodePrincipal(),
|
||||
false);
|
||||
NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
rv = crossSiteListener->Init(mChannel);
|
||||
rv = crossSiteListener->Init(mChannel, DataURIHandling::Allow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
listener = crossSiteListener;
|
||||
} else {
|
||||
|
|
|
@ -1817,8 +1817,19 @@ MediaStreamGraphImpl::RunInStableState(bool aSourceIsMSG)
|
|||
mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
|
||||
#endif
|
||||
|
||||
TaskDispatcher& tailDispatcher = AbstractThread::MainThread()->TailDispatcher();
|
||||
for (uint32_t i = 0; i < runnables.Length(); ++i) {
|
||||
runnables[i]->Run();
|
||||
// "Direct" tail dispatcher are supposed to run immediately following the
|
||||
// execution of the current task. So the meta-tasking that we do here in
|
||||
// RunInStableState() breaks that abstraction a bit unless we handle it here.
|
||||
//
|
||||
// This is particularly important because we can end up with a "stream
|
||||
// ended" notification immediately following a "stream available" notification,
|
||||
// and we need to make sure that the watcher responding to "stream available"
|
||||
// has a chance to run before the second notification starts tearing things
|
||||
// down.
|
||||
tailDispatcher.DrainDirectTasks();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,8 @@ protected:
|
|||
|
||||
~AutoTaskGuard()
|
||||
{
|
||||
DrainDirectTasks();
|
||||
|
||||
MOZ_ASSERT(mQueue->mRunningThread == NS_GetCurrentThread());
|
||||
mQueue->mRunningThread = nullptr;
|
||||
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(StateWatching_h_)
|
||||
#define StateWatching_h_
|
||||
|
||||
#include "AbstractThread.h"
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
/*
|
||||
* The state-watching machinery automates the process of responding to changes
|
||||
* in various pieces of state.
|
||||
*
|
||||
* A standard programming pattern is as follows:
|
||||
*
|
||||
* mFoo = ...;
|
||||
* NotifyStuffChanged();
|
||||
* ...
|
||||
* mBar = ...;
|
||||
* NotifyStuffChanged();
|
||||
*
|
||||
* This pattern is error-prone and difficult to audit because it requires the
|
||||
* programmer to manually trigger the update routine. This can be especially
|
||||
* problematic when the update routine depends on numerous pieces of state, and
|
||||
* when that state is modified across a variety of helper methods. In these
|
||||
* cases the responsibility for invoking the routine is often unclear, causing
|
||||
* developers to scatter calls to it like pixie dust. This can result in
|
||||
* duplicate invocations (which is wasteful) and missing invocations in corner-
|
||||
* cases (which is a source of bugs).
|
||||
*
|
||||
* This file provides a set of primitives that automatically handle updates and
|
||||
* allow the programmers to explicitly construct a graph of state dependencies.
|
||||
* When used correctly, it eliminates the guess-work and wasted cycles described
|
||||
* above.
|
||||
*
|
||||
* There are two basic pieces:
|
||||
* (1) Objects that can be watched for updates. These inherit WatchTarget.
|
||||
* (2) Objects that receive objects and trigger processing. These inherit
|
||||
* AbstractWatcher. Note that some watchers may themselves be watched,
|
||||
* allowing watchers to be composed together.
|
||||
*
|
||||
* Note that none of this machinery is thread-safe - it must all happen on the
|
||||
* same owning thread. To solve multi-threaded use-cases, use state mirroring
|
||||
* and watch the mirrored value.
|
||||
*
|
||||
* Given that semantics may change and comments tend to go out of date, we
|
||||
* deliberately don't provide usage examples here. Grep around to find them.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern PRLogModuleInfo* gStateWatchingLog;
|
||||
|
||||
void EnsureStateWatchingLog();
|
||||
|
||||
#define WATCH_LOG(x, ...) \
|
||||
MOZ_ASSERT(gStateWatchingLog); \
|
||||
PR_LOG(gStateWatchingLog, PR_LOG_DEBUG, (x, ##__VA_ARGS__))
|
||||
|
||||
/*
|
||||
* AbstractWatcher is a superclass from which all watchers must inherit.
|
||||
*/
|
||||
class AbstractWatcher
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(AbstractWatcher)
|
||||
AbstractWatcher() : mDestroyed(false) {}
|
||||
bool IsDestroyed() { return mDestroyed; }
|
||||
virtual void Notify() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~AbstractWatcher() {}
|
||||
virtual void CustomDestroy() {}
|
||||
|
||||
private:
|
||||
// Only the holder is allowed to invoke Destroy().
|
||||
friend class WatcherHolder;
|
||||
void Destroy()
|
||||
{
|
||||
mDestroyed = true;
|
||||
CustomDestroy();
|
||||
}
|
||||
|
||||
bool mDestroyed;
|
||||
};
|
||||
|
||||
/*
|
||||
* WatchTarget is a superclass from which all watchable things must inherit.
|
||||
* Unlike AbstractWatcher, it is a fully-implemented Mix-in, and the subclass
|
||||
* needs only to invoke NotifyWatchers when something changes.
|
||||
*
|
||||
* The functionality that this class provides is not threadsafe, and should only
|
||||
* be used on the thread that owns that WatchTarget.
|
||||
*/
|
||||
class WatchTarget
|
||||
{
|
||||
public:
|
||||
explicit WatchTarget(const char* aName) : mName(aName) {}
|
||||
|
||||
void AddWatcher(AbstractWatcher* aWatcher)
|
||||
{
|
||||
MOZ_ASSERT(!mWatchers.Contains(aWatcher));
|
||||
mWatchers.AppendElement(aWatcher);
|
||||
aWatcher->Notify();
|
||||
}
|
||||
|
||||
void RemoveWatcher(AbstractWatcher* aWatcher)
|
||||
{
|
||||
MOZ_ASSERT(mWatchers.Contains(aWatcher));
|
||||
mWatchers.RemoveElement(aWatcher);
|
||||
}
|
||||
|
||||
protected:
|
||||
void NotifyWatchers()
|
||||
{
|
||||
WATCH_LOG("%s[%p] notifying watchers\n", mName, this);
|
||||
PruneWatchers();
|
||||
for (size_t i = 0; i < mWatchers.Length(); ++i) {
|
||||
mWatchers[i]->Notify();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// We don't have Watchers explicitly unregister themselves when they die,
|
||||
// because then they'd need back-references to all the WatchTargets they're
|
||||
// subscribed to, and WatchTargets aren't reference-counted. So instead we
|
||||
// just prune dead ones at appropriate times, which works just fine.
|
||||
void PruneWatchers()
|
||||
{
|
||||
for (int i = mWatchers.Length() - 1; i >= 0; --i) {
|
||||
if (mWatchers[i]->IsDestroyed()) {
|
||||
mWatchers.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsRefPtr<AbstractWatcher>> mWatchers;
|
||||
const char* mName;
|
||||
};
|
||||
|
||||
/*
|
||||
* Watchable is a wrapper class that turns any primitive into a WatchTarget.
|
||||
*/
|
||||
template<typename T>
|
||||
class Watchable : public WatchTarget
|
||||
{
|
||||
public:
|
||||
Watchable(const T& aInitialValue, const char* aName)
|
||||
: WatchTarget(aName), mValue(aInitialValue) {}
|
||||
|
||||
operator const T&() const { return mValue; }
|
||||
Watchable& operator=(const T& aNewValue)
|
||||
{
|
||||
if (aNewValue != mValue) {
|
||||
mValue = aNewValue;
|
||||
NotifyWatchers();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
Watchable(const Watchable& aOther); // Not implemented
|
||||
Watchable& operator=(const Watchable& aOther); // Not implemented
|
||||
|
||||
T mValue;
|
||||
};
|
||||
|
||||
/*
|
||||
* Watcher is the concrete AbstractWatcher implementation. It registers itself with
|
||||
* various WatchTargets, and accepts any number of callbacks that will be
|
||||
* invoked whenever any WatchTarget notifies of a change. It may also be watched
|
||||
* by other watchers.
|
||||
*
|
||||
* When a notification is received, a runnable is passed as "direct" to the
|
||||
* thread's tail dispatcher, which run directly (rather than via dispatch) when
|
||||
* the tail dispatcher fires. All subsequent notifications are ignored until the
|
||||
* runnable executes, triggering the updates and resetting the flags.
|
||||
*/
|
||||
class Watcher : public AbstractWatcher, public WatchTarget
|
||||
{
|
||||
public:
|
||||
explicit Watcher(const char* aName)
|
||||
: WatchTarget(aName), mNotifying(false) {}
|
||||
|
||||
void Notify() override
|
||||
{
|
||||
if (mNotifying) {
|
||||
return;
|
||||
}
|
||||
mNotifying = true;
|
||||
|
||||
// Queue up our notification jobs to run in a stable state.
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &Watcher::DoNotify);
|
||||
AbstractThread::GetCurrent()->TailDispatcher().AddDirectTask(r.forget());
|
||||
}
|
||||
|
||||
void Watch(WatchTarget& aTarget) { aTarget.AddWatcher(this); }
|
||||
void Unwatch(WatchTarget& aTarget) { aTarget.RemoveWatcher(this); }
|
||||
|
||||
template<typename ThisType>
|
||||
void AddWeakCallback(ThisType* aThisVal, void(ThisType::*aMethod)())
|
||||
{
|
||||
mCallbacks.AppendElement(NS_NewNonOwningRunnableMethod(aThisVal, aMethod));
|
||||
}
|
||||
|
||||
protected:
|
||||
void CustomDestroy() override { mCallbacks.Clear(); }
|
||||
|
||||
void DoNotify()
|
||||
{
|
||||
MOZ_ASSERT(mNotifying);
|
||||
mNotifying = false;
|
||||
|
||||
// Notify dependent watchers.
|
||||
NotifyWatchers();
|
||||
|
||||
for (size_t i = 0; i < mCallbacks.Length(); ++i) {
|
||||
mCallbacks[i]->Run();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mCallbacks;
|
||||
|
||||
bool mNotifying;
|
||||
};
|
||||
|
||||
/*
|
||||
* WatcherHolder encapsulates a Watcher and handles lifetime issues. Use it to
|
||||
* holder watcher members.
|
||||
*/
|
||||
class WatcherHolder
|
||||
{
|
||||
public:
|
||||
explicit WatcherHolder(const char* aName) { mWatcher = new Watcher(aName); }
|
||||
operator Watcher*() { return mWatcher; }
|
||||
Watcher* operator->() { return mWatcher; }
|
||||
|
||||
~WatcherHolder()
|
||||
{
|
||||
mWatcher->Destroy();
|
||||
mWatcher = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<Watcher> mWatcher;
|
||||
};
|
||||
|
||||
|
||||
#undef WATCH_LOG
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -28,7 +28,7 @@ namespace mozilla {
|
|||
*
|
||||
* TaskDispatcher is a general abstract class that accepts tasks and dispatches
|
||||
* them at some later point. These groups of tasks are per-target-thread, and
|
||||
* contain separate queues for two kinds of tasks - "state change tasks" (which
|
||||
* contain separate queues for several kinds of tasks (see comments below). - "state change tasks" (which
|
||||
* run first, and are intended to be used to update the value held by mirrors),
|
||||
* and regular tasks, which are other arbitrary operations that the are gated
|
||||
* to run after all the state changes have completed.
|
||||
|
@ -39,13 +39,25 @@ public:
|
|||
TaskDispatcher() {}
|
||||
virtual ~TaskDispatcher() {}
|
||||
|
||||
// Direct tasks are run directly (rather than dispatched asynchronously) when
|
||||
// the tail dispatcher fires. A direct task may cause other tasks to be added
|
||||
// to the tail dispatcher.
|
||||
virtual void AddDirectTask(already_AddRefed<nsIRunnable> aRunnable) = 0;
|
||||
|
||||
// State change tasks are dispatched asynchronously always run before regular
|
||||
// tasks. They are intended to be used to update the value held by mirrors
|
||||
// before any other dispatched tasks are run on the target thread.
|
||||
virtual void AddStateChangeTask(AbstractThread* aThread,
|
||||
already_AddRefed<nsIRunnable> aRunnable) = 0;
|
||||
|
||||
// Regular tasks are dispatched asynchronously, and run after state change
|
||||
// tasks.
|
||||
virtual void AddTask(AbstractThread* aThread,
|
||||
already_AddRefed<nsIRunnable> aRunnable,
|
||||
AbstractThread::DispatchFailureHandling aFailureHandling = AbstractThread::AssertDispatchSuccess) = 0;
|
||||
|
||||
virtual bool HasTasksFor(AbstractThread* aThread) = 0;
|
||||
virtual void DrainDirectTasks() = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -58,6 +70,17 @@ public:
|
|||
explicit AutoTaskDispatcher(bool aIsTailDispatcher = false) : mIsTailDispatcher(aIsTailDispatcher) {}
|
||||
~AutoTaskDispatcher()
|
||||
{
|
||||
// Given that direct tasks may trigger other code that uses the tail
|
||||
// dispatcher, it's better to avoid processing them in the tail dispatcher's
|
||||
// destructor. So we require TailDispatchers to manually invoke
|
||||
// DrainDirectTasks before the AutoTaskDispatcher gets destroyed. In truth,
|
||||
// this is only necessary in the case where this AutoTaskDispatcher can be
|
||||
// accessed by the direct tasks it dispatches (true for TailDispatchers, but
|
||||
// potentially not true for other hypothetical AutoTaskDispatchers). Feel
|
||||
// free to loosen this restriction to apply only to mIsTailDispatcher if a
|
||||
// use-case requires it.
|
||||
MOZ_ASSERT(mDirectTasks.empty());
|
||||
|
||||
for (size_t i = 0; i < mTaskGroups.Length(); ++i) {
|
||||
UniquePtr<PerThreadTaskGroup> group(Move(mTaskGroups[i]));
|
||||
nsRefPtr<AbstractThread> thread = group->mThread;
|
||||
|
@ -70,6 +93,20 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void DrainDirectTasks() override
|
||||
{
|
||||
while (!mDirectTasks.empty()) {
|
||||
nsCOMPtr<nsIRunnable> r = mDirectTasks.front();
|
||||
mDirectTasks.pop();
|
||||
r->Run();
|
||||
}
|
||||
}
|
||||
|
||||
void AddDirectTask(already_AddRefed<nsIRunnable> aRunnable) override
|
||||
{
|
||||
mDirectTasks.push(Move(aRunnable));
|
||||
}
|
||||
|
||||
void AddStateChangeTask(AbstractThread* aThread,
|
||||
already_AddRefed<nsIRunnable> aRunnable) override
|
||||
{
|
||||
|
@ -90,7 +127,10 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool HasTasksFor(AbstractThread* aThread) override { return !!GetTaskGroup(aThread); }
|
||||
bool HasTasksFor(AbstractThread* aThread) override
|
||||
{
|
||||
return !!GetTaskGroup(aThread) || (aThread == AbstractThread::GetCurrent() && !mDirectTasks.empty());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
@ -118,18 +158,37 @@ private:
|
|||
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
// State change tasks get run all together before any code is run, so
|
||||
// that all state changes are made in an atomic unit.
|
||||
for (size_t i = 0; i < mTasks->mStateChangeTasks.Length(); ++i) {
|
||||
mTasks->mStateChangeTasks[i]->Run();
|
||||
}
|
||||
|
||||
// Once the state changes have completed, drain any direct tasks
|
||||
// generated by those state changes (i.e. watcher notification tasks).
|
||||
// This needs to be outside the loop because we don't want to run code
|
||||
// that might observe intermediate states.
|
||||
MaybeDrainDirectTasks();
|
||||
|
||||
for (size_t i = 0; i < mTasks->mRegularTasks.Length(); ++i) {
|
||||
mTasks->mRegularTasks[i]->Run();
|
||||
|
||||
// Scope direct tasks tightly to the task that generated them.
|
||||
MaybeDrainDirectTasks();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
void MaybeDrainDirectTasks()
|
||||
{
|
||||
AbstractThread* currentThread = AbstractThread::GetCurrent();
|
||||
if (currentThread) {
|
||||
currentThread->TailDispatcher().DrainDirectTasks();
|
||||
}
|
||||
}
|
||||
|
||||
UniquePtr<PerThreadTaskGroup> mTasks;
|
||||
};
|
||||
|
||||
|
@ -156,6 +215,9 @@ private:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Direct tasks.
|
||||
std::queue<nsCOMPtr<nsIRunnable>> mDirectTasks;
|
||||
|
||||
// Task groups, organized by thread.
|
||||
nsTArray<UniquePtr<PerThreadTaskGroup>> mTaskGroups;
|
||||
|
||||
|
|
|
@ -421,7 +421,12 @@ already_AddRefed<AndroidMediaResourceServer>
|
|||
AndroidMediaResourceServer::Start()
|
||||
{
|
||||
nsRefPtr<AndroidMediaResourceServer> server = new AndroidMediaResourceServer();
|
||||
NS_DispatchToMainThread(server, NS_DISPATCH_SYNC);
|
||||
// We should fix this up - see bug 1157476.
|
||||
if (NS_IsMainThread()) {
|
||||
server->Run();
|
||||
} else {
|
||||
NS_DispatchToMainThread(server, NS_DISPATCH_SYNC);
|
||||
}
|
||||
return server.forget();
|
||||
}
|
||||
|
||||
|
|
|
@ -158,6 +158,9 @@ FFmpegH264Decoder<LIBAV_VER>::ReleaseBufferCb(AVCodecContext* aCodecContext,
|
|||
if (image) {
|
||||
image->Release();
|
||||
}
|
||||
for (uint32_t i = 0; i < AV_NUM_DATA_POINTERS; i++) {
|
||||
aFrame->data[i] = nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -36,7 +36,6 @@ runWithMSE(function (ms, v) {
|
|||
});
|
||||
|
||||
v.addEventListener("seeked", function () {
|
||||
ok(v.readyState >= v.HAVE_FUTURE_DATA, "readyState is >= FUTURE_DATA");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -137,6 +137,7 @@ EXPORTS += [
|
|||
'SelfRef.h',
|
||||
'SharedBuffer.h',
|
||||
'SharedThreadPool.h',
|
||||
'StateWatching.h',
|
||||
'StreamBuffer.h',
|
||||
'TaskDispatcher.h',
|
||||
'ThreadPoolCOMListener.h',
|
||||
|
|
|
@ -471,7 +471,7 @@ nsCORSListenerProxy::~nsCORSListenerProxy()
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsCORSListenerProxy::Init(nsIChannel* aChannel, bool aAllowDataURI)
|
||||
nsCORSListenerProxy::Init(nsIChannel* aChannel, DataURIHandling aAllowDataURI)
|
||||
{
|
||||
aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
|
||||
aChannel->SetNotificationCallbacks(this);
|
||||
|
@ -798,7 +798,7 @@ nsCORSListenerProxy::OnRedirectVerifyCallback(nsresult result)
|
|||
NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
nsresult rv = UpdateChannel(mNewRedirectChannel);
|
||||
nsresult rv = UpdateChannel(mNewRedirectChannel, DataURIHandling::Disallow);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("nsCORSListenerProxy::OnRedirectVerifyCallback: "
|
||||
"UpdateChannel() returned failure");
|
||||
|
@ -818,7 +818,8 @@ nsCORSListenerProxy::OnRedirectVerifyCallback(nsresult result)
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, bool aAllowDataURI)
|
||||
nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
|
||||
DataURIHandling aAllowDataURI)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri, originalURI;
|
||||
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
|
||||
|
@ -827,7 +828,7 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, bool aAllowDataURI)
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// exempt data URIs from the same origin check.
|
||||
if (aAllowDataURI && originalURI == uri) {
|
||||
if (aAllowDataURI == DataURIHandling::Allow && originalURI == uri) {
|
||||
bool dataScheme = false;
|
||||
rv = uri->SchemeIs("data", &dataScheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -1240,7 +1241,7 @@ NS_StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
new nsCORSListenerProxy(preflightListener, aPrincipal,
|
||||
aWithCredentials, method,
|
||||
aUnsafeHeaders);
|
||||
rv = corsListener->Init(preflightChannel);
|
||||
rv = corsListener->Init(preflightChannel, DataURIHandling::Disallow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
preflightListener = corsListener;
|
||||
|
||||
|
|
|
@ -29,6 +29,12 @@ NS_StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
nsTArray<nsCString>& aACUnsafeHeaders,
|
||||
nsIChannel** aPreflightChannel);
|
||||
|
||||
enum class DataURIHandling
|
||||
{
|
||||
Allow,
|
||||
Disallow
|
||||
};
|
||||
|
||||
class nsCORSListenerProxy final : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIChannelEventSink,
|
||||
|
@ -56,14 +62,14 @@ public:
|
|||
|
||||
static void Shutdown();
|
||||
|
||||
nsresult Init(nsIChannel* aChannel, bool aAllowDataURI = false);
|
||||
nsresult Init(nsIChannel* aChannel, DataURIHandling aAllowDataURI);
|
||||
|
||||
void SetInterceptController(nsINetworkInterceptController* aInterceptController);
|
||||
|
||||
private:
|
||||
~nsCORSListenerProxy();
|
||||
|
||||
nsresult UpdateChannel(nsIChannel* aChannel, bool aAllowDataURI = false);
|
||||
nsresult UpdateChannel(nsIChannel* aChannel, DataURIHandling aAllowDataURI);
|
||||
nsresult CheckRequestApproved(nsIRequest* aRequest);
|
||||
|
||||
nsCOMPtr<nsIStreamListener> mOuterListener;
|
||||
|
|
|
@ -14,4 +14,5 @@ interface PushSubscription
|
|||
readonly attribute USVString endpoint;
|
||||
readonly attribute DOMString subscriptionId;
|
||||
Promise<boolean> unsubscribe();
|
||||
jsonifier;
|
||||
};
|
|
@ -505,7 +505,7 @@ txCompileObserver::startLoad(nsIURI* aUri, txStylesheetCompiler* aCompiler,
|
|||
// Always install in case of redirects
|
||||
nsRefPtr<nsCORSListenerProxy> listener =
|
||||
new nsCORSListenerProxy(sink, aReferrerPrincipal, false);
|
||||
rv = listener->Init(channel);
|
||||
rv = listener->Init(channel, DataURIHandling::Disallow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return channel->AsyncOpen(listener, parser);
|
||||
|
|
|
@ -925,21 +925,22 @@ DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern)
|
|||
{
|
||||
MarkChanged();
|
||||
|
||||
// It's important to do this before FlushTransformToDC! As this will cause
|
||||
// the transform to become dirty.
|
||||
if (!mClipsArePushed) {
|
||||
mClipsArePushed = true;
|
||||
PushClipsToDC(mDC);
|
||||
}
|
||||
|
||||
FlushTransformToDC();
|
||||
|
||||
if (aOp == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) {
|
||||
// It's important to do this before FlushTransformToDC! As this will cause
|
||||
// the transform to become dirty.
|
||||
PushAllClips();
|
||||
|
||||
FlushTransformToDC();
|
||||
return;
|
||||
}
|
||||
|
||||
PopAllClips();
|
||||
|
||||
mDC->SetTarget(mTempBitmap);
|
||||
mDC->Clear(D2D1::ColorF(0, 0));
|
||||
|
||||
PushAllClips();
|
||||
FlushTransformToDC();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -951,6 +952,8 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
|
|||
return;
|
||||
}
|
||||
|
||||
PopAllClips();
|
||||
|
||||
RefPtr<ID2D1Image> image;
|
||||
mDC->GetTarget(byRef(image));
|
||||
|
||||
|
@ -961,7 +964,38 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
|
|||
|
||||
if (patternSupported) {
|
||||
if (D2DSupportsCompositeMode(aOp)) {
|
||||
D2D1_RECT_F rect;
|
||||
bool isAligned;
|
||||
RefPtr<ID2D1Bitmap> tmpBitmap;
|
||||
bool clipIsComplex = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
|
||||
|
||||
if (clipIsComplex) {
|
||||
if (!IsOperatorBoundByMask(aOp)) {
|
||||
HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), byRef(tmpBitmap));
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr);
|
||||
// For now, crash in this scenario; this should happen because tmpBitmap is
|
||||
// null and CopyFromBitmap call below dereferences it.
|
||||
// return;
|
||||
}
|
||||
mDC->Flush();
|
||||
|
||||
tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
|
||||
}
|
||||
} else {
|
||||
PushAllClips();
|
||||
}
|
||||
mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
|
||||
|
||||
if (tmpBitmap) {
|
||||
RefPtr<ID2D1BitmapBrush> brush;
|
||||
RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry();
|
||||
mDC->CreateBitmapBrush(tmpBitmap, byRef(brush));
|
||||
|
||||
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
|
||||
mDC->FillGeometry(inverseGeom, brush);
|
||||
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -985,7 +1019,6 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
|
|||
|
||||
// This flush is important since the copy method will not know about the context drawing to the surface.
|
||||
// We also need to pop all the clips to make sure any drawn content will have made it to the final bitmap.
|
||||
PopAllClips();
|
||||
mDC->Flush();
|
||||
|
||||
// We need to use a copy here because affects don't accept a surface on
|
||||
|
@ -996,8 +1029,7 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
|
|||
mBlendEffect->SetInput(1, mTempBitmap);
|
||||
mBlendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
|
||||
|
||||
PushClipsToDC(mDC);
|
||||
mClipsArePushed = true;
|
||||
PushAllClips();
|
||||
|
||||
mDC->DrawImage(mBlendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
|
||||
return;
|
||||
|
@ -1009,6 +1041,8 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
|
|||
return;
|
||||
}
|
||||
|
||||
PushAllClips();
|
||||
|
||||
RefPtr<ID2D1Effect> radialGradientEffect;
|
||||
|
||||
mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect));
|
||||
|
@ -1078,6 +1112,8 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
|
|||
return mCurrentClippedGeometry;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mPushedClips.size());
|
||||
|
||||
mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
|
||||
|
||||
// if pathGeom is null then pathRect represents the path.
|
||||
|
@ -1157,6 +1193,24 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
|
|||
return mCurrentClippedGeometry;
|
||||
}
|
||||
|
||||
TemporaryRef<ID2D1Geometry>
|
||||
DrawTargetD2D1::GetInverseClippedGeometry()
|
||||
{
|
||||
IntRect bounds;
|
||||
RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&bounds);
|
||||
RefPtr<ID2D1RectangleGeometry> rectGeom;
|
||||
RefPtr<ID2D1PathGeometry> inverseGeom;
|
||||
|
||||
factory()->CreateRectangleGeometry(D2D1::RectF(0, 0, mSize.width, mSize.height), byRef(rectGeom));
|
||||
factory()->CreatePathGeometry(byRef(inverseGeom));
|
||||
RefPtr<ID2D1GeometrySink> sink;
|
||||
inverseGeom->Open(byRef(sink));
|
||||
rectGeom->CombineWithGeometry(geom, D2D1_COMBINE_MODE_EXCLUDE, D2D1::IdentityMatrix(), sink);
|
||||
sink->Close();
|
||||
|
||||
return inverseGeom;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PopAllClips()
|
||||
{
|
||||
|
@ -1167,6 +1221,16 @@ DrawTargetD2D1::PopAllClips()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PushAllClips()
|
||||
{
|
||||
if (!mClipsArePushed) {
|
||||
PushClipsToDC(mDC);
|
||||
|
||||
mClipsArePushed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC)
|
||||
{
|
||||
|
|
|
@ -173,9 +173,12 @@ private:
|
|||
// bounds to correctly reflect the total clip. This is in device space.
|
||||
TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
|
||||
|
||||
TemporaryRef<ID2D1Geometry> GetInverseClippedGeometry();
|
||||
|
||||
bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
|
||||
|
||||
void PopAllClips();
|
||||
void PushAllClips();
|
||||
void PushClipsToDC(ID2D1DeviceContext *aDC);
|
||||
void PopClipsFromDC(ID2D1DeviceContext *aDC);
|
||||
|
||||
|
|
|
@ -450,9 +450,15 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
|||
// that originated the update, because the only identifying information
|
||||
// we are logging about APZCs is the scroll id, and otherwise we could
|
||||
// confuse APZCs from different layer trees with the same scroll id.
|
||||
if (aLayersId == aState.mOriginatingLayersId && apzc->GetParent()) {
|
||||
aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(), "parentScrollId",
|
||||
apzc->GetParent()->GetGuid().mScrollId);
|
||||
if (aLayersId == aState.mOriginatingLayersId) {
|
||||
if (apzc->IsRootForLayersId()) {
|
||||
aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
|
||||
"isRootForLayersId", true);
|
||||
} else {
|
||||
MOZ_ASSERT(apzc->GetParent());
|
||||
aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
|
||||
"parentScrollId", apzc->GetParent()->GetGuid().mScrollId);
|
||||
}
|
||||
}
|
||||
|
||||
if (newApzc) {
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
// Utilities for writing APZ tests using the framework added in bug 961289
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Functions that convert the APZ test data into a more usable form.
|
||||
// Every place we have a WebIDL sequence whose elements are dictionaries
|
||||
// with two elements, a key, and a value, we convert this into a JS
|
||||
// object with a property for each key/value pair. (This is the structure
|
||||
// we really want, but we can't express in directly in WebIDL.)
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
function convertEntries(entries) {
|
||||
var result = {};
|
||||
for (var i = 0; i < entries.length; ++i) {
|
||||
result[entries[i].key] = entries[i].value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertScrollFrameData(scrollFrames) {
|
||||
var result = {};
|
||||
for (var i = 0; i < scrollFrames.length; ++i) {
|
||||
result[scrollFrames[i].scrollId] = convertEntries(scrollFrames[i].entries);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertBuckets(buckets) {
|
||||
var result = {};
|
||||
for (var i = 0; i < buckets.length; ++i) {
|
||||
result[buckets[i].sequenceNumber] = convertScrollFrameData(buckets[i].scrollFrames);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertTestData(testData) {
|
||||
var result = {};
|
||||
result.paints = convertBuckets(testData.paints);
|
||||
result.repaintRequests = convertBuckets(testData.repaintRequests);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Utilities for reconstructing the structure of the APZC tree from
|
||||
// 'parentScrollId' entries in the APZ test data.
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
// Create a node with scroll id 'id' in the APZC tree.
|
||||
function makeNode(id) {
|
||||
return {scrollId: id, children: []};
|
||||
}
|
||||
|
||||
// Find a node with scroll id 'id' in the APZC tree rooted at 'root'.
|
||||
function findNode(root, id) {
|
||||
if (root.scrollId == id) {
|
||||
return root;
|
||||
}
|
||||
for (var i = 0; i < root.children.length; ++i) {
|
||||
var subtreeResult = findNode(root.children[i], id);
|
||||
if (subtreeResult != null) {
|
||||
return subtreeResult;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add a child -> parent link to the APZC tree rooted at 'root'.
|
||||
function addLink(root, child, parent) {
|
||||
var parentNode = findNode(root, parent);
|
||||
if (parentNode == null) {
|
||||
parentNode = makeNode(parent);
|
||||
root.children.push(parentNode);
|
||||
}
|
||||
parentNode.children.push(makeNode(child));
|
||||
}
|
||||
|
||||
// Add a root node to the APZC tree. It will become a direct
|
||||
// child of 'root'.
|
||||
function addRoot(root, id) {
|
||||
root.children.push(makeNode(id));
|
||||
}
|
||||
|
||||
// Given APZ test data for a single paint on the compositor side,
|
||||
// reconstruct the APZC tree structure from the 'parentScrollId'
|
||||
// entries that were logged. More specifically, the subset of the
|
||||
// APZC tree structure corresponding to the layer subtree for the
|
||||
// content process that triggered the paint, is reconstructed (as
|
||||
// the APZ test data only contains information abot this subtree).
|
||||
function buildApzcTree(paint) {
|
||||
// The APZC tree can potentially have multiple root nodes,
|
||||
// so we invent a node that is the parent of all roots.
|
||||
// This 'root' does not correspond to an APZC.
|
||||
var root = makeNode(-1);
|
||||
for (var scrollId in paint) {
|
||||
if ("isRootForLayersId" in paint[scrollId]) {
|
||||
addRoot(root, scrollId);
|
||||
} else if ("parentScrollId" in paint[scrollId]) {
|
||||
addLink(root, scrollId, paint[scrollId]["parentScrollId"]);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1151663, helper page</title>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Infrastructure to get the test assertions to run at the right time.
|
||||
// -------------------------------------------------------------------
|
||||
var SimpleTest = window.opener.SimpleTest;
|
||||
|
||||
window.onload = function() {
|
||||
window.addEventListener("MozAfterPaint", afterPaint, false);
|
||||
};
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
function afterPaint(e) {
|
||||
// If there is another paint pending, wait for it.
|
||||
if (utils.isMozAfterPaintPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Once there are no more paints pending, remove the
|
||||
// MozAfterPaint listener and run the test logic.
|
||||
window.removeEventListener("MozAfterPaint", afterPaint, false);
|
||||
testBug1151663();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// The actual logic for testing bug 1151663.
|
||||
//
|
||||
// In this test we have a simple page which is scrollable, with a
|
||||
// scrollable <div> which is also scrollable. We test that the
|
||||
// <div> does not get an initial APZC, since primary scrollable
|
||||
// frame is the page's root scroll frame.
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
function testBug1151663() {
|
||||
// Get the content- and compositor-side test data from nsIDOMWindowUtils.
|
||||
var contentTestData = utils.getContentAPZTestData();
|
||||
var compositorTestData = utils.getCompositorAPZTestData();
|
||||
|
||||
// Get the sequence number of the last paint on the compositor side.
|
||||
// We do this before converting the APZ test data because the conversion
|
||||
// loses the order of the paints.
|
||||
SimpleTest.ok(compositorTestData.paints.length > 0,
|
||||
"expected at least one paint in compositor test data");
|
||||
var lastCompositorPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
|
||||
var lastCompositorPaintSeqNo = lastCompositorPaint.sequenceNumber;
|
||||
|
||||
// Convert the test data into a representation that's easier to navigate.
|
||||
contentTestData = convertTestData(contentTestData);
|
||||
compositorTestData = convertTestData(compositorTestData);
|
||||
|
||||
// Reconstruct the APZC tree structure in the last paint.
|
||||
var apzcTree = buildApzcTree(compositorTestData.paints[lastCompositorPaintSeqNo]);
|
||||
|
||||
// The apzc tree for this page should consist of a single root APZC,
|
||||
// and no child APZCs.
|
||||
SimpleTest.is(apzcTree.children.length, 1, "expected a single root APZC");
|
||||
var rootApzc = apzcTree.children[0];
|
||||
SimpleTest.is(rootApzc.children.length, 0, "expected no child APZCs");
|
||||
|
||||
window.opener.finishTest();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="height: 500px; overflow: scroll">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
|
||||
<div style="height: 50px; width: 50px; overflow: scroll">
|
||||
<!-- Put enough content into the subframe to make it have a nonzero scroll range -->
|
||||
<div style="height: 100px; width: 50px"></div>
|
||||
</div>
|
||||
<!-- Put enough content into the page to make it have a nonzero scroll range -->
|
||||
<div style="height: 1000px"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -6,6 +6,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=982141
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 982141, helper page</title>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
@ -29,103 +30,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=982141
|
|||
testBug982141();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Functions that convert the APZ test data into a more usable form.
|
||||
// Every place we have a WebIDL sequence whose elements are dictionaries
|
||||
// with two elements, a key, and a value, we convert this into a JS
|
||||
// object with a property for each key/value pair. (This is the structure
|
||||
// we really want, but we can't express in directly in WebIDL.)
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
function convertEntries(entries) {
|
||||
var result = {};
|
||||
for (var i = 0; i < entries.length; ++i) {
|
||||
result[entries[i].key] = entries[i].value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertScrollFrameData(scrollFrames) {
|
||||
var result = {};
|
||||
for (var i = 0; i < scrollFrames.length; ++i) {
|
||||
result[scrollFrames[i].scrollId] = convertEntries(scrollFrames[i].entries);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertBuckets(buckets) {
|
||||
var result = {};
|
||||
for (var i = 0; i < buckets.length; ++i) {
|
||||
result[buckets[i].sequenceNumber] = convertScrollFrameData(buckets[i].scrollFrames);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertTestData(testData) {
|
||||
var result = {};
|
||||
result.paints = convertBuckets(testData.paints);
|
||||
result.repaintRequests = convertBuckets(testData.repaintRequests);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Utilities for reconstructing the structure of the APZC tree from
|
||||
// 'parentScrollId' entries in the APZ test data.
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
// Create a node with scroll id 'id' in the APZC tree.
|
||||
function makeNode(id) {
|
||||
return {scrollId: id, children: []};
|
||||
}
|
||||
|
||||
// Find a node with scroll id 'id' in the APZC tree rooted at 'root'.
|
||||
function findNode(root, id) {
|
||||
if (root.scrollId == id) {
|
||||
return root;
|
||||
}
|
||||
for (var i = 0; i < root.children.length; ++i) {
|
||||
var subtreeResult = findNode(root.children[i], id);
|
||||
if (subtreeResult != null) {
|
||||
return subtreeResult;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add a child -> parent link to the APZC tree rooted at 'root'.
|
||||
function addLink(root, child, parent) {
|
||||
var parentNode = findNode(root, parent);
|
||||
if (parentNode == null) {
|
||||
parentNode = makeNode(parent);
|
||||
root.children.push(parentNode);
|
||||
}
|
||||
parentNode.children.push(makeNode(child));
|
||||
}
|
||||
|
||||
// Add a root node to the APZC tree. It will become a direct
|
||||
// child of 'root'.
|
||||
function addRoot(root, id) {
|
||||
root.children.push(makeNode(id));
|
||||
}
|
||||
|
||||
// Given APZ test data for a single paint on the compositor side,
|
||||
// reconstruct the APZC tree structure from the 'parentScrollId'
|
||||
// entries that were logged.
|
||||
function buildApzcTree(paint) {
|
||||
// The APZC tree can potentially have multiple root nodes,
|
||||
// so we invent a node that is the parent of all roots.
|
||||
// This 'root' does not correspond to an APZC.
|
||||
var root = makeNode(-1);
|
||||
for (var scrollId in paint) {
|
||||
if ("parentScrollId" in paint[scrollId]) {
|
||||
addLink(root, scrollId, paint[scrollId]["parentScrollId"]);
|
||||
} else {
|
||||
addRoot(root, scrollId);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// The actual logic for testing bug 982141.
|
||||
//
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
apz_test_utils.js
|
||||
helper_bug982141.html
|
||||
helper_bug1151663.html
|
||||
[test_bug982141.html]
|
||||
skip-if = toolkit != 'gonk' # bug 991198
|
||||
support-files =
|
||||
helper_bug982141.html
|
||||
[test_bug1151663.html]
|
||||
skip-if = toolkit != 'gonk' # bug 991198
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1151663</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Run the actual test in its own window, because it requires that the
|
||||
// root APZC be scrollable. Mochitest pages themselves often run
|
||||
// inside an iframe which means we have no control over the root APZC.
|
||||
var w = null;
|
||||
window.onload = function() {
|
||||
w = window.open("helper_bug1151663.html", "_blank");
|
||||
};
|
||||
|
||||
function finishTest() {
|
||||
w.close();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
|
||||
</body>
|
||||
</html>
|
|
@ -406,16 +406,9 @@ SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
|
|||
|
||||
TransformData& data = aAnimation.data().get_TransformData();
|
||||
nsPoint origin = data.origin();
|
||||
// we expect all our transform data to arrive in css pixels, so here we must
|
||||
// adjust to dev pixels.
|
||||
double cssPerDev = double(nsDeviceContext::AppUnitsPerCSSPixel())
|
||||
/ double(data.appUnitsPerDevPixel());
|
||||
// we expect all our transform data to arrive in device pixels
|
||||
Point3D transformOrigin = data.transformOrigin();
|
||||
transformOrigin.x = transformOrigin.x * cssPerDev;
|
||||
transformOrigin.y = transformOrigin.y * cssPerDev;
|
||||
Point3D perspectiveOrigin = data.perspectiveOrigin();
|
||||
perspectiveOrigin.x = perspectiveOrigin.x * cssPerDev;
|
||||
perspectiveOrigin.y = perspectiveOrigin.y * cssPerDev;
|
||||
nsDisplayTransform::FrameTransformProperties props(interpolatedList,
|
||||
transformOrigin,
|
||||
perspectiveOrigin,
|
||||
|
|
|
@ -720,9 +720,7 @@ LayerTransactionParent::RecvGetAnimationTransform(PLayerParent* aParent,
|
|||
Point3D(NS_round(NSAppUnitsToFloatPixels(data.origin().x, scale)),
|
||||
NS_round(NSAppUnitsToFloatPixels(data.origin().y, scale)),
|
||||
0.0f);
|
||||
double cssPerDev =
|
||||
double(nsDeviceContext::AppUnitsPerCSSPixel()) / double(scale);
|
||||
transformOrigin = data.transformOrigin() * cssPerDev;
|
||||
transformOrigin = data.transformOrigin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,9 +154,9 @@ struct AnimationSegment {
|
|||
struct TransformData {
|
||||
// the origin of the frame being transformed in app units
|
||||
nsPoint origin;
|
||||
// the transform-origin property for the transform in css pixels
|
||||
// the transform-origin property for the transform in device pixels
|
||||
Point3D transformOrigin;
|
||||
// the perspective-origin property for the transform in css pixels
|
||||
// the perspective-origin property for the transform in device pixels
|
||||
Point3D perspectiveOrigin;
|
||||
nsRect bounds;
|
||||
nscoord perspective;
|
||||
|
|
|
@ -1674,7 +1674,7 @@ imgLoader::ValidateRequestWithNewChannel(imgRequest* request,
|
|||
bool withCredentials = aCORSMode == imgIRequest::CORS_USE_CREDENTIALS;
|
||||
nsRefPtr<nsCORSListenerProxy> corsproxy =
|
||||
new nsCORSListenerProxy(listener, aLoadingPrincipal, withCredentials);
|
||||
rv = corsproxy->Init(newChannel);
|
||||
rv = corsproxy->Init(newChannel, DataURIHandling::Allow);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2251,7 +2251,7 @@ imgLoader::LoadImage(nsIURI* aURI,
|
|||
|
||||
nsRefPtr<nsCORSListenerProxy> corsproxy =
|
||||
new nsCORSListenerProxy(pl, aLoadingPrincipal, withCredentials);
|
||||
rv = corsproxy->Init(newChannel);
|
||||
rv = corsproxy->Init(newChannel, DataURIHandling::Allow);
|
||||
if (NS_FAILED(rv)) {
|
||||
PR_LOG(GetImgLog(), PR_LOG_DEBUG,
|
||||
("[this=%p] imgLoader::LoadImage -- nsCORSListenerProxy "
|
||||
|
|
|
@ -95,3 +95,4 @@ skip-if = toolkit == "gonk" #Bug 997034 - canvas.toDataURL() often causes lost c
|
|||
[test_image_buffer_limit.html]
|
||||
#skip-if = toolkit != "gonk" #Image buffer limit is only set for Firefox OS currently.
|
||||
disabled = bug 1060869
|
||||
[test_image_crossorigin_data_url.html]
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Test for handling of 'crossorigin' attribute on CSS link with data: URL</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<div id="someuniqueidhere"></div>
|
||||
<img id="testimg" crossorigin src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR42mP4z8AAAAMBAQD3A0FDAAAAAElFTkSuQmCC">
|
||||
<script>
|
||||
var t = async_test("img@crossorigin with data: src");
|
||||
window.addEventListener("load", t.step_func_done(function() {
|
||||
var img = document.getElementById("testimg");
|
||||
assert_equals(img.naturalWidth, 1, "Should have 1px width");
|
||||
assert_equals(img.naturalHeight, 1, "Should have 1px height");
|
||||
var c = document.createElement("canvas");
|
||||
c.width = c.height = 1;
|
||||
var ctx = c.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0);
|
||||
var data = ctx.getImageData(0, 0, 1, 1);
|
||||
assert_equals(data.width, 1, "Should have 1px data width");
|
||||
assert_equals(data.height, 1, "Should have 1px data height");
|
||||
assert_equals(data.data[0], 255, "Should have lots of red");
|
||||
assert_equals(data.data[1], 0, "Should have no green");
|
||||
assert_equals(data.data[2], 0, "Should have no blue");
|
||||
assert_equals(data.data[3], 255, "Should have no translucency");
|
||||
}));
|
||||
</script>
|
|
@ -33,6 +33,12 @@ struct JS_PUBLIC_API(WorkBudget)
|
|||
*/
|
||||
struct JS_PUBLIC_API(SliceBudget)
|
||||
{
|
||||
// Memory of the originally requested budget. If isUnlimited, neither of
|
||||
// these are in use. If deadline==0, then workBudget is valid. Otherwise
|
||||
// timeBudget is valid.
|
||||
TimeBudget timeBudget;
|
||||
WorkBudget workBudget;
|
||||
|
||||
int64_t deadline; /* in microseconds */
|
||||
intptr_t counter;
|
||||
|
||||
|
@ -64,10 +70,12 @@ struct JS_PUBLIC_API(SliceBudget)
|
|||
return checkOverBudget();
|
||||
}
|
||||
|
||||
bool isUnlimited() {
|
||||
bool isUnlimited() const {
|
||||
return deadline == unlimitedDeadline;
|
||||
}
|
||||
|
||||
int describe(char* buffer, size_t maxlen) const;
|
||||
|
||||
private:
|
||||
bool checkOverBudget();
|
||||
|
||||
|
|
|
@ -97,6 +97,10 @@ AsmJSModule::AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t
|
|||
pod.strict_ = strict;
|
||||
pod.usesSignalHandlers_ = canUseSignalHandlers;
|
||||
|
||||
// AsmJSCheckedImmediateRange should be defined to be at most the minimum
|
||||
// heap length so that offsets can be folded into bounds checks.
|
||||
MOZ_ASSERT(pod.minHeapLength_ - AsmJSCheckedImmediateRange <= pod.minHeapLength_);
|
||||
|
||||
scriptSource_->incref();
|
||||
}
|
||||
|
||||
|
|
|
@ -570,14 +570,14 @@ ComputeAccessAddress(EMULATOR_CONTEXT* context, const Disassembler::ComplexAddre
|
|||
|
||||
uintptr_t result = address.disp();
|
||||
|
||||
if (address.base() != Registers::Invalid) {
|
||||
if (address.hasBase()) {
|
||||
uintptr_t base;
|
||||
StoreValueFromGPReg(&base, sizeof(uintptr_t),
|
||||
AddressOfGPRegisterSlot(context, address.base()));
|
||||
result += base;
|
||||
}
|
||||
|
||||
if (address.index() != Registers::Invalid) {
|
||||
if (address.hasIndex()) {
|
||||
uintptr_t index;
|
||||
StoreValueFromGPReg(&index, sizeof(uintptr_t),
|
||||
AddressOfGPRegisterSlot(context, address.index()));
|
||||
|
@ -608,15 +608,15 @@ EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddre
|
|||
// Check x64 asm.js heap access invariants.
|
||||
MOZ_RELEASE_ASSERT(address.disp() >= 0);
|
||||
MOZ_RELEASE_ASSERT(address.base() == HeapReg.code());
|
||||
MOZ_RELEASE_ASSERT(address.index() != HeapReg.code());
|
||||
MOZ_RELEASE_ASSERT(!address.hasIndex() || address.index() != HeapReg.code());
|
||||
MOZ_RELEASE_ASSERT(address.scale() == 0);
|
||||
if (address.base() != Registers::Invalid) {
|
||||
if (address.hasBase()) {
|
||||
uintptr_t base;
|
||||
StoreValueFromGPReg(&base, sizeof(uintptr_t),
|
||||
AddressOfGPRegisterSlot(context, address.base()));
|
||||
MOZ_RELEASE_ASSERT(reinterpret_cast<uint8_t*>(base) == module.maybeHeap());
|
||||
}
|
||||
if (address.index() != Registers::Invalid) {
|
||||
if (address.hasIndex()) {
|
||||
uintptr_t index;
|
||||
StoreValueFromGPReg(&index, sizeof(uintptr_t),
|
||||
AddressOfGPRegisterSlot(context, address.index()));
|
||||
|
|
|
@ -67,12 +67,12 @@ static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange,
|
|||
// the internal ArrayBuffer data array is inflated to 4GiB (only the
|
||||
// byteLength portion of which is accessible) so that out-of-bounds accesses
|
||||
// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
|
||||
// Then, an additional extent is added to permit folding of small immediate
|
||||
// Then, an additional extent is added to permit folding of immediate
|
||||
// values into addresses. And finally, unaligned accesses and mask optimizations
|
||||
// might also try to access a few bytes after this limit, so just inflate it by
|
||||
// AsmJSPageSize.
|
||||
static const size_t AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL +
|
||||
jit::AsmJSCheckedImmediateRange +
|
||||
jit::AsmJSImmediateRange +
|
||||
AsmJSPageSize;
|
||||
|
||||
#endif // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* 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 "TypedObjectConstants.h"
|
||||
|
||||
// ES6 draft 20150304 %TypedArray%.prototype.copyWithin
|
||||
function TypedArrayCopyWithin(target, start, end = undefined) {
|
||||
// This function is not generic.
|
||||
|
@ -649,6 +651,142 @@ function TypedArrayReverse() {
|
|||
return O;
|
||||
}
|
||||
|
||||
function ViewedArrayBufferIfReified(tarray) {
|
||||
assert(IsTypedArray(tarray), "non-typed array asked for its buffer");
|
||||
|
||||
var buf = UnsafeGetReservedSlot(tarray, JS_TYPEDARRAYLAYOUT_BUFFER_SLOT);
|
||||
assert(buf === null || (IsObject(buf) && IsArrayBuffer(buf)),
|
||||
"unexpected value in buffer slot");
|
||||
return buf;
|
||||
}
|
||||
|
||||
function IsDetachedBuffer(buffer) {
|
||||
// Typed arrays whose buffers are null use inline storage and can't have
|
||||
// been neutered.
|
||||
if (buffer === null)
|
||||
return false;
|
||||
|
||||
assert(IsArrayBuffer(buffer),
|
||||
"non-ArrayBuffer passed to IsDetachedBuffer");
|
||||
|
||||
var flags = UnsafeGetInt32FromReservedSlot(buffer, JS_ARRAYBUFFER_FLAGS_SLOT);
|
||||
return (flags & JS_ARRAYBUFFER_NEUTERED_FLAG) !== 0;
|
||||
}
|
||||
|
||||
// ES6 draft 20150220 22.2.3.22.1 %TypedArray%.prototype.set(array [, offset])
|
||||
function SetFromNonTypedArray(target, array, targetOffset, targetLength, targetBuffer) {
|
||||
assert(!IsPossiblyWrappedTypedArray(array),
|
||||
"typed arrays must be passed to SetFromTypedArray");
|
||||
|
||||
// Steps 1-11 provided by caller.
|
||||
|
||||
// Steps 16-17.
|
||||
var src = ToObject(array);
|
||||
|
||||
// Steps 18-19.
|
||||
var srcLength = ToLength(src.length);
|
||||
|
||||
// Step 20.
|
||||
var limitOffset = targetOffset + srcLength;
|
||||
if (limitOffset > targetLength)
|
||||
ThrowRangeError(JSMSG_BAD_INDEX);
|
||||
|
||||
// Step 22.
|
||||
var k = 0;
|
||||
|
||||
// Steps 12-15, 21, 23-24.
|
||||
while (targetOffset < limitOffset) {
|
||||
// Steps 24a-c.
|
||||
var kNumber = ToNumber(src[k]);
|
||||
|
||||
// Step 24d. This explicit check will be unnecessary when we implement
|
||||
// throw-on-getting/setting-element-in-detached-buffer semantics.
|
||||
if (targetBuffer === null) {
|
||||
// A typed array previously using inline storage may acquire a
|
||||
// buffer, so we must check with the source.
|
||||
targetBuffer = ViewedArrayBufferIfReified(target);
|
||||
}
|
||||
if (IsDetachedBuffer(targetBuffer))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
|
||||
// Step 24e.
|
||||
target[targetOffset] = kNumber;
|
||||
|
||||
// Steps 24f-g.
|
||||
k++;
|
||||
targetOffset++;
|
||||
}
|
||||
|
||||
// Step 25.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// ES6 draft 20150220 22.2.3.22.2 %TypedArray%.prototype.set(typedArray [, offset])
|
||||
function SetFromTypedArray(target, typedArray, targetOffset, targetLength) {
|
||||
assert(IsPossiblyWrappedTypedArray(typedArray),
|
||||
"only typed arrays may be passed to this method");
|
||||
|
||||
// Steps 1-11 provided by caller.
|
||||
|
||||
// Steps 12-24.
|
||||
var res = SetFromTypedArrayApproach(target, typedArray, targetOffset,
|
||||
targetLength | 0);
|
||||
assert(res === JS_SETTYPEDARRAY_SAME_TYPE ||
|
||||
res === JS_SETTYPEDARRAY_OVERLAPPING ||
|
||||
res === JS_SETTYPEDARRAY_DISJOINT,
|
||||
"intrinsic didn't return one of its enumerated return values");
|
||||
|
||||
// If the elements had the same type, then SetFromTypedArrayApproach also
|
||||
// performed step 29.
|
||||
if (res == JS_SETTYPEDARRAY_SAME_TYPE)
|
||||
return undefined; // Step 25: done.
|
||||
|
||||
// Otherwise, all checks and side effects except the actual element-writing
|
||||
// happened. Either we're assigning from one range to a non-overlapping
|
||||
// second range, or we're not.
|
||||
|
||||
if (res === JS_SETTYPEDARRAY_DISJOINT) {
|
||||
SetDisjointTypedElements(target, targetOffset | 0, typedArray);
|
||||
return undefined; // Step 25: done.
|
||||
}
|
||||
|
||||
// Now the hard case: overlapping memory ranges. Delegate to yet another
|
||||
// intrinsic.
|
||||
SetOverlappingTypedElements(target, targetOffset | 0, typedArray);
|
||||
|
||||
// Step 25.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// ES6 draft 20150304 %TypedArray%.prototype.set
|
||||
function TypedArraySet(overloaded, offset) {
|
||||
// Steps 2-5, either algorithm.
|
||||
var target = this;
|
||||
if (!IsObject(target) || !IsTypedArray(target)) {
|
||||
return callFunction(CallTypedArrayMethodIfWrapped,
|
||||
target, overloaded, offset, "TypedArraySet");
|
||||
}
|
||||
|
||||
// Steps 6-8, either algorithm.
|
||||
var targetOffset = ToInteger(offset);
|
||||
if (targetOffset < 0)
|
||||
ThrowRangeError(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
|
||||
|
||||
// Steps 9-10.
|
||||
var targetBuffer = ViewedArrayBufferIfReified(target);
|
||||
if (IsDetachedBuffer(targetBuffer))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
|
||||
// Step 11.
|
||||
var targetLength = TypedArrayLength(target);
|
||||
|
||||
// Steps 12 et seq.
|
||||
if (IsPossiblyWrappedTypedArray(overloaded))
|
||||
return SetFromTypedArray(target, overloaded, targetOffset, targetLength);
|
||||
|
||||
return SetFromNonTypedArray(target, overloaded, targetOffset, targetLength, targetBuffer);
|
||||
}
|
||||
|
||||
// ES6 draft rev32 (2015-02-02) 22.2.3.23 %TypedArray%.prototype.slice(start, end).
|
||||
function TypedArraySlice(start, end) {
|
||||
|
||||
|
|
|
@ -9,6 +9,27 @@
|
|||
#ifndef builtin_TypedObjectConstants_h
|
||||
#define builtin_TypedObjectConstants_h
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Values to be returned by SetFromTypedArrayApproach
|
||||
|
||||
#define JS_SETTYPEDARRAY_SAME_TYPE 0
|
||||
#define JS_SETTYPEDARRAY_OVERLAPPING 1
|
||||
#define JS_SETTYPEDARRAY_DISJOINT 2
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Slots for objects using the typed array layout
|
||||
|
||||
#define JS_TYPEDARRAYLAYOUT_BUFFER_SLOT 0
|
||||
#define JS_TYPEDARRAYLAYOUT_LENGTH_SLOT 1
|
||||
#define JS_TYPEDARRAYLAYOUT_BYTEOFFSET_SLOT 2
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Slots and flags for ArrayBuffer objects
|
||||
|
||||
#define JS_ARRAYBUFFER_FLAGS_SLOT 3
|
||||
|
||||
#define JS_ARRAYBUFFER_NEUTERED_FLAG 0x4
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Slots for typed prototypes
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "ffi.h"
|
||||
#include "jsalloc.h"
|
||||
#include "jsprf.h"
|
||||
#include "prlink.h"
|
||||
|
||||
#include "ctypes/typedefs.h"
|
||||
|
@ -53,6 +54,32 @@ AppendString(Vector<T, N, AP>& v, const char (&array)[ArrayLength])
|
|||
v[i + vlen] = array[i];
|
||||
}
|
||||
|
||||
template <class T, size_t N, class AP>
|
||||
void
|
||||
AppendChars(Vector<T, N, AP>& v, const char c, size_t count)
|
||||
{
|
||||
size_t vlen = v.length();
|
||||
if (!v.resize(vlen + count))
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
v[i + vlen] = c;
|
||||
}
|
||||
|
||||
template <class T, size_t N, class AP>
|
||||
void
|
||||
AppendUInt(Vector<T, N, AP>& v, unsigned n)
|
||||
{
|
||||
char array[16];
|
||||
size_t alen = JS_snprintf(array, 16, "%u", n);
|
||||
size_t vlen = v.length();
|
||||
if (!v.resize(vlen + alen))
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < alen; ++i)
|
||||
v[i + vlen] = array[i];
|
||||
}
|
||||
|
||||
template <class T, size_t N, size_t M, class AP>
|
||||
void
|
||||
AppendString(Vector<T, N, AP>& v, Vector<T, M, AP>& w)
|
||||
|
@ -375,6 +402,7 @@ enum CDataSlot {
|
|||
SLOT_REFERENT = 1, // JSObject this object must keep alive, if any
|
||||
SLOT_DATA = 2, // pointer to a buffer containing the binary data
|
||||
SLOT_OWNS = 3, // JSVAL_TRUE if this CData owns its own buffer
|
||||
SLOT_FUNNAME = 4, // JSString representing the function name
|
||||
CDATA_SLOTS
|
||||
};
|
||||
|
||||
|
|
|
@ -321,7 +321,7 @@ Library::Declare(JSContext* cx, unsigned argc, jsval* vp)
|
|||
|
||||
void* data;
|
||||
PRFuncPtr fnptr;
|
||||
JSString* nameStr = args[0].toString();
|
||||
RootedString nameStr(cx, args[0].toString());
|
||||
AutoCString symbol;
|
||||
if (isFunction) {
|
||||
// Build the symbol, with mangling if necessary.
|
||||
|
@ -352,6 +352,9 @@ Library::Declare(JSContext* cx, unsigned argc, jsval* vp)
|
|||
if (!result)
|
||||
return false;
|
||||
|
||||
if (isFunction)
|
||||
JS_SetReservedSlot(result, SLOT_FUNNAME, StringValue(nameStr));
|
||||
|
||||
args.rval().setObject(*result);
|
||||
|
||||
// Seal the CData object, to prevent modification of the function pointer.
|
||||
|
|
|
@ -10,5 +10,30 @@
|
|||
*/
|
||||
|
||||
MSG_DEF(CTYPESMSG_PLACEHOLDER_0, 0, JSEXN_NONE, NULL)
|
||||
MSG_DEF(CTYPESMSG_TYPE_ERROR, 2, JSEXN_TYPEERR, "expected type {0}, got {1}")
|
||||
|
||||
/* type conversion */
|
||||
MSG_DEF(CTYPESMSG_CONV_ERROR_ARG,3, JSEXN_TYPEERR, "can't pass {0} to argument {1} of {2}")
|
||||
MSG_DEF(CTYPESMSG_CONV_ERROR_ARRAY,3, JSEXN_TYPEERR, "can't convert {0} to element {1} of the type {2}")
|
||||
MSG_DEF(CTYPESMSG_CONV_ERROR_FIN,2, JSEXN_TYPEERR, "can't convert {0} to the type of argument 1 of {1}")
|
||||
MSG_DEF(CTYPESMSG_CONV_ERROR_RET,2, JSEXN_TYPEERR, "can't convert {0} to the return type of {1}")
|
||||
MSG_DEF(CTYPESMSG_CONV_ERROR_SET,2, JSEXN_TYPEERR, "can't convert {0} to the type {1}")
|
||||
MSG_DEF(CTYPESMSG_CONV_ERROR_STRUCT,5, JSEXN_TYPEERR, "can't convert {0} to the '{1}' field ({2}) of {3}{4}")
|
||||
MSG_DEF(CTYPESMSG_NON_PRIMITIVE, 1, JSEXN_TYPEERR, ".value only works on character and numeric types, not `{0}`")
|
||||
MSG_DEF(CTYPESMSG_TYPE_ERROR, 2, JSEXN_TYPEERR, "expected {0}, got {1}")
|
||||
|
||||
/* array */
|
||||
MSG_DEF(CTYPESMSG_ARRAY_MISMATCH,4, JSEXN_TYPEERR, "length of {0} does not match to the length of the type {1} (expected {2}, got {3})")
|
||||
MSG_DEF(CTYPESMSG_ARRAY_OVERFLOW,4, JSEXN_TYPEERR, "length of {0} does not fit to the length of the type {1} (expected {2} or lower, got {3})")
|
||||
|
||||
/* struct */
|
||||
MSG_DEF(CTYPESMSG_FIELD_MISMATCH,5, JSEXN_TYPEERR, "property count of {0} does not match to field count of the type {1} (expected {2}, got {3}){4}")
|
||||
MSG_DEF(CTYPESMSG_PROP_NONSTRING,3, JSEXN_TYPEERR, "property name {0} of {1} is not a string{2}")
|
||||
|
||||
/* data finalizer */
|
||||
MSG_DEF(CTYPESMSG_EMPTY_FIN, 1, JSEXN_TYPEERR, "attempting to convert an empty CDataFinalizer{0}")
|
||||
MSG_DEF(CTYPESMSG_FIN_SIZE_ERROR,2, JSEXN_TYPEERR, "expected an object with the same size as argument 1 of {0}, got {1}")
|
||||
|
||||
/* native function */
|
||||
MSG_DEF(CTYPESMSG_ARG_RANGE_MISMATCH,3, JSEXN_RANGEERR, "{0}argument of {1} must be {2}")
|
||||
MSG_DEF(CTYPESMSG_ARG_TYPE_MISMATCH,3, JSEXN_TYPEERR, "{0}argument of {1} must be {2}")
|
||||
MSG_DEF(CTYPESMSG_WRONG_ARG_LENGTH,3, JSEXN_TYPEERR, "{0} takes {1} argument{2}")
|
||||
|
|
|
@ -529,6 +529,9 @@ Statistics::formatData(StatisticsSerializer& ss, uint64_t timestamp)
|
|||
continue;
|
||||
}
|
||||
|
||||
char budgetDescription[200];
|
||||
slices[i].budget.describe(budgetDescription, sizeof(budgetDescription) - 1);
|
||||
|
||||
ss.beginObject(nullptr);
|
||||
ss.extra(" ");
|
||||
ss.appendNumber("Slice", "%d", "", i);
|
||||
|
@ -536,6 +539,7 @@ Statistics::formatData(StatisticsSerializer& ss, uint64_t timestamp)
|
|||
ss.extra(" (");
|
||||
ss.appendDecimal("When", "ms", t(slices[i].start - slices[0].start));
|
||||
ss.appendString("Reason", ExplainReason(slices[i].reason));
|
||||
ss.appendString("Budget", budgetDescription);
|
||||
if (ss.isJSON()) {
|
||||
ss.appendDecimal("Page Faults", "",
|
||||
double(slices[i].endFaults - slices[i].startFaults));
|
||||
|
@ -629,13 +633,16 @@ Statistics::formatDescription()
|
|||
UniqueChars
|
||||
Statistics::formatSliceDescription(unsigned i, const SliceData& slice)
|
||||
{
|
||||
char budgetDescription[200];
|
||||
slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1);
|
||||
|
||||
const char* format =
|
||||
"\
|
||||
---- Slice %u ----\n\
|
||||
Reason: %s\n\
|
||||
Reset: %s%s\n\
|
||||
Page Faults: %ld\n\
|
||||
Pause: %.3fms (@ %.3fms)\n\
|
||||
Pause: %.3fms of %s budget (@ %.3fms)\n\
|
||||
";
|
||||
char buffer[1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
@ -643,7 +650,7 @@ Statistics::formatSliceDescription(unsigned i, const SliceData& slice)
|
|||
ExplainReason(slice.reason),
|
||||
slice.resetReason ? "yes - " : "no", slice.resetReason ? slice.resetReason : "",
|
||||
uint64_t(slice.endFaults - slice.startFaults),
|
||||
t(slice.duration()), t(slice.start - slices[0].start));
|
||||
t(slice.duration()), budgetDescription, t(slice.start - slices[0].start));
|
||||
return make_string_copy(buffer);
|
||||
}
|
||||
|
||||
|
@ -985,7 +992,7 @@ Statistics::endGC()
|
|||
|
||||
void
|
||||
Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
|
||||
JS::gcreason::Reason reason)
|
||||
SliceBudget budget, JS::gcreason::Reason reason)
|
||||
{
|
||||
this->zoneStats = zoneStats;
|
||||
|
||||
|
@ -993,7 +1000,7 @@ Statistics::beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
|
|||
if (first)
|
||||
beginGC(gckind);
|
||||
|
||||
SliceData data(reason, PRMJ_Now(), GetPageFaultCount());
|
||||
SliceData data(budget, reason, PRMJ_Now(), GetPageFaultCount());
|
||||
if (!slices.append(data)) {
|
||||
// OOM testing fails if we CrashAtUnhandlableOOM here.
|
||||
aborted = true;
|
||||
|
|
|
@ -162,7 +162,7 @@ struct Statistics
|
|||
void endParallelPhase(Phase phase, const GCParallelTask* task);
|
||||
|
||||
void beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
|
||||
JS::gcreason::Reason reason);
|
||||
SliceBudget budget, JS::gcreason::Reason reason);
|
||||
void endSlice();
|
||||
|
||||
void startTimingMutator();
|
||||
|
@ -206,13 +206,16 @@ struct Statistics
|
|||
static const size_t MAX_NESTING = 20;
|
||||
|
||||
struct SliceData {
|
||||
SliceData(JS::gcreason::Reason reason, int64_t start, size_t startFaults)
|
||||
: reason(reason), resetReason(nullptr), start(start), startFaults(startFaults)
|
||||
SliceData(SliceBudget budget, JS::gcreason::Reason reason, int64_t start, size_t startFaults)
|
||||
: budget(budget), reason(reason),
|
||||
resetReason(nullptr),
|
||||
start(start), startFaults(startFaults)
|
||||
{
|
||||
for (size_t i = 0; i < MAX_MULTIPARENT_PHASES + 1; i++)
|
||||
mozilla::PodArrayZero(phaseTimes[i]);
|
||||
}
|
||||
|
||||
SliceBudget budget;
|
||||
JS::gcreason::Reason reason;
|
||||
const char* resetReason;
|
||||
int64_t start, end;
|
||||
|
@ -318,12 +321,12 @@ struct Statistics
|
|||
struct AutoGCSlice
|
||||
{
|
||||
AutoGCSlice(Statistics& stats, const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
|
||||
JS::gcreason::Reason reason
|
||||
SliceBudget budget, JS::gcreason::Reason reason
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: stats(stats)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
stats.beginSlice(zoneStats, gckind, reason);
|
||||
stats.beginSlice(zoneStats, gckind, budget, reason);
|
||||
}
|
||||
~AutoGCSlice() { stats.endSlice(); }
|
||||
|
||||
|
|
|
@ -65,3 +65,35 @@ if (typeof assertNoWarning === 'undefined') {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof assertErrorMessage === 'undefined') {
|
||||
var assertErrorMessage = function assertErrorMessage(f, ctor, test) {
|
||||
try {
|
||||
f();
|
||||
} catch (e) {
|
||||
if (!(e instanceof ctor))
|
||||
throw new Error("Assertion failed: expected exception " + ctor.name + ", got " + e);
|
||||
if (typeof test == "string") {
|
||||
if (test != e.message)
|
||||
throw new Error("Assertion failed: expeceted " + test + ", got " + e.message);
|
||||
} else {
|
||||
if (!test.test(e.message))
|
||||
throw new Error("Assertion failed: expeceted " + test.toString() + ", got " + e.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
throw new Error("Assertion failed: expected exception " + ctor.name + ", no exception thrown");
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof assertTypeErrorMessage === 'undefined') {
|
||||
var assertTypeErrorMessage = function assertTypeErrorMessage(f, test) {
|
||||
assertErrorMessage(f, TypeError, test);
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof assertRangeErrorMessage === 'undefined') {
|
||||
var assertRangeErrorMessage = function assertRangeErrorMessage(f, test) {
|
||||
assertErrorMessage(f, RangeError, test);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -505,3 +505,20 @@ assertEq(f(0x424242),0xAA);
|
|||
assertEq(f(0x1000000),0);
|
||||
assertEq(asmLink(m, this, null, new ArrayBuffer(0x2000000))(0),0);
|
||||
assertEq(asmLink(m, this, null, new ArrayBuffer(0x3000000))(0),0);
|
||||
|
||||
// Heap offsets
|
||||
var asmMod = function test (glob, env, b) {
|
||||
'use asm';
|
||||
var i8 = new glob.Int8Array(b);
|
||||
function f(i) {
|
||||
i = i | 0;
|
||||
i = i & 1;
|
||||
i = (i - 0x40000)|0;
|
||||
i8[0x3ffff] = 0;
|
||||
return i8[(i + 0x7fffe) >> 0] | 0;
|
||||
}
|
||||
return f;
|
||||
};
|
||||
var buffer = new ArrayBuffer(0x40000);
|
||||
var asm = asmMod(this, {}, buffer);
|
||||
assertEq(asm(-1),0);
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.default_abi.toSource(1); },
|
||||
"ABI.prototype.toSource takes no arguments");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,15 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.ArrayType(); },
|
||||
"ArrayType takes one or two arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array(10)(1, 2); },
|
||||
"size defined ArrayType constructor takes at most one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array()(1, 2); },
|
||||
"size undefined ArrayType constructor takes one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array(10)().addressOfElement(); },
|
||||
"ArrayType.prototype.addressOfElement takes one argument");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,15 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(0).address(1); },
|
||||
"CData.prototype.address takes no arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.char.array(10)().readString(1); },
|
||||
"CData.prototype.readString takes no arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.char.array(10)().readStringReplaceMalformed(1); },
|
||||
"CData.prototype.readStringReplaceMalformed takes no arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(0).toSource(1); },
|
||||
"CData.prototype.toSource takes no arguments");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,11 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.cast(); },
|
||||
"ctypes.cast takes two arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.getRuntime(); },
|
||||
"ctypes.getRuntime takes one argument");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,16 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer(1); },
|
||||
"CDataFinalizer constructor takes two arguments");
|
||||
|
||||
let fin = ctypes.CDataFinalizer(ctypes.int32_t(0), ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.int32_t]).ptr(x => x));
|
||||
assertTypeErrorMessage(() => { fin.forget(1); },
|
||||
"CDataFinalizer.prototype.forget takes no arguments");
|
||||
assertTypeErrorMessage(() => { fin.dispose(1); },
|
||||
"CDataFinalizer.prototype.dispose takes no arguments");
|
||||
fin.forget();
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,11 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.FunctionType(); },
|
||||
"FunctionType takes two or three arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, []).ptr({}, 1); },
|
||||
"FunctionType constructor takes one argument");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,36 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.Int64(1).toString(1, 2); },
|
||||
"Int64.prototype.toString takes at most one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64(1).toSource(1); },
|
||||
"Int64.prototype.toSource takes no arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64(); },
|
||||
"Int64 constructor takes one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.compare(); },
|
||||
"Int64.compare takes two arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.lo(); },
|
||||
"Int64.lo takes one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.hi(); },
|
||||
"Int64.hi takes one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.join(); },
|
||||
"Int64.join takes two arguments");
|
||||
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64(1).toString(1, 2); },
|
||||
"UInt64.prototype.toString takes at most one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64(1).toSource(1); },
|
||||
"UInt64.prototype.toSource takes no arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64(); },
|
||||
"UInt64 constructor takes one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.compare(); },
|
||||
"UInt64.compare takes two arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.lo(); },
|
||||
"UInt64.lo takes one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.hi(); },
|
||||
"UInt64.hi takes one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.join(); },
|
||||
"UInt64.join takes two arguments");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,11 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.PointerType(); },
|
||||
"PointerType takes one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.ptr(1, 2, 3, 4); },
|
||||
"PointerType constructor takes 0, 1, 2, or 3 arguments");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,11 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(1, 2, 3); },
|
||||
"CType constructor takes at most one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array(1, 2); },
|
||||
"CType.prototype.array takes at most one argument");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,17 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.StructType(); },
|
||||
"StructType takes one or two arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.StructType("a").define(); },
|
||||
"StructType.prototype.define takes one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.StructType("a", [])(1, 2, 3); },
|
||||
"StructType constructor takes at most one argument");
|
||||
assertTypeErrorMessage(() => { ctypes.StructType("a", [ {"x": ctypes.int32_t }, {"y": ctypes.int32_t }, {"z": ctypes.int32_t }])(1, 2); },
|
||||
"StructType constructor takes 0, 1, or 3 arguments");
|
||||
assertTypeErrorMessage(() => { ctypes.StructType("a", [ {"x": ctypes.int32_t } ])().addressOfField(); },
|
||||
"StructType.prototype.addressOfField takes one argument");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,17 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array({}); },
|
||||
"argument of CType.prototype.array must be a nonnegative integer");
|
||||
assertTypeErrorMessage(() => { ctypes.ArrayType(1); },
|
||||
"first argument of ArrayType must be a CType");
|
||||
assertTypeErrorMessage(() => { ctypes.ArrayType(ctypes.int32_t, {}); },
|
||||
"second argument of ArrayType must be a nonnegative integer");
|
||||
assertTypeErrorMessage(() => { ctypes.char.array()({}); },
|
||||
"argument of size undefined ArrayType constructor must be an array object or integer");
|
||||
assertTypeErrorMessage(() => { ctypes.char.array()(false); },
|
||||
"argument of size undefined ArrayType constructor must be an array object or integer");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,13 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.cast(1, 2); },
|
||||
"first argument of ctypes.cast must be a CData");
|
||||
assertTypeErrorMessage(() => { ctypes.cast(ctypes.int32_t(0), 2); },
|
||||
"second argument of ctypes.cast must be a CType");
|
||||
assertTypeErrorMessage(() => { ctypes.getRuntime(1); },
|
||||
"argument of ctypes.getRuntime must be a CType");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,9 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, 1); },
|
||||
"third argument of FunctionType must be an array");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,28 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertRangeErrorMessage(() => { ctypes.Int64(0).toString("a"); },
|
||||
"argument of Int64.prototype.toString must be an integer at least 2 and no greater than 36");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.compare(1, 2); },
|
||||
"first argument of Int64.compare must be a Int64");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.compare(ctypes.Int64(0), 2); },
|
||||
"second argument of Int64.compare must be a Int64");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.lo(1); },
|
||||
"argument of Int64.lo must be a Int64");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.hi(1); },
|
||||
"argument of Int64.hi must be a Int64");
|
||||
|
||||
assertRangeErrorMessage(() => { ctypes.UInt64(0).toString("a"); },
|
||||
"argument of UInt64.prototype.toString must be an integer at least 2 and no greater than 36");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.compare(1, 2); },
|
||||
"first argument of UInt64.compare must be a UInt64");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.compare(ctypes.UInt64(0), 2); },
|
||||
"second argument of UInt64.compare must be a UInt64");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.lo(1); },
|
||||
"argument of UInt64.lo must be a UInt64");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.hi(1); },
|
||||
"argument of UInt64.hi must be a UInt64");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,9 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.PointerType({}); },
|
||||
"argument of PointerType must be a CType");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,17 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.StructType(1); },
|
||||
"first argument of StructType must be a string");
|
||||
assertTypeErrorMessage(() => { ctypes.StructType("a", 1); },
|
||||
"second argument of StructType must be an array");
|
||||
assertTypeErrorMessage(() => { ctypes.StructType("a").define(1); },
|
||||
"argument of StructType.prototype.define must be an array");
|
||||
assertTypeErrorMessage(() => { ctypes.StructType("a").define({}); },
|
||||
"argument of StructType.prototype.define must be an array");
|
||||
assertTypeErrorMessage(() => { ctypes.StructType("a", [{x:ctypes.int32_t}])().addressOfField(1); },
|
||||
"argument of StructType.prototype.addressOfField must be a string");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,36 @@
|
|||
// Type conversion error should report its type.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
// constructor
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array()("foo"); },
|
||||
"can't convert the string \"foo\" to the type ctypes.int32_t.array()");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array(10)("foo"); },
|
||||
"can't convert the string \"foo\" to the type ctypes.int32_t.array(10)");
|
||||
assertTypeErrorMessage(() => { ctypes.char.array(2)("foo"); },
|
||||
"length of the string \"foo\" does not fit to the length of the type ctypes.char.array(2) (expected 2 or lower, got 3)");
|
||||
assertTypeErrorMessage(() => { ctypes.char16_t.array(2)("foo"); },
|
||||
"length of the string \"foo\" does not fit to the length of the type ctypes.char16_t.array(2) (expected 2 or lower, got 3)");
|
||||
assertTypeErrorMessage(() => { ctypes.int8_t.array(2)(new ArrayBuffer(8)); },
|
||||
"length of the array buffer ({}) does not match to the length of the type ctypes.int8_t.array(2) (expected 2, got 8)");
|
||||
assertTypeErrorMessage(() => { ctypes.int8_t.array(2)(new Int8Array(8)); },
|
||||
"length of the typed array ({0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0}) does not match to the length of the type ctypes.int8_t.array(2) (expected 2, got 8)");
|
||||
|
||||
// elem setter
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array(10)()[0] = "foo"; },
|
||||
"can't convert the string \"foo\" to element 0 of the type ctypes.int32_t.array(10)");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array(10)()[1] = "foo"; },
|
||||
"can't convert the string \"foo\" to element 1 of the type ctypes.int32_t.array(10)");
|
||||
|
||||
// value setter
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array(1)().value = ["foo"]; },
|
||||
"can't convert the string \"foo\" to element 0 of the type ctypes.int32_t.array(1)");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array(1)().value = [2, "foo"]; },
|
||||
"length of the array [2, \"foo\"] does not match to the length of the type ctypes.int32_t.array(1) (expected 1, got 2)");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.array(2)().value = [2, "foo"]; },
|
||||
"can't convert the string \"foo\" to element 1 of the type ctypes.int32_t.array(2)");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,14 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
let obj = {
|
||||
toSource() {
|
||||
throw 1;
|
||||
}
|
||||
};
|
||||
assertTypeErrorMessage(() => { ctypes.double().value = obj; },
|
||||
"can't convert <<error converting value to string>> to the type double");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,61 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
// non object
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer(0, "foo"); },
|
||||
"expected _a CData object_ of a function pointer type, got the string \"foo\"");
|
||||
// non CData object
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer(0, ["foo"]); },
|
||||
"expected a _CData_ object of a function pointer type, got the array [\"foo\"]");
|
||||
|
||||
// a CData which is not a pointer
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer(0, ctypes.int32_t(0)); },
|
||||
"expected a CData object of a function _pointer_ type, got ctypes.int32_t(0)");
|
||||
// a pointer CData which is not a function
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer(0, ctypes.int32_t.ptr(0)); },
|
||||
"expected a CData object of a _function_ pointer type, got ctypes.int32_t.ptr(ctypes.UInt64(\"0x0\"))");
|
||||
|
||||
// null function
|
||||
let func_type = ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t,
|
||||
[ctypes.int32_t, ctypes.int32_t]).ptr;
|
||||
let f0 = func_type(0);
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer(0, f0); },
|
||||
"expected a CData object of a _non-NULL_ function pointer type, got ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t, [ctypes.int32_t, ctypes.int32_t]).ptr(ctypes.UInt64(\"0x0\"))");
|
||||
|
||||
// a function with 2 arguments
|
||||
let f1 = func_type(x => x);
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer(0, f1); },
|
||||
"expected a function accepting exactly one argument, got ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t, [ctypes.int32_t, ctypes.int32_t])");
|
||||
|
||||
// non CData in argument 1
|
||||
let func_type2 = ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t,
|
||||
[ctypes.int32_t.ptr]).ptr;
|
||||
let f2 = func_type2(x => x);
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer(0, f2); },
|
||||
"can't convert the number 0 to the type of argument 1 of ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t, [ctypes.int32_t.ptr]).ptr");
|
||||
|
||||
// wrong struct in argument 1
|
||||
let test_struct = ctypes.StructType("test_struct", [{ "x": ctypes.int32_t }]);
|
||||
let func_type3 = ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t,
|
||||
[test_struct]).ptr;
|
||||
let f3 = func_type3(x => x);
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer({ "x": "foo" }, f3); },
|
||||
"can't convert the string \"foo\" to the 'x' field (int32_t) of test_struct at argument 1 of ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t, [test_struct]).ptr");
|
||||
|
||||
// different size in argument 1
|
||||
let func_type4 = ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t,
|
||||
[ctypes.int32_t]).ptr;
|
||||
let f4 = func_type4(x => x);
|
||||
assertTypeErrorMessage(() => { ctypes.CDataFinalizer(ctypes.int16_t(0), f4); },
|
||||
"expected an object with the same size as argument 1 of ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.int32_t]).ptr, got ctypes.int16_t(0)");
|
||||
|
||||
let fin = ctypes.CDataFinalizer(ctypes.int32_t(0), f4);
|
||||
fin.dispose();
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(0).value = fin; },
|
||||
"attempting to convert an empty CDataFinalizer");
|
||||
assertTypeErrorMessage(() => { f4(fin); },
|
||||
/attempting to convert an empty CDataFinalizer at argument 1 of ctypes\.FunctionType\(ctypes\.default_abi, ctypes\.int32_t, \[ctypes\.int32_t\]\)\.ptr\(ctypes\.UInt64\(\"[x0-9A-Fa-f]+\"\)\)/);
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,33 @@
|
|||
// Type conversion error should report its type.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
// Note: js shell cannot handle the exception in return value.
|
||||
|
||||
// primitive
|
||||
let func_type = ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t,
|
||||
[ctypes.int32_t]).ptr;
|
||||
let f1 = func_type(function() {});
|
||||
assertTypeErrorMessage(() => { f1("foo"); },
|
||||
/can't pass the string "foo" to argument 1 of ctypes\.FunctionType\(ctypes\.default_abi, ctypes\.voidptr_t, \[ctypes\.int32_t\]\)\.ptr\(ctypes\.UInt64\("[x0-9A-Fa-f]+"\)\)/);
|
||||
|
||||
// struct
|
||||
let test_struct = ctypes.StructType("test_struct", [{ "x": ctypes.int32_t }]);
|
||||
let func_type2 = ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t,
|
||||
[test_struct]).ptr;
|
||||
let f2 = func_type2(function() {});
|
||||
assertTypeErrorMessage(() => { f2({ "x": "foo" }); },
|
||||
/can't convert the string \"foo\" to the 'x' field \(int32_t\) of test_struct at argument 1 of ctypes\.FunctionType\(ctypes\.default_abi, ctypes.int32_t, \[test_struct\]\)\.ptr\(ctypes\.UInt64\(\"[x0-9A-Fa-f]+\"\)\)/);
|
||||
assertTypeErrorMessage(() => { f2({ "x": "foo", "y": "bar" }); },
|
||||
/property count of the object \(\{x:\"foo\", y:\"bar\"\}\) does not match to field count of the type test_struct \(expected 1, got 2\) at argument 1 of ctypes\.FunctionType\(ctypes\.default_abi, ctypes\.int32_t, \[test_struct\]\)\.ptr\(ctypes\.UInt64\(\"[x0-9A-Fa-f]+\"\)\)/);
|
||||
assertTypeErrorMessage(() => { f2({ 0: "foo" }); },
|
||||
/property name the number 0 of the object \(\{0:\"foo\"\}\) is not a string at argument 1 of ctypes\.FunctionType\(ctypes\.default_abi, ctypes\.int32_t, \[test_struct\]\)\.ptr\(ctypes\.UInt64\(\"[x0-9A-Fa-f]+\"\)\)/);
|
||||
|
||||
// error sentinel
|
||||
assertTypeErrorMessage(() => { func_type(function() {}, null, "foo"); },
|
||||
"can't convert the string \"foo\" to the return type of ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t, [ctypes.int32_t])");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,20 @@
|
|||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
assertTypeErrorMessage(() => { ctypes.Int64("0xfffffffffffffffffffffff"); },
|
||||
"can't pass the string \"0xfffffffffffffffffffffff\" to argument 1 of Int64");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.join("foo", 0); },
|
||||
"can't pass the string \"foo\" to argument 1 of Int64.join");
|
||||
assertTypeErrorMessage(() => { ctypes.Int64.join(0, "foo"); },
|
||||
"can't pass the string \"foo\" to argument 2 of Int64.join");
|
||||
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64("0xfffffffffffffffffffffff"); },
|
||||
"can't pass the string \"0xfffffffffffffffffffffff\" to argument 1 of UInt64");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.join("foo", 0); },
|
||||
"can't pass the string \"foo\" to argument 1 of UInt64.join");
|
||||
assertTypeErrorMessage(() => { ctypes.UInt64.join(0, "foo"); },
|
||||
"can't pass the string \"foo\" to argument 2 of UInt64.join");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,37 @@
|
|||
// Type conversion error for native function should report its name and type
|
||||
// in C style.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
let lib;
|
||||
try {
|
||||
lib = ctypes.open(ctypes.libraryName("c"));
|
||||
} catch (e) {
|
||||
}
|
||||
if (!lib)
|
||||
return;
|
||||
|
||||
let func = lib.declare("hypot",
|
||||
ctypes.default_abi,
|
||||
ctypes.double,
|
||||
ctypes.double, ctypes.double);
|
||||
assertTypeErrorMessage(() => { func(1, "xyzzy"); },
|
||||
"can't pass the string \"xyzzy\" to argument 2 of double hypot(double, double)");
|
||||
|
||||
// test C style source for various types
|
||||
let test_struct = ctypes.StructType("test_struct", [{ "x": ctypes.int32_t }]);
|
||||
let test_func = ctypes.FunctionType(ctypes.default_abi, ctypes.voidptr_t,
|
||||
[ctypes.int32_t]).ptr;
|
||||
func = lib.declare("hypot",
|
||||
ctypes.default_abi,
|
||||
ctypes.double,
|
||||
ctypes.double, ctypes.int32_t.ptr.ptr.ptr.array(),
|
||||
test_struct, test_struct.ptr.ptr,
|
||||
test_func, test_func.ptr.ptr.ptr, "...");
|
||||
assertTypeErrorMessage(() => { func("xyzzy", 1, 2, 3, 4, 5); },
|
||||
"can't pass the string \"xyzzy\" to argument 1 of double hypot(double, int32_t****, struct test_struct, struct test_struct**, void* (*)(int32_t), void* (****)(int32_t), ...)");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,29 @@
|
|||
// Type conversion error should report its type.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
let test_struct = ctypes.StructType("test_struct", [{ "x": ctypes.int32_t }]);
|
||||
let struct_val = test_struct();
|
||||
|
||||
// constructor
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t.ptr("foo"); },
|
||||
"can't convert the string \"foo\" to the type ctypes.int32_t.ptr");
|
||||
|
||||
// value setter
|
||||
assertTypeErrorMessage(() => { test_struct.ptr().value = "foo"; },
|
||||
"can't convert the string \"foo\" to the type test_struct.ptr");
|
||||
assertTypeErrorMessage(() => { test_struct.ptr().value = {}; },
|
||||
"can't convert the object ({}) to the type test_struct.ptr");
|
||||
assertTypeErrorMessage(() => { test_struct.ptr().value = [1, 2]; },
|
||||
"can't convert the array [1, 2] to the type test_struct.ptr");
|
||||
assertTypeErrorMessage(() => { test_struct.ptr().value = Int8Array([1, 2]); },
|
||||
"can't convert the typed array ({0:1, 1:2}) to the type test_struct.ptr");
|
||||
|
||||
// contents setter
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t().address().contents = {}; },
|
||||
"can't convert the object ({}) to the type int32_t");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,44 @@
|
|||
// Type conversion error should report its type.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
// constructor
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t("foo"); },
|
||||
"can't convert the string \"foo\" to the type int32_t");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(null); },
|
||||
"can't convert null to the type int32_t");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(undefined); },
|
||||
"can't convert undefined to the type int32_t");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t({}); },
|
||||
"can't convert the object ({}) to the type int32_t");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t([]); },
|
||||
"can't convert the array [] to the type int32_t");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(new Int8Array([])); },
|
||||
"can't convert the typed array ({}) to the type int32_t");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(ctypes.int32_t); },
|
||||
"can't convert ctypes.int32_t to the type int32_t");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t("0xfffffffffffffffffffffff"); },
|
||||
"can't convert the string \"0xfffffffffffffffffffffff\" to the type int32_t");
|
||||
if (typeof Symbol === "function") {
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(Symbol.iterator); },
|
||||
"can't convert Symbol.iterator to the type int32_t");
|
||||
assertTypeErrorMessage(() => { ctypes.int32_t(Symbol("foo")); },
|
||||
"can't convert Symbol(\"foo\") to the type int32_t");
|
||||
}
|
||||
|
||||
// value setter
|
||||
let test_struct = ctypes.StructType("test_struct", [{ "x": ctypes.int32_t }]);
|
||||
let struct_val = test_struct();
|
||||
assertTypeErrorMessage(() => { ctypes.bool().value = struct_val; },
|
||||
"can't convert test_struct(0) to the type boolean");
|
||||
assertTypeErrorMessage(() => { ctypes.char16_t().value = struct_val; },
|
||||
"can't convert test_struct(0) to the type char16_t");
|
||||
assertTypeErrorMessage(() => { ctypes.int8_t().value = struct_val; },
|
||||
"can't convert test_struct(0) to the type int8_t");
|
||||
assertTypeErrorMessage(() => { ctypes.double().value = struct_val; },
|
||||
"can't convert test_struct(0) to the type double");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,36 @@
|
|||
// Type conversion error should report its type.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
let test_struct = ctypes.StructType("test_struct", [{ "x": ctypes.int32_t },
|
||||
{ "bar": ctypes.int32_t }]);
|
||||
|
||||
// constructor
|
||||
assertTypeErrorMessage(() => { new test_struct("foo"); },
|
||||
"can't convert the string \"foo\" to the type test_struct");
|
||||
assertTypeErrorMessage(() => { new test_struct("foo", "x"); },
|
||||
"can't convert the string \"foo\" to the 'x' field (int32_t) of test_struct");
|
||||
assertTypeErrorMessage(() => { new test_struct({ "x": "foo", "bar": 1 }); },
|
||||
"can't convert the string \"foo\" to the 'x' field (int32_t) of test_struct");
|
||||
assertTypeErrorMessage(() => { new test_struct({ 0: 1, "bar": 1 }); },
|
||||
"property name the number 0 of the object ({0:1, bar:1}) is not a string");
|
||||
|
||||
// field setter
|
||||
let struct_val = test_struct();
|
||||
assertTypeErrorMessage(() => { struct_val.x = "foo"; },
|
||||
"can't convert the string \"foo\" to the 'x' field (int32_t) of test_struct");
|
||||
assertTypeErrorMessage(() => { struct_val.bar = "foo"; },
|
||||
"can't convert the string \"foo\" to the 'bar' field (int32_t) of test_struct");
|
||||
|
||||
// value setter
|
||||
assertTypeErrorMessage(() => { struct_val.value = { "x": "foo" }; },
|
||||
"property count of the object ({x:\"foo\"}) does not match to field count of the type test_struct (expected 2, got 1)");
|
||||
assertTypeErrorMessage(() => { struct_val.value = { "x": "foo", "bar": 1 }; },
|
||||
"can't convert the string \"foo\" to the 'x' field (int32_t) of test_struct");
|
||||
assertTypeErrorMessage(() => { struct_val.value = "foo"; },
|
||||
"can't convert the string \"foo\" to the type test_struct");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -0,0 +1,20 @@
|
|||
// Accessing `value` property of non primitive type should report its type.
|
||||
|
||||
load(libdir + 'asserts.js');
|
||||
|
||||
function test() {
|
||||
let test_struct = ctypes.StructType("test_struct", [{ "x": ctypes.voidptr_t }]);
|
||||
assertTypeErrorMessage(() => test_struct().value,
|
||||
".value only works on character and numeric types, not `test_struct`");
|
||||
|
||||
let test_array = ctypes.ArrayType(test_struct);
|
||||
assertTypeErrorMessage(() => test_array(10).value,
|
||||
".value only works on character and numeric types, not `test_struct.array(10)`");
|
||||
|
||||
let test_pointer = ctypes.PointerType(test_struct);
|
||||
assertTypeErrorMessage(() => test_pointer(10).value,
|
||||
".value only works on character and numeric types, not `test_struct.ptr`");
|
||||
}
|
||||
|
||||
if (typeof ctypes === "object")
|
||||
test();
|
|
@ -4974,6 +4974,22 @@ CodeGenerator::visitTypedArrayElements(LTypedArrayElements* lir)
|
|||
masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), out);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitSetDisjointTypedElements(LSetDisjointTypedElements* lir)
|
||||
{
|
||||
Register target = ToRegister(lir->target());
|
||||
Register targetOffset = ToRegister(lir->targetOffset());
|
||||
Register source = ToRegister(lir->source());
|
||||
|
||||
Register temp = ToRegister(lir->temp());
|
||||
|
||||
masm.setupUnalignedABICall(3, temp);
|
||||
masm.passABIArg(target);
|
||||
masm.passABIArg(targetOffset);
|
||||
masm.passABIArg(source);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::SetDisjointTypedElements));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitTypedObjectDescr(LTypedObjectDescr* lir)
|
||||
{
|
||||
|
|
|
@ -180,6 +180,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
void visitSetArrayLength(LSetArrayLength* lir);
|
||||
void visitTypedArrayLength(LTypedArrayLength* lir);
|
||||
void visitTypedArrayElements(LTypedArrayElements* lir);
|
||||
void visitSetDisjointTypedElements(LSetDisjointTypedElements* lir);
|
||||
void visitTypedObjectElements(LTypedObjectElements* lir);
|
||||
void visitSetTypedObjectOffset(LSetTypedObjectOffset* lir);
|
||||
void visitTypedObjectDescr(LTypedObjectDescr* ins);
|
||||
|
|
|
@ -97,13 +97,21 @@ class ComplexAddress {
|
|||
return disp_;
|
||||
}
|
||||
|
||||
bool hasBase() const {
|
||||
return base_ != Registers::Invalid;
|
||||
}
|
||||
|
||||
Register::Encoding base() const {
|
||||
MOZ_ASSERT(base_ != Registers::Invalid);
|
||||
MOZ_ASSERT(hasBase());
|
||||
return base_;
|
||||
}
|
||||
|
||||
bool hasIndex() const {
|
||||
return index_ != Registers::Invalid;
|
||||
}
|
||||
|
||||
Register::Encoding index() const {
|
||||
MOZ_ASSERT(index_ != Registers::Invalid);
|
||||
MOZ_ASSERT(hasIndex());
|
||||
return index_;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,8 +101,38 @@ AnalyzeLsh(TempAllocator& alloc, MLsh* lsh)
|
|||
}
|
||||
|
||||
template<typename MAsmJSHeapAccessType>
|
||||
static void
|
||||
AnalyzeAsmHeapAccess(MAsmJSHeapAccessType* ins, MIRGraph& graph)
|
||||
bool
|
||||
EffectiveAddressAnalysis::tryAddDisplacement(MAsmJSHeapAccessType *ins, int32_t o)
|
||||
{
|
||||
// Compute the new offset. Check for overflow and negative. In theory it
|
||||
// ought to be possible to support negative offsets, but it'd require
|
||||
// more elaborate bounds checking mechanisms than we currently have.
|
||||
MOZ_ASSERT(ins->offset() >= 0);
|
||||
int32_t newOffset = uint32_t(ins->offset()) + o;
|
||||
if (newOffset < 0)
|
||||
return false;
|
||||
|
||||
// Compute the new offset to the end of the access. Check for overflow
|
||||
// and negative here also.
|
||||
int32_t newEnd = uint32_t(newOffset) + ins->byteSize();
|
||||
if (newEnd < 0)
|
||||
return false;
|
||||
MOZ_ASSERT(uint32_t(newEnd) >= uint32_t(newOffset));
|
||||
|
||||
// Determine the range of valid offsets which can be folded into this
|
||||
// instruction and check whether our computed offset is within that range.
|
||||
size_t range = mir_->foldableOffsetRange(ins);
|
||||
if (size_t(newEnd) > range)
|
||||
return false;
|
||||
|
||||
// Everything checks out. This is the new offset.
|
||||
ins->setOffset(newOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename MAsmJSHeapAccessType>
|
||||
void
|
||||
EffectiveAddressAnalysis::analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins)
|
||||
{
|
||||
MDefinition* ptr = ins->ptr();
|
||||
|
||||
|
@ -113,8 +143,8 @@ AnalyzeAsmHeapAccess(MAsmJSHeapAccessType* ins, MIRGraph& graph)
|
|||
// a situation where the sum of a constant pointer value and a non-zero
|
||||
// offset doesn't actually fit into the address mode immediate.
|
||||
int32_t imm = ptr->constantValue().toInt32();
|
||||
if (imm != 0 && ins->tryAddDisplacement(imm)) {
|
||||
MInstruction* zero = MConstant::New(graph.alloc(), Int32Value(0));
|
||||
if (imm != 0 && tryAddDisplacement(ins, imm)) {
|
||||
MInstruction* zero = MConstant::New(graph_.alloc(), Int32Value(0));
|
||||
ins->block()->insertBefore(ins, zero);
|
||||
ins->replacePtr(zero);
|
||||
}
|
||||
|
@ -128,7 +158,7 @@ AnalyzeAsmHeapAccess(MAsmJSHeapAccessType* ins, MIRGraph& graph)
|
|||
mozilla::Swap(op0, op1);
|
||||
if (op1->isConstantValue()) {
|
||||
int32_t imm = op1->constantValue().toInt32();
|
||||
if (ins->tryAddDisplacement(imm))
|
||||
if (tryAddDisplacement(ins, imm))
|
||||
ins->replacePtr(op0);
|
||||
}
|
||||
}
|
||||
|
@ -159,9 +189,9 @@ EffectiveAddressAnalysis::analyze()
|
|||
if (i->isLsh())
|
||||
AnalyzeLsh(graph_.alloc(), i->toLsh());
|
||||
else if (i->isAsmJSLoadHeap())
|
||||
AnalyzeAsmHeapAccess(i->toAsmJSLoadHeap(), graph_);
|
||||
analyzeAsmHeapAccess(i->toAsmJSLoadHeap());
|
||||
else if (i->isAsmJSStoreHeap())
|
||||
AnalyzeAsmHeapAccess(i->toAsmJSStoreHeap(), graph_);
|
||||
analyzeAsmHeapAccess(i->toAsmJSStoreHeap());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -14,11 +14,18 @@ class MIRGraph;
|
|||
|
||||
class EffectiveAddressAnalysis
|
||||
{
|
||||
MIRGenerator* mir_;
|
||||
MIRGraph& graph_;
|
||||
|
||||
template<typename MAsmJSHeapAccessType>
|
||||
bool tryAddDisplacement(MAsmJSHeapAccessType *ins, int32_t o);
|
||||
|
||||
template<typename MAsmJSHeapAccessType>
|
||||
void analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins);
|
||||
|
||||
public:
|
||||
explicit EffectiveAddressAnalysis(MIRGraph& graph)
|
||||
: graph_(graph)
|
||||
EffectiveAddressAnalysis(MIRGenerator *mir, MIRGraph& graph)
|
||||
: mir_(mir), graph_(graph)
|
||||
{}
|
||||
|
||||
bool analyze();
|
||||
|
|
|
@ -1411,7 +1411,7 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
|
||||
if (mir->optimizationInfo().eaaEnabled()) {
|
||||
AutoTraceLog log(logger, TraceLogger_EffectiveAddressAnalysis);
|
||||
EffectiveAddressAnalysis eaa(graph);
|
||||
EffectiveAddressAnalysis eaa(mir, graph);
|
||||
if (!eaa.analyze())
|
||||
return false;
|
||||
IonSpewPass("Effective Address Analysis");
|
||||
|
|
|
@ -803,8 +803,12 @@ class IonBuilder
|
|||
MIRType knownValueType);
|
||||
|
||||
// TypedArray intrinsics.
|
||||
enum WrappingBehavior { AllowWrappedTypedArrays, RejectWrappedTypedArrays };
|
||||
InliningStatus inlineIsTypedArrayHelper(CallInfo& callInfo, WrappingBehavior wrappingBehavior);
|
||||
InliningStatus inlineIsTypedArray(CallInfo& callInfo);
|
||||
InliningStatus inlineIsPossiblyWrappedTypedArray(CallInfo& callInfo);
|
||||
InliningStatus inlineTypedArrayLength(CallInfo& callInfo);
|
||||
InliningStatus inlineSetDisjointTypedElements(CallInfo& callInfo);
|
||||
|
||||
// TypedObject intrinsics and natives.
|
||||
InliningStatus inlineObjectIsTypeDescr(CallInfo& callInfo);
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче