Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2015-04-23 15:58:20 +02:00
Родитель 3e864279a2 20f04c6b64
Коммит f87169c55d
155 изменённых файлов: 3896 добавлений и 646 удалений

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

@ -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;

262
dom/media/StateWatching.h Normal file
Просмотреть файл

@ -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);

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